CraftDuino v2.0
  • - это CraftDuino - наш вариант полностью Arduino-совместимой платы.
  • CraftDuino - настоящий конструктор, для очень быстрого прототипирования и реализации идей.
  • Любая возможность автоматизировать что-то с лёгкостью реализуется с CraftDuino!
Просто добавьте CraftDuino!

Практическое программирование Arduino/CraftDuino - Аналоговый ввод – осциллограф

0. Начало
1. Цифровой ввод — кнопка
2. Аналоговый вывод — Fading
3. Аналоговый ввод – потенциометр

Насколько мы помним, у Arduino есть такая замечательная функция — analogRead(), которая считывает значение с указанного аналогового порта.
А это значит мы можем получить простенький осциллограф :)

4. Аналоговый ввод – осциллограф

Для этого будем считывать данные с аналогового порта Arduino/Freeduino и записывать их в последовательный (COM) порт. А уже из последовательно порта их будет принимать наша программа и строить график сигнала :)

Итак, скетч для Arduino/Freeduino будет совсем простой:


//
// Oscilloscope
//

#define ANALOGA_IN 0

void setup() 
{
  Serial.begin(38400); // указываем скорость работы с COM-портом
}

void loop() 
{
  int val;
  val = analogRead(ANALOGA_IN); // считываем данные 
  Serial.println( val);         // записываем данные в порт
}

Подключившись к порту мы увидим что-то вроде:

403
401
384
361
346
341
341
358
376
386


Осталось только написать программу для превращения данных из последовательного порта в красивый график :)

На основе программы построения графиков функций
Graph.pyw (http://purepython.narod.ru/tkinter.html)
я набросал программу для отображения данных из COM-порта.

Graph-serial.py

Для работы потребуется Python и библиотека:
pySerial – собственно сама библиотека для работы с COM-портом (для её работы под Windows требуется pyWin32)

собственно работа с COM-портом из данной библиотеки крайне проста:


#
# для работы с COM-портом нужна библиотека
# pySerial, кроме того, под винду понадобится еще pyWin32
#
import serial
SERIAL_PORT = 'COM1'
SERIAL_SPEED = 38400

ser = serial.Serial(SERIAL_PORT, SERIAL_SPEED)

while 1:
            #s = ser.read()            # считывается один байт
            s = ser.readline().strip() # считывается строка и убираются символы “\r\n”
            print s                    # печатаем на экран


Весь остальной код нужен для работы с графикой Tkinter и отображения линий графика с помощью
create_line(x1,y1,x2,y2,….,xn,yn)

Однако, у меня пока не получилось заставить работать программу в системе real-time –
поэтому бесконечный цикл заменил на конечный
for i in range(0,200):

В итоге, получается, что при нажатии на кнопку «Serial» скрипт считывает 200 значений с COM-порта и строит по ним график:


Другие примеры осциллографов на ардуине:

для проекта Carduino

И самый интересный
шестиканальный Arduinoscope на processing :)
http://code.google.com/p/arduinoscope/wiki/Usage

Processing является простым скриптовым языком для создания визуализаций с помощью платформы Java Virtual Mashine.
Processing использовался для создания некоторых реклам Nike, клипов Radiohead & R.E.M., инсталляций в музеях, а также входит в учебные курсы некоторых вузов США.
http://processing.org
http://processing.org/download/processing-1.0.5.zip — версия IDE для Windows


Код скетча для Arduino:

//
// Oscilloscope
// http://accrochages.drone.ws/en/node/90
//

#define ANALOGA_IN 0
#define ANALOGB_IN 1
#define ANALOGC_IN 2
#define ANALOGD_IN 3
#define ANALOGE_IN 4
#define ANALOGF_IN 5

int counter = 0;

void setup() 
{
  Serial.begin(38400);
}

void loop() 
{
  int val[5];

  val[0] = analogRead(ANALOGA_IN);
  val[1] = analogRead(ANALOGB_IN);
  val[2] = analogRead(ANALOGC_IN);
  val[3] = analogRead(ANALOGD_IN);
  val[4] = analogRead(ANALOGE_IN);
  val[5] = analogRead(ANALOGF_IN);


  Serial.print( "A" );
  Serial.print( val[0] );

  Serial.print( "B" );
  Serial.print( val[1] );
  Serial.print( "C" );
  Serial.print( val[2] );
  Serial.print( "D" );
  Serial.print( val[3] );
  Serial.print( "E" );
  Serial.print( val[4] );
  Serial.print( "F" );
  Serial.print( val[5] );
}

Как видно, сигнал считывается со всех шести аналоговых входов Arduino и сопровождаясь буквенными обозначениями передаётся в последовательный порт.

Подключившись к порту увидим следующее:

A129B131C132D133E134F131A129B131C133D133E134F131A132B134C136D137E138F133



Скетч для Processing-а (как видите IDE – почти такая же :):

Здесь так же используется функция setup(), а вот вместо loop() используется функция draw()

Сам скетч отличается от авторского только явным указанием последовательного порта:
port = new Serial(this,"COM1", 38400); // Serial.list()[0]



import processing.serial.*;

Serial port; // Create object from Serial class
int valA;
int valB;
int valC;
int valD;
int valE;
int valF;
// this should have been some kind of 2-diminsional array, I guess, but it works.
int[] valuesA;
int[] valuesB;
int[] valuesC;
int[] valuesD;
int[] valuesE;
int[] valuesF;

PFont fontA;
PFont fontB;

void setup()
{

// make sure you have these fonts made for Processing. Use Tools...Create Font.
// "fontA" is a 48 pt font of some sort. It's what we use to show the "now" value.
  fontA = loadFont("CourierNewPSMT-48.vlw");

// "fontB" is a 14 pt font of some sort. It's what we use to show the min and max values.
  fontB = loadFont("CourierNewPSMT-14.vlw");

// I wouldn't change the size if I were you. There are some functions that don't use
// the actual sizes to figure out where to put things. Sorry about that.
  size(550, 600);

// Open the port that the board is connected to and use the same speed
// anything faster than 38.4k seems faster than the ADC on the Arduino can keep up with.
// So, if you want it to be smooth, keep it at or below 38400. 28800 doesn't work at all,
// I do not know why. If you turn on smooth() you need to drop the rate to 19.2k or lower.
// You will probably have to adjust Serial.list()[1] to get your serial port.
  port = new Serial(this,"COM1", 38400); // Serial.list()[0]

// These are 6 arrays for the 6 analog input channels.
// I'm sure it could have just as well been a 2d array, but I'm not that familiar
// with Processing yet and this was the easy way out.
  valuesA = new int[width-150];
  valuesB = new int[width-150];
  valuesC = new int[width-150];
  valuesD = new int[width-150];
  valuesE = new int[width-150];
  valuesF = new int[width-150];
// the -150 gives us room on the side for our text values.

// this turns on anti-aliasing. max bps is about 19.2k.
// uncomment out the next line to turn it on. Personally, I think it's better left off.
//smooth();
}

int getY(int val) 
{
  // I added -40 to this line to keep the lines from overlapping, to
  // keep the values within their gray boxes.
  return (int)(val / 1023.0f * (height-40)) - 1;
}

void draw()
{
  String decoder = "";
  while (port.available() >= 3)
  {
    // read serial until we get to an "A".
    decoder = port.readStringUntil(65);
  }
// sanity check. make sure the string we got from the Arduino has all the values inside.
if ((decoder.indexOf("B")>=1) & (decoder.indexOf("C")>=1) &
(decoder.indexOf("D")>=1) & (decoder.indexOf("E")>=1) &
(decoder.indexOf("F")>=1))
{
  // decoder string doesn't contain an A at the beginning. it's at the end.
  valA=int(decoder.substring(0,decoder.indexOf("B")));
  //println("A" + str(valA));
  valB=int(decoder.substring(decoder.indexOf("B")+1,decoder.indexOf("C")));
  //println("B" + str(valB));
  valC=int(decoder.substring(decoder.indexOf("C")+1,decoder.indexOf("D")));
  //println("C" + str(valC));
  valD=int(decoder.substring(decoder.indexOf("D")+1,decoder.indexOf("E")));
  //println("D" + str(valD));
  valE=int(decoder.substring(decoder.indexOf("E")+1,decoder.indexOf("F")));
  //println("E" + str(valE));
  valF=int(decoder.substring(decoder.indexOf("F")+1,decoder.indexOf("A")));
  //println("F" + str(valF));
  }

  //shift the new values into the array, move everything else over by one
  for (int i=0; i<width-151; i++) 
  {
    valuesA[i] = valuesA[i+1];
    valuesB[i] = valuesB[i+1];
    valuesC[i] = valuesC[i+1];
    valuesD[i] = valuesD[i+1];
    valuesE[i] = valuesE[i+1];
    valuesF[i] = valuesF[i+1];
  }

  // -151 because the array is 151 less than the width. remember we
  // saved room on the side of the screen for the actual text values.
  valuesA[width-151] = valA;
  valuesB[width-151] = valB;
  valuesC[width-151] = valC;
  valuesD[width-151] = valD;
  valuesE[width-151] = valE;
  valuesF[width-151] = valF;

  background(0);

  textFont(fontA);

  // I'm sure these c/should have been determined using height math, but I don't have the time really.
  // Draw out the now values with the big font.
  text(valA + 1, (width-140), 108-5);
  text(valB + 1, (width-140), 206-5);
  text(valC + 1, (width-140), 304-5);
  text(valD + 1, (width-140), 402-5);
  text(valE + 1, (width-140), 500-5);
  text(valF + 1, (width-140), 598-5);

  textFont(fontB);
  // Draw out the min and max values with the small font.
  // the h value (30,128,266,etc) is a function of height,
  // but I didn't bother to actually do the math.
  // I guess it's (98*n)+30 where n is 0,1,2,3,4,5, but I don't know
  // exactly how height (600) relates to 98... ((h/6)-2??)
  drawdata("0", width-90, 30, valuesA);
  drawdata("1", width-90, 128, valuesB);
  drawdata("2", width-90, 226, valuesC);
  drawdata("3", width-90, 324, valuesD);
  drawdata("4", width-90, 422, valuesE);
  drawdata("5", width-90, 520, valuesF);

for (int x=150; x<width-1; x++) 
{
  // next line adjusts the color of the stroke depending on the x value. (fades out the end of the line)
  check(x,255,0,0);

  // next line draws the line needed to get this value in the array to the next value in the array.
  // the offsets (6+ in the next line) were used to get the values where I wanted them without
  // having to actually do real spacial math. There's a hack in getY that offsets a little, too.
  line((width)-x,
  6+((height/6)*0)+((height-1-getY(valuesA[x-150]))/6), (width)-1-x,
  6+((height/6)*0)+((height-1-getY(valuesA[x-149]))/6));
  check(x,0,255,0);
  line((width)-x,
  4+((height/6)*1)+((height-1-getY(valuesB[x-150]))/6), (width)-1-x,
  4+((height/6)*1)+((height-1-getY(valuesB[x-149]))/6));
  check(x,0,0,255);
  line((width)-x,
  2+((height/6)*2)+((height-1-getY(valuesC[x-150]))/6), (width)-1-x,
  2+((height/6)*2)+((height-1-getY(valuesC[x-149]))/6));
  check(x,255,255,0);
  line((width)-x,
  0+((height/6)*3)+((height-1-getY(valuesD[x-150]))/6), (width)-1-x,
  0+((height/6)*3)+((height-1-getY(valuesD[x-149]))/6));
  check(x,0,255,255);
  line((width)-x,
  -2+((height/6)*4)+((height-1-getY(valuesE[x-150]))/6), (width)-1-x,
  -2+((height/6)*4)+((height-1-getY(valuesE[x-149]))/6));
  check(x,255,0,255);
  line((width)-x,
  -4+((height/6)*5)+((height-1-getY(valuesF[x-150]))/6), (width)-1-x,
  -4+((height/6)*5)+((height-1-getY(valuesF[x-149]))/6));
}

  // draw the boxes in gray.
  stroke(170,170,170);

  // these 5 lines divide the 6 inputs
  line(0,108,width-1,108);
  line(0,206,width-1,206);
  line(0,304,width-1,304);
  line(0,402,width-1,402);
  line(0,500,width-1,500);

  // these four lines make up the outer box
  line( 0, 0, width-1, 0); // along the top
  line(width-1, 0, width-1, height-1); // down the right
  line(width-1, height-1, 0, height-1); // along the bottom
  line( 0, height-1, 0, 0); // up the left
}

