PHP для Arduino — часть 2

WebduinoВ предыдущей статье мы остановились на месте, где Webduino уже мог обслуживать нас любыми файлами с SD-карты. Теперь нам нужно пропустить выбранные файлы через наш, так называемый, PHP и отправить результат клиенту.

Чтобы упростить весь процесс, мы предполагаем, что каждый файл, который необходимо обработать, нам известен. То есть мы регистрируем каждый такой файл (URL) с расширением addCommand.

Но как это должно работать? Идея в том, что у нас есть свои функции в коде скетча и результат работы которых нужно вставить в выбранные места в HTML-коде. Итак, мы хотим иметь файл HTML с этим фрагментом кода:

В результате работы нашего парсера мы хотим, чтобы RESULT1 и RESULT2 были заменены на результат работы функции в скетче.

Но, как сохранить в HTML то, что наш парсер должен поставить вместо него другой текст. Для простоты примем следующую формулу: #{X} она будет заменена вызовом соответствующей функции. X это мнемоника из одной буквы, для которой мы хотим вызвать функцию.

Функции должны иметь конкретное определение и не могут принимать аргументы. Почему? Принятие аргументов усложняет синтаксический анализатор и, вероятно, на данный момент в этом нет необходимости.

Мы выбрали формат HTML. Но, как наша функция будет передавать результат операции? Итак, мы предполагаем, что пример функции имеет следующее определение:

Предполагается, что функция не возвращает данные ( void ), а принимает указатель на текстовый буфер. В этот буфер предполагается поместить результат своей работы, который затем будет вставлен в соответствующее место в HTML. Сама функция должна следить за тем, чтобы не переполнить этот буфер. Его размер определяется:

P4A_MAX_FUNCTION_RESPONSE_LENGTH .

Как видите, приведенная выше функция возвращает количество полных секунд, прошедших с момента запуска или сброса Arduino.

Как разобрать файл? Благодаря простому маркеру — это относительно просто. Читаем файл посимвольно. Если мы встречаем #, то читаем следующий символ и проверяем, является ли он фигурной скобкой { которые вместе образуют открывающую последовательность наших результатов. Пока нам не попадаются # (эти неинтересные символы), мы отправляем их в буфер, который в итоге будет отправлен в браузер.

Читать также:  Модернизация цифрового штангенциркуля своими руками

Если следующий символ после # не фигурная скобка, мы отправляем в браузер и #, и следующий символ — нам в этот момент делать нечего, ждем следующего #.

Однако, если вторым знаком была фигурная скобка, то читаем следующий знак — это наша мнемоника! Вызываем соответствующую функцию в зависимости от мнемоники, отправляем результат в браузер.

Затем мы читаем файл до закрывающей скобки и сканируем файл в поисках следующего символа #.

Остается вопрос о назначении функций мнемоникам. Нам для этого послужит массив указателей на функцию.

Массив указателей на функцию

Теперь мы поговорим о более сложной теме — указателях на функции. Ну, если подумать, то скомпилированная функция — это адрес, по которому находится исполняющий ее код, и некий контракт, определяющий, как данные передаются в функцию, и как они из нее возвращаются.

Если контракт один и тот же для нескольких функций (то есть список типов аргументов и возвращаемое значение), функции можно записать в виде указателя и сохранить в массиве. Затем мы можем вызывать такую ​​функцию по сохраненному указателю, нам не нужно знать ее имя в коде.

Именно это и послужит нам механизмом перевода мнемоники в вызываемые функции. Теперь вы знаете, почему все наши функции должны иметь одинаковый интерфейс/контракт (как мы установили будет для void возвращаемых данных и char * в качестве аргумента) — благодаря этому мы можем хранить их в одном массиве, индексом которого будет буква. Перво-наперво:

Мы определяем массив указателей на функции. Пустота впереди указывает тип возвращаемого значения функции, что в круглых скобках в конце указывает, какие аргументы ожидает функция. Посередине находится объявление массива. Его размер ‘z’-‘a’ может показаться странным, но в этом контексте символы рассматриваются компилятором как байты. Итак, из кода «z» вычитаем код «a» — разница в количестве букв. Благодаря этому у нас есть массив, который может содержать столько указателей на функции, сколько строчных букв в латинице (точнее, в стандарте ASCII).

Читать также:  Основной цикл и вызов функции скетча Ардуино

