Skip to content

Instantly share code, notes, and snippets.

@dangpzanco
Last active August 27, 2018 06:43
Show Gist options
  • Save dangpzanco/fd7267c1d9a2aaf95f915c7cccd6a36c to your computer and use it in GitHub Desktop.
Save dangpzanco/fd7267c1d9a2aaf95f915c7cccd6a36c to your computer and use it in GitHub Desktop.
import serial
import sys
import argparse
import csv
import pathlib
from datetime import datetime
def list_serial_ports():
""" Lists serial port names
:raises EnvironmentError:
On unsupported or unknown platforms
:returns:
A list of the serial ports available on the system
:reference:
https://stackoverflow.com/a/14224477/2801287
"""
if sys.platform.startswith('win'):
ports = ['COM%s' % (i + 1) for i in range(256)]
elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
# this excludes your current terminal "/dev/tty"
ports = glob.glob('/dev/tty[A-Za-z]*')
elif sys.platform.startswith('darwin'):
ports = glob.glob('/dev/tty.*')
else:
raise EnvironmentError('Unsupported platform')
result = []
for port in ports:
try:
with serial.Serial(port) as s:
result.append(port)
except (OSError, serial.SerialException):
pass
return result
def parse_rssi(line):
str_line = str(line.decode("utf-8"))
rssi = int(float(str_line[5:10]))
error_count = int(float(str_line[10:]))
return rssi, error_count
def log_rssi_data(filename,num_samples,overwrite):
# List all the available serial ports
serial_ports = list_serial_ports()
if len(serial_ports) == 0:
print('Nenhuma porta serial disponível!')
sys.exit()
else:
print(f'Portas serial disponíveis: {serial_ports}')
# Set CC1350's serial port (change this index if there are multiple devices)
# A placa tem duas portas serial, a segunda é a que envia os dados
com_port = serial_ports[1]
# CSV header keynames (in order)
keys = ['index', 'time', 'rssi', 'error_count']
# Initialize dictionary from keys (with 0)
data = dict.fromkeys(keys, 0)
data['rssi'] = -120
# Set CSV filename
csv_name = pathlib.Path(filename)
if len(csv_name.suffix) == 0:
csv_name = csv_name.with_suffix('.csv')
# Avoid overwriting a file
if not overwrite and csv_name.exists():
timestamp = datetime.now().isoformat().replace(':','')
csv_name = csv_name.with_name(csv_name.stem + timestamp + csv_name.suffix)
# Just write the CSV header
with open(csv_name, 'w', newline='') as f:
w = csv.DictWriter(f, keys)
w.writeheader()
# Print some information
print('Gravando {n} amostras no arquivo {f}.'.format(f=filename,n=num_samples))
# Print header
print('index\ttime\t\t\t\trssi\terror_count')
for i in range(num_samples):
# Indexing
data['index'] = i
data['time'] = datetime.now().isoformat()
with serial.Serial(com_port, baudrate=115200) as ser:
# Read the RSSI data, try again if it fails
successful = False
while not successful:
try:
line = ser.readline()
rssi, error_count = parse_rssi(line)
# Error counter
data['error_count'] = error_count
# RSSI [dB]
data['rssi'] = rssi
# Leave loop
successful = True
except:
pass
# Save dictionary to a CSV line and print line
with open(csv_name, 'a', newline='') as f:
w = csv.DictWriter(f, keys)
w.writerow(data)
# Printing stuff
l = list(w._dict_to_list(data))
string_to_print = '{}\t{}\t{}\t{}'.format(*l)
print(string_to_print,end='\r')
print()
def cmdline_args():
# Make parser object
p = argparse.ArgumentParser(description=
"""
Script para gravar os dados de RSSI lidos via serial do CC1350.
Os dados são salvos em um arquivo CSV, que pode ser lido com
Python (se referir ao script `plot_cc1350.py`), MATLAB ou Excel, entre outros.
""",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
p.add_argument("-f", "--filename", type=str, default='CC1350_data.csv',
help="Nome do arquivo CSV no qual os dados serão salvos.")
p.add_argument("-n", "--num_amostras", type=int, default=100,
help="Número de amostras a serem salvas.")
p.add_argument("-o", "--overwrite", action="store_true",
help="Sobreescrever o arquivo CSV.")
return(p.parse_args())
if __name__ == '__main__':
if sys.version_info < (3,5,0):
sys.stderr.write("Você precisa de Python 3.5+ para rodar esse script.\n")
sys.exit(1)
try:
args = cmdline_args()
log_rssi_data(filename=args.filename,num_samples=args.num_amostras,overwrite=args.overwrite)
print('Fim do script.')
except:
print('\nTente o comando:\npython log_cc1350.py --filename log.txt --num_amostras 1000')
import sys
import argparse
import pathlib
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
def plot_rssi_data(filename):
# Abrir arquivo CSV
filename = pathlib.Path(filename)
data = pd.read_csv(filename)
# Ler colunas da tabela
index = data['index'].values
rssi = data['rssi'].values
error_count = data['error_count'].values
# Converter o tempo para segundos decorridos
dt = pd.to_datetime(data['time'].values)
elapsed_time = dt - dt[0]
time = elapsed_time.total_seconds().values
# Dados úteis
num_amostras = index[-1]
erros = np.hstack([0,np.diff(error_count)])
FER = erros.mean()
fig = plt.figure(figsize=(8,7))
plt.subplot(2, 1, 1)
plt.plot(time, rssi)
plt.title('RSSI x Amostras - {a:.2f} amostras/s'.format(a=num_amostras/time[-1]))
plt.xlabel('Tempo [s]')
plt.ylabel('RSSI [dB]')
plt.grid()
plt.subplot(2, 1, 2)
plt.stem(time, erros)
plt.title('ERRO de Pacote x Amostras - FER = ' + str(FER))
plt.xlabel('Tempo [s]')
plt.ylabel('ERRO')
plt.tight_layout()
plt.show()
def cmdline_args():
# Make parser object
p = argparse.ArgumentParser(description=
"""
Script teste para plotar os dados lidos do CC1350.
""",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
p.add_argument("-f", "--filename", type=str, default='CC1350_data.csv',
help="Nome do arquivo CSV no qual os dados foram salvos.")
return(p.parse_args())
if __name__ == '__main__':
if sys.version_info < (3,5,0):
sys.stderr.write("Você precisa de Python 3.5+ para rodar esse script.\n")
sys.exit(1)
try:
args = cmdline_args()
plot_rssi_data(filename=args.filename)
print('Fim do script.')
except:
print('\nTente o comando:\npython plot_cc1350.py --filename log.txt')
@dangpzanco
Copy link
Author

Passo a passo de instalação do Python (no Windows):

1 - Miniconda: https://conda.io/miniconda.html
Python 3.6 -> https://repo.continuum.io/miniconda/Miniconda3-latest-Windows-x86_64.exe

Na instalação, escolher colocar o caminho do Miniconda/Anaconda no PATH (garante acesso aos comandos python e ipython via cmd)

2 - Instalar alguns pacotes necessários:

a) pyserial - Acesso às portas serial
pip install pyserial

b) pandas - Manipulação de tabelas
conda install pandas