void drawdata(String pin, int w, int h, int[] values)
{
  text("pin: " + pin, w, h);
  text("min: " + str(min(values) + 1), w, h + 14);
  text("max: " + str(max(values) + 1), w, h + 28);
}

void check(int xx, int rr, int gg, int bb)
{

// floating point operations in Processing are expensive.
// only do the math for the float (fading out effect) if
// we have to. You can change 170 to 160 if you want it to
// fade faster, but be sure to change the other 170 to 160
// and the 20 to 10.
// (20 is the difference between 170 and 150)
  if (xx<=170)
  {
    float kick = (parseFloat(170-xx)/20)*255;
    // invert kick so the brighter parts are on the left side instead of the right.
    stroke(rr,gg,bb,255-kick);
  }
  else
  {
    stroke(rr,gg,bb);
  }
}

Запустив это приложение увидим красивую картинку :)

Собственно – остаётся:

— научиться определять уровень напряжения сигнала
уровень напряжения можно получить из простой формулы
5V/1024 значений = 0,004883 В/значение (4,883 мВ).

— и определять частоту сигнала :)

NB
Если посмотреть код скетча — увидим, что Arduino работает с COM-портом на скорости 38400 бод (бит/сек ru.wikipedia.org/wiki/Бод)
для передачи байта по протоколу RS-232 используется 10 бит (стартовый бит, 8 бит данных, бит чётности (не используется), стоповый бит)
38400/10 = 3840 байт/сек
т.к. на один отсчёт идёт 3 байта (4-5) получаем
3840/3 = 1280 (960-768) отсчётов в секунду

читать далее: 5. Генерация звука – пьезоизлучатель

По теме:
Processing и Arduino
Arduino, термины, начало работы
КМБ для начинающих ардуинщиков
Состав стартера (точка входа для начинающих ардуинщиков)
  • +1
  • 10 августа 2009, 14:03
  • admin

Комментарии (9)

RSS свернуть / развернуть
+
0
Установил Processing 1.2, загрузил программу, но появляются ошибки на эту строчку:
port = new Serial(this,'COM1', 38400); // Serial.list()[0]

пишет: Badly formated character constant

Что бы это значило?

Прежде, чем двигаться дальше, хочу понять как это работает.
avatar

Alexandr

  • 19 декабря 2010, 15:38
+
0
в коде скетча была опечатка (одинарные кавычки), а правильно двойные:
port = new Serial(this,"COM1", 38400);
avatar

admin

  • 19 декабря 2010, 16:04
+
0
Эту проблему решил (вместо одинарных кавычек нужно писать двойные). Проблема другого плана: загружаю скетч в Ардуино и запускаю программу на Питоне. Значение порта считывается. При этом Ардуино больше не может посылать в этот порт какие-либо значения, так как порт занят программой на Питоне.
Может какая фишка есть на Питоне чтобы не занимать порт, а только слушать его?
avatar

Alexandr

  • 19 декабря 2010, 19:17
+
0
Ещё вопрос. Запустил программу на Процессинге 1.2. Значения для графика очень долго читаются. В чем причина? (В моём компе, характерной заторможенностью или чем-то ещё)
avatar

Alexandr

  • 19 декабря 2010, 19:37
+
0
Прикольно. Оказывается сигнал на порте № 0 (к примеру) влияет на все остальные 5 портов — согласно графикам, полученным программой.
avatar

Alexandr

  • 19 декабря 2010, 19:44
+
0
наводки=\
если оч мешает и нужно только как показометр(форму наблюдать а не точные измерения проводить) можно повесить резисторы по 1к на землю.
Должно помочь=)
avatar

Zoltberg

  • 21 декабря 2010, 13:30
+
0
А теперь мне нужно описание языка программирования, на котором пишутся скечи для Processing, на русском языке.
avatar

Alexandr

  • 19 декабря 2010, 19:46
+
0
Не правиьлновыразился. Не языка, а основных функций для работы со строками, портами, графикой, текстом.
avatar

Alexandr

  • 19 декабря 2010, 19:54
+
0
называть это «осциллографом» как-то язык не поворачивается. У реальных осциллографов частота семплирования обычно порядка гигасемпл/сек. А это так, рисовалка.
avatar

nes

  • 2 июля 2012, 12:05

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.