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

от автора

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

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

Вообще-то правильный ответ я знаю – нужно использовать 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. Вот так сейчас выглядит моя программа:

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

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

ссылка на оригинал статьи http://habrahabr.ru/post/196972/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *