Marzogh / arduino_serial.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
import serial |
import csv |
import re |
import matplotlib . pyplot as plt |
import pandas as pd |
portPath = «/dev/ttyACM0» # Must match value shown on Arduino IDE |
baud = 115200 # Must match Arduino baud rate |
timeout = 5 # Seconds |
filename = «data.csv» |
max_num_readings = 16000 |
num_signals = 1 |
def create_serial_obj ( portPath , baud_rate , tout ): |
«»» |
Given the port path, baud rate, and timeout value, creates |
and returns a pyserial object. |
«»» |
return serial . Serial ( portPath , baud_rate , timeout = tout ) |
def read_serial_data ( serial ): |
«»» |
Given a pyserial object (serial). Outputs a list of lines read in |
from the serial port |
«»» |
serial . flushInput () |
serial_data = [] |
readings_left = True |
timeout_reached = False |
while readings_left and not timeout_reached : |
serial_line = serial . readline () |
if serial_line == » : |
timeout_reached = True |
else : |
serial_data . append ( serial_line ) |
if len ( serial_data ) == max_num_readings : |
readings_left = False |
return serial_data |
def is_number ( string ): |
«»» |
Given a string returns True if the string represents a number. |
Returns False otherwise. |
«»» |
try : |
float ( string ) |
return True |
except ValueError : |
return False |
def clean_serial_data ( data ): |
«»» |
Given a list of serial lines (data). Removes all characters. |
Returns the cleaned list of lists of digits. |
Given something like: [‘0.5000,33 \r \n ‘, ‘1.0000,283 \r \n ‘] |
Returns: [[0.5,33.0], [1.0,283.0]] |
«»» |
clean_data = [] |
for line in data : |
line_data = re . findall ( «\d*\.\d*|\d*» , line ) # Find all digits |
line_data = [ float ( element ) for element in line_data if is_number ( element )] # Convert strings to float |
if len ( line_data ) >= 2 : |
clean_data . append ( line_data ) |
return clean_data |
def save_to_csv ( data , filename ): |
«»» |
Saves a list of lists (data) to filename |
«»» |
with open ( filename , ‘wb’ ) as csvfile : |
csvwrite = csv . writer ( csvfile ) |
csvwrite . writerows ( data ) |
def gen_col_list ( num_signals ): |
«»» |
Given the number of signals returns |
a list of columns for the data. |
E.g. 3 signals returns the list: [‘Time’,’Signal1′,’Signal2′,’Signal3′] |
«»» |
col_list = [ ‘Time’ ] |
for i in range ( 1 , num_signals + 1 ): |
col = ‘Signal’ + str ( i ) |
col_list . append ( col ) |
return col_list |
def map_value ( x , in_min , in_max , out_min , out_max ): |
return ((( x — in_min ) * ( out_max — out_min )) / ( in_max — in_min )) + out_min |
def simple_plot ( csv_file , columns , headers ): |
plt . clf () |
plt . close () |
plt . plotfile ( csv_file , columns , names = headers , newfig = True ) |
plt . show () |
def plot_csv ( csv_file , cols ): |
# Create Pandas DataFrame from csv data |
data_frame = pd . read_csv ( csv_file ) |
# Set the names of the columns |
data_frame . columns = cols |
# Set the first column (Time) as the index |
data_frame = data_frame . set_index ( cols [ 0 ]) |
# Map the voltage values from 0-1023 to 0-5 |
data_frame = data_frame . apply ( lambda x : map_value ( x , 0. , 1023 , 0 , 5 )) |
# Bring back the Time column |
data_frame = data_frame . reset_index () |
plt . clf () |
plt . close () |
# Plot the data |
data_frame . plot ( x = cols [ 0 ], y = cols [ 1 :]) |
plt . show () |
print «Creating serial object. « |
serial_obj = create_serial_obj ( portPath , baud , timeout ) |
print «Reading serial data. « |
serial_data = read_serial_data ( serial_obj ) |
print len ( serial_data ) |
print «Cleaning data. « |
clean_data = clean_serial_data ( serial_data ) |
print «Saving to csv. « |
save_to_csv ( clean_data , filename ) |
print «Plotting data. « |
#simple_plot(filename, (0,1,2), [‘time (s)’, ‘voltage1’, ‘voltage2’]) |
#simple_plot(filename, (0,1), [‘time (s)’, ‘voltage1’]) |
plot_csv ( filename , gen_col_list ( num_signals )) |
Short introduction¶
Open port at “38400,8,E,1”, non blocking HW handshaking:
>>> ser = serial.Serial('COM3', 38400, timeout=0, . parity=serial.PARITY_EVEN, rtscts=1) >>> s = ser.read(100) # read up to one hundred bytes . # or as much is in the buffer
Configuring ports later¶
Get a Serial instance and configure/open it later:
>>> ser = serial.Serial() >>> ser.baudrate = 19200 >>> ser.port = 'COM1' >>> ser Serial(port='COM1', baudrate=19200, bytesize=8, parity='N', stopbits=1, timeout=None, xonxoff=0, rtscts=0) >>> ser.open() >>> ser.is_open True >>> ser.close() >>> ser.is_open False
with serial.Serial() as ser: ser.baudrate = 19200 ser.port = 'COM1' ser.open() ser.write(b'hello')
Readline¶
readline() reads up to one line, including the \n at the end. Be careful when using readline() . Do specify a timeout when opening the serial port otherwise it could block forever if no newline character is received. If the \n is missing in the return value, it returned on timeout.
readlines() tries to read “all” lines which is not well defined for a serial port that is still open. Therefore readlines() depends on having a timeout on the port and interprets that as EOF (end of file). It raises an exception if the port is not opened correctly. The returned list of lines do not include the \n .
Both functions call read() to get their data and the serial port timeout is acting on this function. Therefore the effective timeout, especially for readlines() , can be much larger.
Do also have a look at the example files in the examples directory in the source distribution or online.
The eol parameter for readline() is no longer supported when pySerial is run with newer Python versions (V2.6+) where the module io is available.
EOL¶
To specify the EOL character for readline() or to use universal newline mode, it is advised to use io.TextIOWrapper:
import serial import io ser = serial.serial_for_url('loop://', timeout=1) sio = io.TextIOWrapper(io.BufferedRWPair(ser, ser)) sio.write(unicode("hello\n")) sio.flush() # it is buffering. required to get the data out *now* hello = sio.readline() print(hello == unicode("hello\n"))
Testing ports¶
Listing ports¶
python -m serial.tools.list_ports will print a list of available ports. It is also possible to add a regexp as first argument and the list will only include entries that matched.
The enumeration may not work on all operating systems. It may be incomplete, list unavailable ports or may lack detailed descriptions of the ports.
Accessing ports¶
pySerial includes a small console based terminal program called serial.tools.miniterm . It can be started with python -m serial.tools.miniterm (use option -h to get a listing of all options).
© Copyright 2001-2020, Chris Liechti Revision 31fa4807 .
Versions latest stable Downloads pdf html epub On Read the Docs Project Home Builds Free document hosting provided by Read the Docs.
Saving Arduino sensor data to a text file
you can write the sensor data to the serial port using serial-lib and write a small processing program that reads the data from the serial port and writes it to a file.
in the arduino code initalize the serial lib in the setup method
and write your sensor values to the serial interface using
on the processing side use a PrintWriter to write the data read from the serial port to a file
import processing.serial.*; Serial mySerial; PrintWriter output; void setup() < mySerial = new Serial( this, Serial.list()[0], 9600 ); output = createWriter( "data.txt" ); >void draw() < if (mySerial.available() >0 ) < String value = mySerial.readString(); if ( value != null ) < output.println( value ); >> > void keyPressed() < output.flush(); // Writes the remaining data to the file output.close(); // Finishes the file exit(); // Stops the program >
\$\begingroup\$ Where should the «processing side» code go — in the same arduino code or a separate c script? \$\endgroup\$
\$\begingroup\$ In a separate processing script — processing isn’t compiled to C but to java \$\endgroup\$
\$\begingroup\$ @UladKasach «Processing» is a multi-platform programming environment. In fact, it’s the programming environment with the world’s stupidest name. \$\endgroup\$
Yet another option is to use an SD Card reader / writer, and write your file to a SD card. When you’re done collecting data, swap out SD cards with your workstation computer. This approach will allow you to run your project disconnected from a computer, and will provide for non-volatile storage of large amounts of data.
The program gobetwino will log sensor values from an Arduino to a text file or spreadsheet with minimal effort. It can also automate things on the computer, add timestamps (so you don’t need to program them into the arduino), etc.
The simplest method is to use the Serial library and output to that. You can then capture the output to a text file using a terminal program. Hyperterminal is available on Windows, Teraterm on Linux and Z Term on OS X.
If you want to write sensor data directly to a file on your computer without having to copy and paste the output form the serial monitor window, then try reading the data stream directly from the serial port (which is what the serial monitor does anyway, I suspect). On mac/linux do something like:
God knows what’s the equivalent on windows machines.
\$\begingroup\$ You’d probably also want to associate timestamps with each sensor reading, in which case you’ll probably need to replace that cat command with some sort of script. \$\endgroup\$
You can create a python script to read the serial port and write the results into a text file:
############## ## Script listens to serial port and writes contents into a file ############## ## requires pySerial to be installed import serial serial_port = '/dev/ttyACM0'; baud_rate = 9600; #In arduino, Serial.begin(baud_rate) write_to_file_path = "output.txt"; output_file = open(write_to_file_path, "w+"); ser = serial.Serial(serial_port, baud_rate) while True: line = ser.readline(); line = line.decode("utf-8") #ser.readline returns a binary, convert to string print(line); output_file.write(line);