c) matplotlib - Biblioteca de plotagem de gráficos
conda install matplotlib


Como usar os scripts:

$ python log_cc1350.py --help

usage: log_cc1350.py [-h] [-f FILENAME] [-n NUM_AMOSTRAS] [-o]

Script para gravar os dados de RSSI lidos via serial do CC1350. Os dados são
salvos em um arquivo CSV, que pode ser lido com Python (se referir ao script
`plot_cc1350.py`), MATLAB ou Excel, entre outros.

optional arguments:
  -h, --help            show this help message and exit
  -f FILENAME, --filename FILENAME
                        Nome do arquivo CSV no qual os dados serão salvos.
                        (default: CC1350_data.csv)
  -n NUM_AMOSTRAS, --num_amostras NUM_AMOSTRAS
                        Número de amostras a serem salvas. (default: 100)
  -o, --overwrite       Sobreescrever o arquivo CSV. (default: False)

Tente o comando:
python log_cc1350.py --filename log.txt --num_amostras 1000

$ python plot_cc1350.py --help

usage: plot_cc1350.py [-h] [-f FILENAME]

Script teste para plotar os dados lidos do CC1350.

optional arguments:
  -h, --help            show this help message and exit
  -f FILENAME, --filename FILENAME
                        Nome do arquivo CSV no qual os dados foram salvos.
                        (default: CC1350_data.csv)

Tente o comando:
python plot_cc1350.py --filename log.txt

Extra (programas úteis para programação em Python):

1 - Usar o ConEmu como substituto do cmd no Windows
https://conemu.github.io/

2 - Instalar o Sublime Text 3 (Se o Python estiver no PATH, o atalho Ctrl+B executa os scripts.)

a) Instalar a extensão Package Control.
https://packagecontrol.io/installation

b) Usar a extensão JEDI, para autocompletar e tirar dúvidas de funções em Python direto do editor de texto.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment