...

среда, 9 октября 2013 г.

Сбор показаний датчиков и их отображение


Людям нравятся красивые презентации. Красивые картинки, немного текста, меняющиеся слайды. Красивая картинка позволяет быстро передать информацию человеку, сообщить самое важное. Мы все это знаем. Я вот думаю, как «скрестить ежа и ужа»?


Как наглядно на мониторе компьютера представить процессы, происходящие внутри микроконтроллера или ПЛИС? Или как показать, что происходит внутри всей системы автоматики, реализованной на микроконтроллере или ПЛИС?


Вообще-то правильный ответ я знаю – нужно использовать SCADA системы.

SCADA – это supervisory control and data acquisition, диспетчерское управление и сбор данных. Но мы не ищем легких путей, мы хотим немножко изобрести своего велосипеда.


Хочу поделиться своим простым методом отображения данных, получаемых от датчиков и сенсоров из платы управления.


Тут, прежде всего, нужно разделить три компоненты:



  • протокол передачи данных. Нужно как-то кодировать передаваемую от контроллера к компьютеру информацию.

  • firmware в системе автоматики, в микроконтроллере или ПЛИС. Этот модуль должен собарать показания датчиков и передавать их на компьютер для отображения в «красивом виде»

  • программное обеспечение визуализации. Показывает состояние и значения датчиков. Может строит какие-то графики.


Вот так, по порядку попробую рассказать.


Протокол передачи данных.




В настоящее время физических возможностей подключить какое-то устройство к компьютеру или ноутбуку фактически осталось только две: сетевое подключение через Ethernet/WiFi или USB. Практически ушли в прошлое «настоящие» параллельные и последовательные порты. С ними было просто. Конечно их еще можно найти, если поискать. Но лучше в эту сторону и не думать.

Ethernet пока отставляю в сторону. Для передачи по сети нужно в контроллере иметь драйвера стека TCP/IP, как правило это тянет за собой наличие ОС, обычно Linux или ucLinux. Потом потребуется интерфейс настройки сети: а какой IP адрес? А статический или динамический? А какая маска и gateway? В общем не очень просто в реализации и настройке.


USB кажется гораздо проще, хотя и тут много подводных камней: а какой класс/субкласс устройства? А нужно ли ему драйвера или используются стандартные драйвера той же Windows?

И опять возвращаемся «на круги своя» — проще всего использовать последовательный порт через USB. В простейшем случае есть шнуры типа USB2Serial. Ну или как отличный вариант для разработчика плат и контроллеров – различные микросхемы FTDI.


ОК, все же выбираем последовательный порт через USB. А раз так, то значит пересылка данных может быть в виде последовательности символов. Значит дальше еще проще: показания датчиков можно передавать в виде строк вида «НАЗВАНИЕ_ДАТЧИКА=ЗНАЧЕНИЕ»


При таком подходе мы сможем легко увеличивать количество опрашиваемых сенсоров, и легко менять их тип. Состояние концевика или геркона будет передваться, например, в виде строк «but0=1» или «but1=0». Значение температуры можно передавать в виде строки «t0=36,6». Строки проще всего разделять символами «перевода каретки»: 0x0D 0x0A.


Так, на первых порах даже и программа визуализации на компьютере не нужна. Можно просто запустить программу терминала вроде Putty и смотреть на показания датчиков из контроллера.


Контроллер.




Мой контроллер выполнен на базе ПЛИС Altera Cyclone III. На самом деле это известная разработчикам плата Марсоход2. Я уже писал про некоторые проекты, выполненные на ней. Например, когда-то мы сделали на этой плате на чистой ПЛИС FM радио передатчик. А еще мы сделали на ней USB Tracker. Есть и другие проекты.

Вот такая плата:



На плате уже есть 2 кнопочки – это первые два датчика для моих экспериментов.

Еще я подключил микросхему термометра ds18b20 – это второй датчик.

Можно еще использовать АЦП платы для измерения чего-то-пока-не-знаю-чего. Пока здесь просто переменный резистор вместо датчика.


Важно, что на плате уже стоит микросхема FTDI FT2232HL, которая обеспечивает связь с компьютером через USB в виде виртуального последовательного порта. Скорость передачи аж 12Мбит/сек. Это условно 1,2 Мбайта/сек. Если, например, плата опрашивает датчики каждые 100 миллисекунд, то получается можно за каждый опрос передавать компьютеру более 100Кбайт данных. Вполне прилично.


Сейчас не буду рассказывать о проекте для платы и ПЛИС Cyclone III. Для этого есть отдельная статья. В этой статье подробно рассказано, как опрашиваются данные и передаются результаты в компьютер через последовательный порт. Лучше перейдем к рассмотрению 3-ей компоненты – программы визуализации значений датчиков, которая работает на компьютере.


Программа визуализации данных.




Тут хотелось все сделать быстро и просто. На чем писать программу, чтоб было просто написать, менять, дополнять?

Я выбираю Питон, хотя честно говоря опыта нет. Просто кажется, что это будет хорошо. Как сказал один из хабражителей (извините, не помню кто) – дополнительно хотелось бы «потреннировать своего питона».


Итак, поскольку программа будет графическая, то попробую встроенный в питон Tkinter. Для работы с последовательным портом буду использовать pyserial.


Хочется написать эдакий набор классов — для каждого типа датчиков свой класс.


Самый простой класс – двоичный датчик. Это может быть кнопка, концевик, геркон. Его значения 0 или 1. Соответствующий этому датчику питоновский класс BinSensor отображает всего 2 состояния. Предлагаю каждому состоянию нарисовать свое изображение. Изображение прикрепляется к фиксированным координатам окна программы поверх фонового изображения.



Как только пришло значение «0» показываем первую картинку. Если пришло значение «1», то показываем вторую картинку. Изображения могут быть любыми — все зависит от нашей фантазии.


Вот этот класс:



#!/usr/bin/env python

import Tkinter
from Tkinter import *

root = Tk()

class BinSensor:
def __init__(self,name,img0,img1,x,y):
self.name=name
self.x=x
self.y=y
self.img0=PhotoImage(file=img0)
self.img1=PhotoImage(file=img1)
self.val=0
self.label_img=Label(root,image=self.img0)
self.label_img.place(x=self.x,y=self.y)
def set(self,state):
if(self.val==state): return
self.val=state
if( int(state)==0 ):
self.label_img.configure(image=self.img0)
else:
self.label_img.configure(image=self.img1)




В функцию __init__, которая вызывается при создании экземпляра класса, передаются параметры:

Name – название датчика

Img0 и img1 – имена файлов картинок, используемых для отображения состояния датчика.

X и y – координаты окна, где будет отображаться датчик.

При создании объекта датчика сразу создается Label с картинкой и размещается в окне Tkinter.


Функция set принимает параметр строку – это новое состояние датчика «0» или «1». В зависимости от нового значения картинка внутри Label переконфигурируется, меняется на другую. В общем это и все.


Аналогичным образом реализуется второй класс vBarSensor.



class vBarSensor:
def __init__(self,name,scale,min,max,x,y,w,h):
self.name=name
self.scale=scale
self.x=x
self.y=y
self.h=h
self.val=min
self.min=min
self.max=max
self.delta=max-min
h1=self.h*(self.val-self.min)/self.delta
h0=self.h-h1
self.canv0 = Canvas(root, width = w, height = h0, bg = "lightblue", bd=1, relief='ridge')
self.canv1 = Canvas(root, width = w, height = h1, bg = "red", bd=1, relief='ridge')
self.barLabel = Label(root, text = "0")
self.canv0.place(x=self.x,y=self.y)
self.canv1.place(x=self.x,y=self.y+h0)
self.barLabel.place(x=self.x,y=self.y+h+5)
def set(self,newval):
#newval is signed hex string like "83A5"
val=int(newval,16)
if(val>0x7fff): val=-val
val=val/self.scale
if(self.val==val): return
self.val=val
h1=self.h*(self.val-self.min)/self.delta
h0=self.h-h1
self.barLabel.configure(text=str(self.val))
self.canv0.configure(height = h0)
self.canv1.configure(height = h1)
self.canv1.place(y=self.y+h0)




Этот класс графически представляет датчик типа термометра. Значения из датчика могут меняться в некотором диапазоне. Так же при создании экземпляра этого класса нужно указать имя датчика. Кроме этого у термометра есть возможное минимальное и максимальное значение, и еще указываем координаты столбика в окне визуализации, ширину и высоту столбика.

Столбик термометра как бы состоит из двух частей нижняя красная и верхняя светлая.

Можно было бы создать один Tkinter canvas и на нем нарисовать эти столбики, но почему-то я сделал не так. Сделал два canvas разного цвета и в функции set() меняю им вертикальный размер. В принципе это не важно. Работает. Кстати, если хочется видеть именно изображение термометра в окне визуализации, то его можно нарисовать на фоновом изображении окна, а поверх него разместить экземпляр vBarSensor.



Наверное будет симпатично.


Написал еще один класс GridDisplay для отображения показаний датчика и изменении их во времени. Его исходный код приводить здесь не буду, чтобы не перегружать статью излишними подробностями. Кому будет нужно скачает с сайта весь проект, вместе с исходниками для ПЛИС для Altera Quartus II.


А вот главную программу alls.py пожалуй покажу. Здесь не очень много всего:



#!/usr/bin/env python

import sensor
from sensor import *

import serial
from serial import *

class AllSensors:
def __init__(self):
#open serial port
self.s=serial.Serial("COM27",115200,timeout=10)
#load background image
self.bgnd=PhotoImage(file="bgnd.gif")
self.label_bgnd=Label(root,image=self.bgnd)
self.label_bgnd.place(x=0,y=0)
#add all sensors and indicators
self.all=[]
self.all.append( BinSensor("b0","f0.gif","f1.gif",32,32) )
self.all.append( BinSensor("b1","f0.gif","f1.gif",32,128) )
self.all.append( vBarSensor("a0",1,0,255,128,32,32,160) )
self.all.append( GridDisplay("t0",16,-55,125,10,16,180,32,256,160) )
def set(self,name,val):
for sens in self.all:
if(sens.name==name):
sens.set(val)
return
def setline(self,line):
p=line.split("=")
if(len(p)==2):
self.set( p[0], p[1] )
def run(self):
while(1):
line=self.s.readline()
line=line.rstrip()
#print(line)
self.setline(line)
root.update()

a=AllSensors()
a.run()




В этой программе открываю последовательный порт для чтения. Загружаю фоновое изображение в окно. Создаю список всех имеющихся датчиков. Далее читаю строки з порта, разбираю их и по имени датчика передаю соответствующему экземпляру классов новое значение.

Запустить программу из Python легко: «import alls», где alls — это имя главной программы файл alls.py. Вот так сейчас выглядит моя программа:



Вот видео, которое показывает, как все это работает (только не пугайтесь, я там фен включаю для нагрева датчика температуры, так что звук лучше прикрутить):



Теперь, когда «скелет» приложения работает, то можно приступать к детальному рисованию плана помещения и установленных в нем датчиков.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Комментариев нет:

Отправить комментарий