Людям нравятся красивые презентации. Красивые картинки, немного текста, меняющиеся слайды. Красивая картинка позволяет быстро передать информацию человеку, сообщить самое важное. Мы все это знаем. Я вот думаю, как «скрестить ежа и ужа»?
Как наглядно на мониторе компьютера представить процессы, происходящие внутри микроконтроллера или ПЛИС? Или как показать, что происходит внутри всей системы автоматики, реализованной на микроконтроллере или ПЛИС?
Вообще-то правильный ответ я знаю – нужно использовать 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. Вот так сейчас выглядит моя программа:
Вот видео, которое показывает, как все это работает (только не пугайтесь, я там фен включаю для нагрева датчика температуры, так что звук лучше прикрутить):
Теперь, когда «скелет» приложения работает, то можно приступать к детальному рисованию плана помещения и установленных в нем датчиков.
Автор: nckma