Если у нас есть буквенный код, просто вычитаем из него буквы и мы получим индекс из таблицы. В любом случае, давайте посмотрим код:

c[0] содержит знак нашей мнемотехники. Если массив не имеет значения (т. е. NULL) в индексе, c[0] — ‘a’ тогда вставляется HTML n/a — наш способ сигнализировать о плохой мнемонике.

‘a’ в ASCII он имеет код 97. Если наша мнемоника тоже ‘a‘ 97-97 = 0, то это первый элемент массива. Если мнемоника ‘b’ = ‘b’-‘a’ 98 — 97 = 1, то есть второй элемент массива и т.д. Было бы полезно проверить, находится ли мнемоника в правильном диапазоне, иначе можно попробовать вызвать функцию со случайным адресом (если индекс массива вне допустимого диапазона, то ОЗУ из-за пределов массива будет прочитано и процессор попытается интерпретировать значение из этого адреса как адрес вызываемой функции — гарантированный сбой).

Приведенный выше код также показывает, как вызывать функции в массиве:

Мы используем переменную для анализа файла status , благодаря которой мы знаем состояние нашего парсера. Возможно:

  • P4A_PARSE_REGULAR — состояние, в котором ищем знак #
  • P4A_PARSE_HASH — состояние, в котором ждем открытия скобки
  • P4A_PARSE_COMMAND — состояние, в котором ищем мнемотехнику

Эти состояния упрощают управление тем, что делает наш парсер. В зависимости от текущего состояния и следующего знака вы можете решить, что делать дальше. Отправка данных в браузер или вызов функции на основе мнемоники.

Кому интересно, вот вся функция parseP4A :

P4A или PHP для Arduino в работе

Предположим, мы хотим сделать хороший виртуальный барометр, но показывающий реальное давление. Мы будем использовать BMP085 — это удобная плата для подключения датчика давления и температуры. Ожидается, что результат будет как на картинке:

Виртуальный барометр

Стрелка должна показывать значение, считанное с датчика, а символ прогноза погоды должен меняться в зависимости от значения давления.

Как быть с тем, что, как мы писали в предыдущем эпизоде, веб-сервер Arduino — не лучшее решение, когда нужно обслуживать много файлов одновременно (картинок!)? Ну, если все это в любом случае должно быть доступно из сети, почему бы не поделиться статическими ресурсами с сетевого сервера? У нас есть такой сервер для своих нужд и на нем размещены все необходимые графические элементы. То есть циферблат барометра, изображение указателя и символы погоды.

Читать также:  Следующий за рукой робот на Ардуино

На Arduino есть только файл HTML, на SD-карте. В скетче поместим функцию, которая вызывает парсер для этого файла:

parseP4A — это функция, которая анализирует данный файл и отправляет результат, используя объект сервера Webduino. Осталось зарегистрировать нашу функцию как команду по умолчанию:

Сам HTML использует JavaScript и объект canvas для работы самого Ethernet Shield. Это делается функцией draw, которая принимает в качестве аргумента давление в гектопаскалях. Когда страница готова к отображению (т.е. загружена), мы в onload вызываем функцию draw через атрибут, давление вставляется нашим P4A:

Мнемоника p должна быть связана с правильной функцией. В скетче setup мы устанавливаем функцию для p:

pressureReport имеет вид:

Серийный номер используется для проверки того, что значения соответствуют ожидаемым и не влияют на работу барометра. bmp085_read_temperature_and_pressure — функция из обработчика BMP085.

Вы можете скачать весь код с нашего ЯндексДиска. Это скетч, который управляет нашим сервером, а также файл HTML и графика. Циферблат барометра, графика и код HTML/JS.

Скачайте архив и распакуйте в sketchbook. Поместите содержимое подкаталога html в основной каталог на SD-карте, поместите его в шилд. Исправьте скетч, введя правильный MAC и IP-адрес. После открытия главной страницы мы должны увидеть барометр, если у вас используется тоже датчик BMP085.

Итог

Код является бета-версией, т.е. — работает настолько, насколько это подтверждают наши тесты, могут быть (и наверняка есть) несколько багов, о которых мы понятия не имеем…

Код должен быть отсортирован, — например, функции, связанные с буферизованным выводом, должны быть перемещены в код Webduino. Мы планируем это сделать и отправить все изменения, которые были внесены в Webduino, разработчикам Webduino — возможно, что-то из этого будет прямо в библиотеке.

С Уважением, МониторБанк

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