Сегодня я расскажу вам о таком прекрасном продукте как Cube Monitor от компании ST. Это приложение, которое позволяет визуализировать и изменять переменные в процессе исполнения программы на микроконтроллере. Для визуализации данных можно создавать графические приложения доступные через браузер.
Для начала предлагаю разобраться в том что это такое и как это работает, а потом перейти к практике. ST в своей презентации предлагает вот какую схему:
С левой частью схемы все понятно, подключаем STLink к своему устройству, а программатор к ПК. А вот дальше интереснее. Gateway
, это пк с подключенным STLink и на который установленно программное обеспечение от ST. Оно разворачивает на пк сервер, к которому может подключиться любое устройства с браузером и на нем станет доступен дашборд. С дашбордом все просто, его можно увидеть запустив просмотрщик из конструктора или из браузера на той же самой машине обратившись к локал хосту, или с другой введя на ней в браузере ip адрес гетвея.
На гетвее я бы остановился по подробнее. На самом деле внутри данного программного пакета находится Node-RED и компания ST по сути просто добавила туда несколько своих нод которые позволяют работать с STLink server а значит и с самим линком. К сожалению, из этого следует, что никакой другой программатор кроме STLink использовать не получится. Однако нет поводов для печали потому что компания ST далеко не единственная кто додумался добавить свои компоненты в Node-RED и для работы с любыми контроллерами с помощью Jlink можно использовать программный пакет FreeMASTER Lite (вообще он только для контроллеров NXP, но никто вам не мешает в меню выбора контроллера, просто выбрать ядро Cortex M не привязанное к конкретному контроллеру). Вероятно, я когда ни будь тоже про него напишу.
Суть Node-RED состоит в том что мы соединяем некоторые ноды между собой, которые общаются перекидыванием json объектов, по каким то событием. Событием может быть приход новых данных в какой, то интерфейс или изменения состояния какого то объекта в gui. Сами же ноды позволяют принимать или передавать данные по каким-то интерфейсам, а также принимать и передавать данные на дашборд. Также система позволяет построить этот самый дашборд. Но хватит рассуждений приступим к делу.
Для начала напишем простенькую программу под STM32 на которой будем тестировать функционал отладчика, просто мигалку светодиодом. Я возьму Nucleo-F411RE вы можете взять любую другую. Пишем следующий код (в main):
uint32_t i,x = 0;
uint32_t delay = 10;
...
while (1)
{
HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
if (++i == 100) i = 0;
x = sqrt(i);
HAL_Delay(delay);
...
При сборке выдаст 2 варнинга, это связанно с тем что мы не подключили math.h
, но умная ide и так ее подключит. Тут нужно обратить внимание не то что все переменные за которыми мы хотим следить должны быть глобальные, то есть иметь постоянные фиксированные адреса в памяти, иначе отладчик не сможет их найти. Также код обязательно нужно собирать с debag информацией, иначе мы не сможем найти адреса переменных по их названиям.
Далее прошиваем контроллер и запускаем CubeMonitor и видим следующее:
Компания ST позаботилась о нас и сделала простенький пример, который позволит строить графики переменных в реальном времени. Для его запуска необходимо настроить 2 ноды. В первой (под номером 1) необходимо выбрать наш STLink, для этого делаем двойной щелчок по ноде и открывается меню настроек. Там нам предложат добавить новый probe
, нажимаем не карандаш справа (везде в этом ПО карандаш значит создание нового элемента) и выбираем там наш программатор. После чего можно будет выбрать протокол связи и частоту работы (у STLink V3, кстати, она в 19 раз выше). После подтверждения над изменяемой нодой пропадет красный квадрат, что значит отсутствие ошибок в настройках нашей ноды.
Вообще у меня нет желания также подробно писывать каждый шаг, если по какой-то причине это вам необходимо то это можно посмотреть тут и тут, я же буду более краток. Далее необходимо настроить ноду под номером 2. Для начала нужно выбрать elf файл в меню Executable
, а переменные за которыми мы хотим следить. После чего на данной ноде тоже пропадет красный треугольник. После этого нужно еще выбрать программатор в ноде myProbe_In
(которую я к сожалению забыл пронумеровать).
После этого над всеми нодами появятся синии кружки, а значит ошибок негде нет. Нажимаем DEPLOY
(3) (это нужно делать после каждого изменения) и DASHBOARD
(4) после чего увидим приложение которое будет строить нам графики. На нем отображается все переменные за которыми мы хотели следить - i
и корень из i
.
Далее опишу несколько интересных моментов:
- При нажатии на название переменной график данной переменной перестанет отображаться, а окно авто масштабируется по высоте под оставшиеся графики. Однако оно подстроится не под видимую часть, а под весь график как бы давно он не строился.
- Если остановить чтение (нажать на
STOP ACQUISITION
) то график можно будет масштабировать. - Если вы хотите пере прошить контроллер и что-то поменять в коде, то обязательно освободить программатор (остановить чтение), а также после пере сборки измененного кода обязательно в ноде с выбором переменных обновить их адреса (для этого достаточно просто открыть ее настройки и согласится на апдейт). Это нужно потому что адреса переменных могут меняться от сборке к сборке.
- Числа с плавающей точкой тоже хорошо отображаются.
- На графике можно отобразить сохраненные данные если нажать
IMPORT DATA
. - В ноде
Processing
которая на скриншоте выше называетсяmyVariables
можно создавать дополнительные переменные которые будут вычисляться по определенной формуле от текущих. - Для доступа к дашборду из браузера используйте URL: http://localhost:1880/ui/, кстати сам редактор ноды можно открыть из браузера по адресу: http://localhost:1880/.
Поигравшись с этим вы начнете задумываться от том, что это лиш маленькая часть функционала который может дать данная программа. Но вот что делать дальше совсем не понятно, и вот собственно об этом данная статья.
Для начала было-бы не плохо разобраться с тем как в принципе работать с Node-RED. Этот можно посмотреть тут, или в официальной документации, я же изложу самое важное для понимая.
Любая нода отправляет сообщение по какому-то событию. Это может быть нажатие на кнопку на дашборде или приход пакета по tcp
. Важно то, что эти сообщения могут иметь 2 свойства (мне очень не нравится этот термин, но он используется в интерфейсе программы) payload
и topic
. Мы конечно можем создать любое их количество, но по умолчанию используются только эти. Первый из них - payload
содержит в себе полезную нагрузку сообщения, и может быть любым типом. Второй же - topic
чаще всего строка. Топик может передать сообщение с одним из этих свойств.
Для минимальной работы системы, просто отрисовки графика, нужно построить следующий поток
:
Наш поток состоит из 2х веток, верхней и нижней. Первая нужна для отправки данных на STLink server, а вторая на прием. Для того чтобы нода STLink in
начала принимать данные с контроллера, сначала нужно передать данные для работы STLink server через ноду STLink out
. Она ждет payload
содержащий настройки, которые генерируется в блоке variables
, которые мы задаем через настройку данного блока, а также topic
который может содержать строку read
, start
и stop
. Они нужны для того чтобы считать один набор значений из отладчика, включить непрерывное чтение и прекратить это чтение. Таки образом вместо кнопок мы можем использовать любой стандартный элемент который сможет записать строку в topic
(в примере ниже используются кнопки), но настройки для программатора можно создать только в ноде Variables.
Нижний же блок устроен по проще, нода STLink in
передает сырые данные (через свойство payload
) в ноду Variables
, по сути ноду процессинга, в которой по адресам считанных переменных им присваиваются имена. Далее после процессинга json с данными и значениями попадает в ноду Chart
, которая отрисовывает график на дашборде, интересные момент заключается в том что если использовать стандартный топик Chart
вместо фирменного, то приложение упадет из-за слишком большого количества данных.
В принципе нам может потребоваться еще ровно 2 функции чтобы полноценно взаимодействовать со стандартными нодами, это чтение произвольной переменной и запись. С первым все проще компания ST позаботилась о нас и сделала специальный под поток Single value
который позволяет выделить одну переменную после процессинга. Но иногда бывает такое что необходимо преобразовать тип данных, например переменная может иметь значения от 1 до 100, и мы хотим чтобы на дашборде загоралась кнопка если значение больше 80ти. Для этого нужно будет использовать ноду function
которая позволит выполнить произвольный код Js при приходе сообщению. В итоге поток будет выглядеть так:
А код функции:
if (msg.payload < 80)
return { payload: true };
else
return { payload: false };
С записью же переменных дела обстоят хуже. ST дает нам ноду (write panel
) которая позволит нам писать значения в переменные, но представляет из себя панель, в которую мы можем руками забивать значения. Выглядит это так:
А подключить никакие стандартные элементы интерфейса мы не можем. Но есть выход, мы можем с помощью ноды debug
узнать что шлет нода write panel
в STLink out
и сформировать такое-же сообщение. Выглядит это будет так:
Слайдер отправляет значения из определенного диапазона в свойство payload
, после чего нода Write
оборачивает его в шаблон который мы подсмотрели в write panel
, а нода set
пишет в свойство topic
строку wire
т.к. без этого значение не отправится в дебагер. Шаблон внутри ноды Write
должен выглядеть так:
{
"variablelist": [
{
"address": "0x20000000",
"name": "PWM",
"type": 6,
"value": "{{payload}}"
}
],
"accesspoint": 0
}
Вместо {{payload}}
будет подставлено значение со слайдера. Однако в этом методе есть проблема. Каждый раз когда адреса переменных будет меняться при пере сборке кода, придется снова их смотреть с помощью дебагера, и прописать их руками в шаблон (а так-же тип). Вот если бы это можно было автоматизировать, и у нас есть для этого средства - функции и под потоки. Создадим под поток который решит все наши проблемы, в параметрах которого мы запишем название переменной и он сам сможет определять адрес данной переменной.
Внутри же под потока создадим всего одну ноду с функцией Js. На самом деле можно было бы обойтись без отдельного под потока, только функцией, но тогда мы не смогли бы указать название переменной точно так-же как это работает в под потоке Single value
.
Самое интересное происходит внутри ноды функции:
let message_send = {
"variablelist": [
{
"address": "",
"name": "",
"type": 0,
"value": ""
}
],
"accesspoint": 0
};
if (msg.topic == "start" || msg.topic == "stop" || msg.topic == "read")
{
context.set("save_payload", msg.payload);
}
else
{
var save_payload = context.get("save_payload");
if (!save_payload){
node.error("Not data for wariable !!!");
return;
}
for (var i = 0; i < save_payload.variablelist.length; i++){
if (save_payload.variablelist[i].name == env.get("variable")) {
message_send.variablelist[0].address = save_payload.variablelist[i].address;
message_send.variablelist[0].name = env.get("variable");
message_send.variablelist[0].type = save_payload.variablelist[i].type;
message_send.variablelist[0].value = String(msg.payload);
message_send.accesspoint = save_payload.accesspoint;
return { payload: message_send, topic: "write" };
}
}
node.error("Wariable \"" + env.get("variable") + "\" not found !!!");
}
В самом начале мы создаем объект - сообщение которое будем отправлять в ноду дебагера. Далее смотрим, если сообщение которое приняла наша функция имеет строку в свойстве топик, то значит это сообщение для дебагера с командой стартовать или остановится. Мы сохраним это сообщение в локальном контексте ноды (почитать про это можно тут), что-бы потом вытаскивать из него адреса переменных по названиям.
Если поле контекста пустое это значит что пришло число которое нам нужно записать в переменную, указанную в конфиге ноды.
Данная функция имеет ряд ограничений, например нельзя писать значение до такого как включится плоттер (это свойственно и под потоку Single value
), так-же в наш под поток Mem writer
можно писать только числа. И обязательно должно быть указанно название переменной. Впрочем никто не запрещает вам его улучшать.
Пример проекта для CubeIDE и потока CubeMonitor можно найти тут.
Интересные ссылки:
- Wiki ST по куб монитору.
- Документация на
Node RED
. - Описание
Node RED
на русском.