Você está na página 1de 21

Osciloscpio com Arduino: como

montei meu (quase-)osciloscpio


Um (quase-)osciloscpio com Arduino, display LCD
grfico, 2 potencimetros, 1 boto e um resistor, que
cabe em uma mini-protoboard.
Rearranjando os componentes do meu jogo de paredo com Arduino e display Nokia, e
acrescentando um boto e um potencimetro, criei um (quase-)osciloscpio bastante
funcional, que voc pode modificar (o cdigo est no Github) para as suas finalidades.

O osciloscpio um dos instrumentos essenciais de um laboratrio de eletrnica bem


equipado, e geralmente consiste em um display que plota continuamente um (ou mais)
pontos ou linhas representando a tenso lida na sua entrada.
Meu (quase-)osciloscpio atende de forma bem restrita definio acima, e assim no
me sinto vontade em cham-lo de osciloscpio. Por ler diretamente os valores de uma

porta analgica do Arduino, ele fica restrito capacidade dela, normalmente restrita a
tenses positivas entre 0 e 5V (voc pode aumentar essa faixa usando circuitos externos
tais como divisores de tenso, se desejar).
Ele tambm no tem suporte a alguns recursos comuns em osciloscpios atuais, como
seleo dinmica da frequncia/perodo, acompanhamento simultneo de mais de uma
entrada, entre outros e alguns desses talvez entrem em uma futura verso do
programa, se eu tiver a demanda. Em especial, ele no tenta fazer uma relao entre o
valor lido na porta e a tenso externa correspondente a ele.
Meu (quase-)osciloscpio um (no-)osciloscpio que plota os valores lidos de uma
porta analgica, com controle dinmico de centro e amplitude do grfico.
O que ele faz, atendendo plenamente minha demanda atual, exibir um grfico do
valor lido da porta (atualizado ~10 vezes por segundo), oferecendo duas maneiras de
selecionar dinamicamente os parmetros do grfico. Uma delas manual, girando um
par de potencimetros que definem qual valor corresponde ao centro do eixo Y do
grfico (default: 512) e qual a amplitude exibida na tela (default: 1024).
A outra automtica: pressionando um boto que define a partir das 80 leituras mais
recentes o melhor centro e amplitude para visualizar os dados atualmente em exibio.
Alm do grfico contnuo, que preenche a tela a cada ~8 segundos, so exibidos
permanentemente no alto da tela os valores mnimo, central e mximo da faixa exibida
no eixo Y, e na parte de baixo da tela um acompanhamento do valor mais recente lido e
plotado.

Como montar o (quase-)osciloscpio com Arduino


Como voc deve ter notado nas imagens acima, o nmero de cabos na montagem em
protoboard parece grande, mas em parte isso se deve minha escolha da montagem em
uma mini-protoboard, para deix-lo montado por uns tempos antes de fixar as conexes
em alguma verso mais permanente.

Mas as conexes so simples, como veremos a seguir.

Para os potencimetros que eu usei, o pino esquerdo (use como referncia a imagem
acima) conectado ao GND, e o pino direito conectado ao pino AREF (ou 3V3). O
pino central de cada um deles conectado a um pino analgico do Arduino na minha
montagem, o pino central do potencimetro da esquerda foi conectado ao pino A2 do
Arduino, e o pino central do potencimetro da direita foi conectado ao pino A4 do
Arduino.

Eu usei um boto bem simples, com 2 terminais e NA (normalmente aberto). Um dos


terminais conectado ao GND, e o outro a um pino digital do Arduino (o 12, no meu
caso).

Para ter dados significativos para plotar, basta conectar um sensor a uma porta analgica
(a A0, no meu caso). Meu escolhido foi um LDR, um resistor cuja resistncia varia
conforme a incidncia de luz, servindo portanto para detectar a luminosidade. Usei nele
uma configurao com pull-up, ilustrada no diagrama acima: um de seus terminais vai
conectado ao GND do Arduino, o outro vai conectado tanto ao pino A0 do Arduino
quanto a um resistor de 10K ligado ao pino AREF ou 3V3 do Arduino.

Para completar, s falta descrever a conexo do mdulo display LCD 84x48, que um
dia j foi parte de um celular Nokia 5110. J expliquei detalhadamente sobre ele no
artigo anterior, portanto agora vou apenas listar as conexes de pinos que usei:

pino 1 (VCC) do display pino 3V3 do Arduino

pino 2 (GND) do display pino GND do Arduino

pino 3 (SCE) do display pino 10 do Arduino

pino 4 (RST) do display pino 9 do Arduino

pino 5 (D/C) do display pino 8 do Arduino

pino 6 (DN) do display pino 11 do Arduino

pino 7 (SCLK) do display pino 13 do Arduino

pino 8 (LED) do display deixei desconectado (sem backlight)

Duas observaes importantes:


1. O display que eu usei feito para operar em nvel lgico de 3,3V. Eu o conectei
a um Uno Plus, clone do Arduino Uno que permite selecionar o nvel lgico de
3,3V. A maior parte dos Arduinos opera com nvel lgico de 5V, e neste caso o
fabricante recomenda usar resistores de 10K entre os pinos do Arduino e os
pinos 4, 5, 6 e 7 do mdulo, e um resistor de 1K entre o pino do Arduino e o
pino 3 do mdulo, como vimos no artigo anterior.
2. Existem diversas variaes de mdulo display Nokia 5110 venda, algumas
com pinagens diferentes da descrita acima, e outras com nveis lgicos
ajustveis ou diferentes. Consulte a documentao do seu mdulo especfico e
faa as adequaes necessrias.

Como programar o (quase)-osciloscpio no Arduino


Publiquei o cdigo completo no meu Github; abaixo voc v a verso corrente na data
da publicao deste artigo, colorizada como de hbito, para facilitar as explicaes que
vm logo a seguir. Vale tambm olhar as explicaes do cdigo do artigo anterior, pois
so bastante complementares, em especial quanto ao uso do display, e foram
apresentadas com maior detalhamento.

// nokia-monitor-grafico

// um nao-osciloscopio em um Arduino com display Nokia 5110

// (c) Augusto Campos 2015 - BR-Arduino.org

// licenca ao final deste arquivo.

// Usa 2 potenciometros (um para definir o centro do eixo Y

// e o outro para definir a amplitude) e um botao para

// selecionar automaticamente os melhores parametros de

// exibicao.

#include <SPI.h>

#include <Adafruit_GFX.h>

#include <Adafruit_PCD8544.h>

// configuracoes de hardware

const byte pinoBotao=12;

const byte pot1Analogico=A2;

const int pot1max=1007; // valor lido no pino do


potenciometro quando girado ate o maximo

const byte pot2Analogico=A4;

const int pot2max=1007;

// SPI via software:

// pino 13 - Serial clock out (SCLK;MOSI)

// pino 11 - Serial data out (DN;DIN)

// pino 8 - Data/Command select (D/C)

// pino 10 - LCD chip select (CS;SCE)

// pino 9 - LCD reset (RST)

Adafruit_PCD8544 display = Adafruit_PCD8544(13, 11, 8, 10,


9);

void setup()

display.begin();

display.clearDisplay();

display.setContrast(50);

display.display();

pinMode(pinoBotao,INPUT_PULLUP);

int X=0;

int Y=0;

int cMin;

int cMax;

int oldX=-1;

int oldY=-1;

int cY;

int cCentro;

int amplitude;

int old_cMin;

int old_cMax;

int linha[40];

int extremoAlto=0;

int extremoBaixo=1024;

int old_extremoAlto=1024;

int old_extremoBaixo=0;

unsigned long botaoUltimoM=0;

int pot1val;

int pot2val;

int old_pot1val;

int old_pot2val;

boolean botaoMudou=true;

void plotaPonto(int YY, boolean limpar=true) {

const byte xMax=80;

if (limpar) {

if (X<xMax-10) display.drawLine(X+10,5,X+10,40,WHITE);

if (X==0) display.fillRect(X,5,X+10,40,WHITE);

linha[X]=YY;

if (YY>extremoAlto) extremoAlto=YY;

if (YY<extremoBaixo) extremoBaixo=YY;

if (X==40) {

old_extremoAlto=extremoAlto;

extremoAlto=0;

old_extremoBaixo=extremoBaixo;

extremoBaixo=1024;

if (cMax-cMin!=1023) {

cY=constrain(YY,cMin,cMax)-cMin;

cY=map(cY,0,cMax-cMin,0,30);

else cY=map(YY,0,1024,0,35);

if (X>0) display.drawLine(oldX,oldY,X,39-cY,BLACK);

else display.drawPixel(X,39-cY,BLACK);

X++;

if (X==80) X=0;

oldX=X;

oldY=39-cY;

void replota() {

int Xsave=X;

X=0;

display.fillRect(0,8,84,40,WHITE);

for (byte i=0;i<Xsave;i++) {

plotaPonto(linha[i],false);

X=Xsave;

void autoEscala() {

display.setCursor(75,40);

display.print("x");

int _dAmp;

if (X<10) {

_dAmp=(old_extremoAlto-old_extremoBaixo)/20;

if (_dAmp<30) _dAmp=30;

cMin=old_extremoBaixo-_dAmp;

cMax=old_extremoAlto+_dAmp;

} else {

_dAmp=(extremoAlto-extremoBaixo)/20;

if (_dAmp<30) _dAmp=30;

cMin=extremoBaixo-_dAmp;

cMax=extremoAlto+_dAmp;

cCentro=cMin+((cMax-cMin)/2);

old_cMin=cMin;

old_cMax=cMax;

replota();

void atualizaEscala() {

cCentro=1024-int(pot1val*1.0/pot1max*1024);

int folga=cCentro;

if ((1024-cCentro)<folga) folga=1024-cCentro;

amplitude=1024-int(pot2val*1.0/pot2max*1024);

if (amplitude > (2*folga)) amplitude=2*folga;

cMin=cCentro-amplitude/2;

cMax=cCentro+amplitude/2;

if (abs((old_cMin-cMin)>2) | (abs(old_cMax-cMax)>2)) {

if (X>0) replota();

old_cMin=cMin;

old_cMax=cMax;

void processaEscala() {

if ((digitalRead(pinoBotao)==LOW) & ((abs(millis()botaoUltimoM))>200)) {

if (botaoMudou==true) {

autoEscala();

botaoUltimoM=millis();

botaoMudou=false;

if (!botaoMudou & ((digitalRead(pinoBotao)==HIGH)))


botaoMudou=true;

if ( (abs(old_pot1val-pot1val)>10) | (abs(old_pot2valpot2val)>10) ) {

atualizaEscala();

old_pot1val=pot1val;

old_pot2val=pot2val;

void atualizaPlacares() {

display.fillRect(0,40,84,8,WHITE);

display.setCursor(5,40);

display.print(cY);

display.setCursor(40,40);

display.print(Y);

display.fillRect(0,0,84,8,WHITE);

display.setCursor(0,0);

display.print(cMin);

display.setCursor(32,0);

display.print(cCentro);

display.setCursor(58,0);

display.print(cMax);

void loop() {

delay(100);

Y=analogRead(A0);

plotaPonto(Y);

pot1val=analogRead(pot1Analogico);

pot2val=analogRead(pot2Analogico);

processaEscala();

atualizaPlacares();

display.display();

/*

2015 Augusto Campos http://augustocampos.net/ (13.04.2015)

Licensed under the Apache License, Version 2.0 (the


"License");

you may not use this file except in compliance with the
License.

You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,


software

distributed under the License is distributed on an "AS IS"


BASIS,

WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express


or implied.

See the License for the specific language governing


permissions and

limitations under the License.

*/

De cima para baixo, comeamos pelo trecho em salmo, que tem a incluso das
bibliotecas de comunicao e de suporte ao display, seguida da definio de constantes
de posio e calibrao do boto e dos potencimetros, da importante instanciao do
objeto display (quando informamos para a biblioteca quais os pinos do Arduino que
esto conectados aos pinos relevantes do LCD), e do

setup

propriamente dito, que se

restringe a deixar a tela pronta para uso e definir o modo do pino conectado ao boto (os
pinos em uso pelos potencimetros so analgicos e no precisam ser inicializados, e os
do display so inicializados pela biblioteca).
A seguir, em verde, temos a definio das variveis globais que governam quase tudo
que acontece no (no-)osciloscpio. Por exemplo:
plotado o prximo ponto,
e

linha[40]

cMin

cMax

a coluna da tela em que ser

so os limites da amplitude do display,

onde fica gravada a srie de pontos da volta atual do grfico. Voc ver

as referncias s demais quando estudar as funes a seguir.


Logo aps, em vermelho, temos as 2 funes responsveis pelo grfico. A
primeira,

plotaPonto ,

recebe como parmetro

YY

um valor lido na porta analgica, e o

converte em um ponto ou linha na tela. Ela chamada em 2 contextos diferentes:


pelo

loop()

parmetro

(para postar um ponto lido diretamente da porta analgica, sempre com o

limpar

como true) e pela funo

replota()

alterao da escala, sempre com o parmetro

limpar

(para retraar a linha aps uma

como false).

Note que o comeo da funo tem um grande bloco de cdigo executado apenas
quando
do

limpar=true ,

loop() .

ou seja, quando a funo chamada diretamente a partir

Nesse bloco, alm de limpar o campo do grfico algumas colunas frente

da coluna atual (definida por X ), tambm executada a gravao do valor no histrico


( linha[] ) e ajustando o registro de valores mnimos e mximos, necessrios para
permitir o ajuste automtico da amplitude do grfico, caso o usurio aperte o boto.
As operaes matemticas essenciais para ajustar o valor lido amplitude e escala
da tela so realizadas pelas funes constrain e map, da biblioteca do Arduino.

Em seguida, as funes

constrain()

map()

da biblioteca do Arduino so usadas

para restringir o valor de YY amplitude configurada e convert-lo escala em uso. A


s plotar o ponto (ou linha), ajustar o
Comparada complexidade da

para a prxima volta, e pronto :-)

plotaPonto ,

a funo

simplesmente refaz (chamando mltiplas vezes a

replota()

plotaPonto )

trivial: ela

o grfico na tela, para

ajust-lo quando o usurio muda a escala.


A seguir, em laranja, vm as 3 funes que gerenciam a escala do grfico. A primeira
delas,

autoEscala() ,

chamada quando o usurio aperta o boto de seleo

automtica. Os clculos dela so simples, ajustando o ponto mnimo e mximo da escala


a partir do mnimo e mximo registrados na volta mais recente do grfico (com uma
folga de 10%), e em seguida chamando a j apresentada
A

atualizaEscala()

replota() .

faz clculos similares, mas usa como entrada no o histrico, e

sim a posio dos 2 potencimetros: o da esquerda define o centro, e o da direita define


a amplitude.
A tcnica de debounce consiste em usar temporizaes ou outros mtodos para
evitar que as instabilidades no contato eltrico faam com que um pressionamento
do boto seja percebido como mltiplos pressionamentos.
J a

processaEscala()

faz um trabalho mais bruto, sem clculos. ela que verifica se

o boto foi pressionado, ou se os potencimetros foram movidos, e chama as duas


funes acima, dependendo do caso. A leitura do boto usa uma tcnica
chamada

debounce ,

para evitar que o processamento ocorra mais de uma vez no caso de

o boto permanecer pressionado, ou de ele alternar vrias vezes entre pressionado e


solto (situao comum no momento em que o contato fsico da chave est se
estabelecendo ou se soltando).
Em marrom temos a

atualizaPlacares() ,

que herdou seu nome do jogo que eu

implementei no mesmo display, e que simplesmente atualiza na tela 5 nmeros de


acompanhamento: os extremos e centro da escala, ao alto, e os valores de Y (convertido
e original) mais recentemente plotado, abaixo.
O loop() fica simples porque toda a complexidade foi terceirizada para as funes
de apoio.

Para finalizar, em fcsia, temos o

loop()

do Arduino. Como toda a complexidade foi

terceirizada para as funes que j vimos, ele ficou bem simples, limitando-se a ler e
plotar periodicamente o valor do sensor, ler os potencimetros e processar a escala,
atualizar os placares e chamar a importante funo

display.display() ,

nico

momento em que as alteraes feitas nos demais pontos do programa so realmente


atualizadas na tela visvel.
Para o futuro (acompanhe no meu Github), tenho em mente adicionar algumas
funcionalidades: plotar o valor de uma segunda porta analgica, tornar configurvel a
frequncia/perodo, oferecer um display de status que possa apontar a mdia e outras
funes estatsticas da srie histrica, entre outras. Para o momento, entretanto, estou
bem satisfeito com o meu (no-)osciloscpio ;-)