Генерация кода для ESP 8266 (Датчик звука в Telegram-боте)¶
В этом демонстрационном примере рассматривается разработка модели
Engee для обработки сигнала от датчика звука с последующей генерацией кода из модели для микроконтроллера ESP 8266, управляемого через Telegram-бота.
Цель этого примера - реализовать в Engee моделирование с последующей генерацией кода простого алгоритма, применимого в домашней автоматизации и "интернете вещей". Целевое устройство - микроконтроллер со встроенным WiFi-модулем будет управляться через Telegram-бота, что также обеспечивается моделью Engee. Telegram-бот будет получать от контроллера уровень звука в месте расположения датчика, усредненный методом скользящего среднего.
Целевым устройством, которое используется в этом примере, является отладочная плата NodeMCU v1.0 с WiFi-модулем ESP12E на микроконтроллере ESP8266. Аналоговый сигнал на вход устройства поступает от датчика звука, реализованного на микросхеме LM393. Питание датчик получает от микроконтроллера. Ниже представлена схема соединения устройств данного примера.
Микроконтроллер программируется через порт USB, программа загружается во Flash-память. Для дальнейшей эксплуатации контроллер с датчиком получают питание от блока питания.
Компиляция пользовательского скетча и программирование контроллера осуществляется через Arduino IDE 2.3.2. Для работы с NodeMCU v1.0 (ESP-12E) в Arduino IDE также установлена библиотека плат на базе чипа ESP 8266 от ESP8266 Community.
В качестве пользовательского интерфейса для управления контроллером был создан Telegram-бот "ESP_test" (@EngeeESPTestBot). Для дальнейшей работы с ботом понадобится его токен для доступа к HTTP API (формируется при создании бота) и пользовательский Chat ID.
В используемом боте сконфигурированы две команды: /hi
- для вывода текстового сообщения, /sound
- для вывода усредненного уровня звука.
Процессы по созданию Telegram-бота, а также получению Chat ID пользователя тривиальны, общедоступны и в данном примере не рассматриваются.
Следует заметить, что в пользовательском коде обработки входящих сообщений Telegram-бота для этого примера используется идентификатор только для одного заданного пользователя. Для доступа к обработке команд от любого пользователя следует изменить функцию обработки входящих сообщений бота.
Управляющий алгоритм для контроллера воспроизводится в модели Engee. Её структура приводится на рисунке ниже.
Здесь блоки MovingAverage
и AverageDiv
производят расчёт скользящего среднего по 10 значениям сигнала. Взаимодействие с периферией контроллера осуществляется блоками C Function
:
AnalogInput
- получает сигнал уровня звука от аналогового входа;
ToSerial
- инициализирует последовательный порт и передаёт в него значение усредненного уровня звука;
TelegramBot
- инициализирует Telegram-бота, проверяет наличие входящих сообщений;
WiFiConnect
- устанавливает WiFi-соединение.
На вход блока TelegramBot
передаётся идентификатор пользовательского чата MyChatID
, а на вход WiFiConnect
- время ожидания подключения ResponseTime
, которое следует увеличивать при слабом сигнале WiFi.
Для работы контроллера с Telegram-ботом можно воспользоваться любой доступной библиотекой. В этом примере используется библиотека C++ FastBot.
Для подключения к WiFi-сети в блоке WiFiConnect
используются макросы WIFI_SSID
, WIFI_PASS
- значения SSID и пароля WiFi-сети, определяемые далее в пользовательском скетче.
Подробные описания алгоритмов работы кода в блоках C Function
даны в комментариях блоков.
Результаты моделирования¶
Для моделирования алгоритма скользящего среднего на выходе блока AnalogInput
также формируется случайное выходное значение в диапазоне от 8 до 10. Проверим работу алгоритма, загрузив и выполнив собранную модель.
Построим графики аналогового сигнала (сгенерированных случайных значений) и результата расчёта скользящего среднего.
Алгоритм производит корректный расчёт, далее перейдём к выполнению кода на целевом устройстве.
Загрузка кода в ESP 8266¶
Для загрузки на ESP 8266 предварительно необходимо сгенерировать код из разработанной модели.
[ Info: Generated code and artifacts: /user/start/examples/codegen/esp8266_tg_sound_detect/esp8266_tg_sound_detect_code
Сгенерированные в указанной директории файлы подключим в пользовательском скетче esp8266_tg_sound_detect.ino
. Также в скетче определяются макросы, переменные и функция обработки входящих сообщений, подключаются библиотеки и инициализируется Telegram-бот. Скачаем эти файлы и загрузим в ESP8266 при помощи Arduino IDE.
Выполнение кода на ESP 8266¶
После успешной компиляции пользовательского скетча и загрузки кода в контроллер в мониторе порта Arduino IDE можно наблюдать следующий вывод:
При отправке сообщений, содержащих заданные команды Telegram-боту, можно наблюдать следующие ответ бота:
Как видно из результатов работы программы, на целевом устройстве воспроизводится заданный в модели Engee алгоритм.
В этом демонстрационном примере был рассмотрен процесс создания модели Engee, которая поддерживает работу с периферий контроллера - аналоговым входом, последовательным портом и модулем WiFi. Также модель реализует: обработку снимаемого с датчика звука аналогового сигнала, конфигурацию и мониторинг Telegram-бота.
Блоки, использованные в примере¶
{"id": "52b7eb56-1926-4fb7-83fc-1bbce68a155c", "data": [{"showlegend": true, "mode": "lines", "xaxis": "x", "colorbar": {"title": {"text": ""}}, "name": "Сигнал от датчика", "zmin": null, "yaxis": "y", "legendgroup": "Сигнал от датчика", "zmax": null, "line": {"color": "rgba(0, 154, 250, 1.000)", "shape": "linear", "dash": "solid", "width": 2}, "y": [10, 10, 8, 8, 10, 10, 8, 8, 8, 8, 8, 8, 9, 9, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 8, 8, 9, 9, 8, 8, 8, 8, 8, 8, 10, 10, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 9, 9, 10, 10, 8, 8, 8, 8, 8, 8, 8, 8, 10, 10, 8, 8, 9, 9, 10, 10, 9, 9, 8, 8, 10, 10, 8, 8, 8, 8, 9, 9, 10, 10, 8, 8, 8, 8, 10, 10, 10, 10, 9, 9, 9, 9, 8, 8, 10, 10, 9, 9, 8, 8, 9, 9, 8, 8, 9, 9, 9, 9, 8, 8, 9, 9, 8, 8, 8, 8, 10, 10, 8, 8, 9, 9, 8, 8, 9, 9, 9, 9, 9, 9, 8, 8, 10, 10, 10, 10, 9, 9, 8, 8, 9, 9, 10, 10, 9, 9, 10, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 9, 9, 8, 8, 10, 10, 9, 9, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 9, 9, 8, 8, 9, 9, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 9, 9, 10, 10, 9, 9, 9, 9, 8, 8, 8, 8, 10, 10, 9, 9, 10, 10, 10, 10, 8, 8, 9, 9, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 8, 8, 8, 8, 10, 10, 10, 10, 8, 8, 9, 9, 8, 8, 9, 9, 10, 10, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 8, 8, 10, 10, 9, 9, 8, 8, 10, 10, 9, 9, 10, 10, 10, 10, 8, 8, 10, 10, 10, 10, 10, 10, 8, 8, 9, 9, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 8, 8, 9, 9, 9, 9, 10, 10, 8, 8, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 8, 8, 9, 9, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 10, 10, 8, 8, 9, 9, 10, 10, 9, 9, 8, 8, 8, 8, 8, 8, 9, 9, 8, 8, 8, 8, 10], "type": "scatter", "x": [0, 0.01, 0.01, 0.02, 0.02, 0.03, 0.03, 0.04, 0.04, 0.05, 0.05, 0.06, 0.06, 0.07, 0.07, 0.08, 0.08, 0.09, 0.09, 0.1, 0.1, 0.11, 0.11, 0.12, 0.12, 0.13, 0.13, 0.14, 0.14, 0.15, 0.15, 0.16, 0.16, 0.17, 0.17, 0.18, 0.18, 0.19, 0.19, 0.2, 0.2, 0.21, 0.21, 0.22, 0.22, 0.23, 0.23, 0.24, 0.24, 0.25, 0.25, 0.26, 0.26, 0.27, 0.27, 0.28, 0.28, 0.29, 0.29, 0.3, 0.3, 0.31, 0.31, 0.32, 0.32, 0.33, 0.33, 0.34, 0.34, 0.35, 0.35, 0.36, 0.36, 0.37, 0.37, 0.38, 0.38, 0.39, 0.39, 0.4, 0.4, 0.41, 0.41, 0.42, 0.42, 0.43, 0.43, 0.44, 0.44, 0.45, 0.45, 0.46, 0.46, 0.47, 0.47, 0.48, 0.48, 0.49, 0.49, 0.5, 0.5, 0.51, 0.51, 0.52, 0.52, 0.53, 0.53, 0.54, 0.54, 0.55, 0.55, 0.56, 0.56, 0.57, 0.57, 0.58, 0.58, 0.59, 0.59, 0.6, 0.6, 0.61, 0.61, 0.62, 0.62, 0.63, 0.63, 0.64, 0.64, 0.65, 0.65, 0.66, 0.66, 0.67, 0.67, 0.68, 0.68, 0.69, 0.69, 0.7, 0.7, 0.71, 0.71, 0.72, 0.72, 0.73, 0.73, 0.74, 0.74, 0.75, 0.75, 0.76, 0.76, 0.77, 0.77, 0.78, 0.78, 0.79, 0.79, 0.8, 0.8, 0.81, 0.81, 0.82, 0.82, 0.83, 0.83, 0.84, 0.84, 0.85, 0.85, 0.86, 0.86, 0.87, 0.87, 0.88, 0.88, 0.89, 0.89, 0.9, 0.9, 0.91, 0.91, 0.92, 0.92, 0.93, 0.93, 0.94, 0.94, 0.95, 0.95, 0.96, 0.96, 0.97, 0.97, 0.98, 0.98, 0.99, 0.99, 1, 1, 1.01, 1.01, 1.02, 1.02, 1.03, 1.03, 1.04, 1.04, 1.05, 1.05, 1.06, 1.06, 1.07, 1.07, 1.08, 1.08, 1.09, 1.09, 1.1, 1.1, 1.11, 1.11, 1.12, 1.12, 1.13, 1.13, 1.14, 1.14, 1.15, 1.15, 1.16, 1.16, 1.17, 1.17, 1.18, 1.18, 1.19, 1.19, 1.2, 1.2, 1.21, 1.21, 1.22, 1.22, 1.23, 1.23, 1.24, 1.24, 1.25, 1.25, 1.26, 1.26, 1.27, 1.27, 1.28, 1.28, 1.29, 1.29, 1.3, 1.3, 1.31, 1.31, 1.32, 1.32, 1.33, 1.33, 1.34, 1.34, 1.35, 1.35, 1.36, 1.36, 1.37, 1.37, 1.38, 1.38, 1.39, 1.39, 1.4, 1.4, 1.41, 1.41, 1.42, 1.42, 1.43, 1.43, 1.44, 1.44, 1.45, 1.45, 1.46, 1.46, 1.47, 1.47, 1.48, 1.48, 1.49, 1.49, 1.5, 1.5, 1.51, 1.51, 1.52, 1.52, 1.53, 1.53, 1.54, 1.54, 1.55, 1.55, 1.56, 1.56, 1.57, 1.57, 1.58, 1.58, 1.59, 1.59, 1.6, 1.6, 1.61, 1.61, 1.62, 1.62, 1.63, 1.63, 1.64, 1.64, 1.65, 1.65, 1.66, 1.66, 1.67, 1.67, 1.68, 1.68, 1.69, 1.69, 1.7, 1.7, 1.71, 1.71, 1.72, 1.72, 1.73, 1.73, 1.74, 1.74, 1.75, 1.75, 1.76, 1.76, 1.77, 1.77, 1.78, 1.78, 1.79, 1.79, 1.8, 1.8, 1.81, 1.81, 1.82, 1.82, 1.83, 1.83, 1.84, 1.84, 1.85, 1.85, 1.86, 1.86, 1.87, 1.87, 1.88, 1.88, 1.89, 1.89, 1.9, 1.9, 1.91, 1.91, 1.92, 1.92, 1.93, 1.93, 1.94, 1.94, 1.95, 1.95, 1.96, 1.96, 1.97, 1.97, 1.98, 1.98, 1.99, 1.99, 2, 2], "zaxis": null, "z": null, "metadata": {"shouldEnableSmartZoom": false, "smartZoomParams": {"minCount": 25000, "maxCount": 401, "currentCount": 401}}}, {"showlegend": true, "mode": "lines", "xaxis": "x", "colorbar": {"title": {"text": ""}}, "name": "Усредненный сигнал", "zmin": null, "yaxis": "y", "legendgroup": "Усредненный сигнал", "zmax": null, "line": {"color": "rgba(227, 111, 71, 1.000)", "shape": "linear", "dash": "solid", "width": 2}, "y": [0, 0, 0.8, 0.8, 1.7, 1.7, 2.5, 2.5, 3.4, 3.4, 4.4, 4.4, 5.2, 5.2, 6, 6, 6.8, 6.8, 7.7, 7.7, 8.5, 8.5, 8.6, 8.6, 8.6, 8.6, 8.8, 8.8, 8.9, 8.9, 8.9, 8.9, 9, 9, 9.1, 9.1, 9.2, 9.2, 9.2, 9.2, 9.4, 9.4, 9.4, 9.4, 9.5, 9.5, 9.5, 9.5, 9.3, 9.3, 9.2, 9.2, 9.1, 9.1, 9.2, 9.2, 9.1, 9.1, 9.2, 9.2, 9.2, 9.2, 9.1, 9.1, 8.9, 8.9, 8.8, 8.8, 9, 9, 9.1, 9.1, 9.1, 9.1, 9, 9, 9, 9, 9, 9, 8.9, 8.9, 8.9, 8.9, 9, 9, 9.1, 9.1, 9.1, 9.1, 9, 9, 9.2, 9.2, 9.1, 9.1, 9.1, 9.1, 9.1, 9.1, 9, 9, 9.1, 9.1, 9.1, 9.1, 9.1, 9.1, 9.1, 9.1, 9.1, 9.1, 8.9, 8.9, 9.1, 9.1, 9.1, 9.1, 9, 9, 9.1, 9.1, 9.2, 9.2, 9.3, 9.3, 9.2, 9.2, 9.1, 9.1, 9.2, 9.2, 9.3, 9.3, 9.1, 9.1, 9.2, 9.2, 9.1, 9.1, 9.2, 9.2, 9.1, 9.1, 9, 9, 9.1, 9.1, 9, 9, 8.9, 8.9, 9, 9, 9.1, 9.1, 9.2, 9.2, 9.2, 9.2, 9.1, 9.1, 9, 9, 9.1, 9.1, 9, 9, 9, 9, 9.1, 9.1, 9.1, 9.1, 9.1, 9.1, 9.1, 9.1, 9.3, 9.3, 9.4, 9.4, 9.6, 9.6, 9.6, 9.6, 9.6, 9.6, 9.7, 9.7, 9.6, 9.6, 9.4, 9.4, 9.3, 9.3, 9.1, 9.1, 9, 9, 8.9, 8.9, 8.9, 8.9, 8.9, 8.9, 9, 9, 9, 9, 9, 9, 9, 9, 9.1, 9.1, 9.3, 9.3, 9.4, 9.4, 9.5, 9.5, 9.5, 9.5, 9.4, 9.4, 9.4, 9.4, 9.4, 9.4, 9.5, 9.5, 9.5, 9.5, 9.6, 9.6, 9.4, 9.4, 9.2, 9.2, 9, 9, 8.9, 8.9, 8.9, 8.9, 8.8, 8.8, 8.7, 8.7, 8.6, 8.6, 8.8, 8.8, 8.8, 8.8, 8.8, 8.8, 8.8, 8.8, 8.9, 8.9, 8.9, 8.9, 9, 9, 8.9, 8.9, 9.1, 9.1, 9.2, 9.2, 9.2, 9.2, 9.2, 9.2, 9.2, 9.2, 9.4, 9.4, 9.4, 9.4, 9.3, 9.3, 9.2, 9.2, 9.4, 9.4, 9.2, 9.2, 9.1, 9.1, 8.9, 8.9, 8.7, 8.7, 8.9, 8.9, 8.7, 8.7, 8.8, 8.8, 9, 9, 8.9, 8.9, 8.7, 8.7, 8.9, 8.9, 8.8, 8.8, 9, 9, 9, 9, 8.8, 8.8, 8.8, 8.8, 8.7, 8.7, 8.6, 8.6, 8.7, 8.7, 8.9, 8.9, 8.8, 8.8, 8.8, 8.8, 8.6, 8.6, 8.8, 8.8, 8.9, 8.9, 9.1, 9.1, 9.1, 9.1, 9.2, 9.2, 9.1, 9.1, 9, 9, 9.1, 9.1, 9.3, 9.3, 9.3, 9.3, 9.2, 9.2, 9.3, 9.3, 9.1, 9.1, 9.2, 9.2, 9.1, 9.1, 9.3, 9.3, 9.3, 9.3, 9.3, 9.3, 9.2, 9.2, 9.4, 9.4, 9.4, 9.4, 9.4, 9.4, 9.6, 9.6, 9.5, 9.5, 9.6, 9.6, 9.6, 9.6, 9.6, 9.6, 9.4, 9.4, 9.3, 9.3, 9.3, 9.3, 9.2, 9.2, 9.1, 9.1, 9, 9, 8.9], "type": "scatter", "x": [0, 0.01, 0.01, 0.02, 0.02, 0.03, 0.03, 0.04, 0.04, 0.05, 0.05, 0.06, 0.06, 0.07, 0.07, 0.08, 0.08, 0.09, 0.09, 0.1, 0.1, 0.11, 0.11, 0.12, 0.12, 0.13, 0.13, 0.14, 0.14, 0.15, 0.15, 0.16, 0.16, 0.17, 0.17, 0.18, 0.18, 0.19, 0.19, 0.2, 0.2, 0.21, 0.21, 0.22, 0.22, 0.23, 0.23, 0.24, 0.24, 0.25, 0.25, 0.26, 0.26, 0.27, 0.27, 0.28, 0.28, 0.29, 0.29, 0.3, 0.3, 0.31, 0.31, 0.32, 0.32, 0.33, 0.33, 0.34, 0.34, 0.35, 0.35, 0.36, 0.36, 0.37, 0.37, 0.38, 0.38, 0.39, 0.39, 0.4, 0.4, 0.41, 0.41, 0.42, 0.42, 0.43, 0.43, 0.44, 0.44, 0.45, 0.45, 0.46, 0.46, 0.47, 0.47, 0.48, 0.48, 0.49, 0.49, 0.5, 0.5, 0.51, 0.51, 0.52, 0.52, 0.53, 0.53, 0.54, 0.54, 0.55, 0.55, 0.56, 0.56, 0.57, 0.57, 0.58, 0.58, 0.59, 0.59, 0.6, 0.6, 0.61, 0.61, 0.62, 0.62, 0.63, 0.63, 0.64, 0.64, 0.65, 0.65, 0.66, 0.66, 0.67, 0.67, 0.68, 0.68, 0.69, 0.69, 0.7, 0.7, 0.71, 0.71, 0.72, 0.72, 0.73, 0.73, 0.74, 0.74, 0.75, 0.75, 0.76, 0.76, 0.77, 0.77, 0.78, 0.78, 0.79, 0.79, 0.8, 0.8, 0.81, 0.81, 0.82, 0.82, 0.83, 0.83, 0.84, 0.84, 0.85, 0.85, 0.86, 0.86, 0.87, 0.87, 0.88, 0.88, 0.89, 0.89, 0.9, 0.9, 0.91, 0.91, 0.92, 0.92, 0.93, 0.93, 0.94, 0.94, 0.95, 0.95, 0.96, 0.96, 0.97, 0.97, 0.98, 0.98, 0.99, 0.99, 1, 1, 1.01, 1.01, 1.02, 1.02, 1.03, 1.03, 1.04, 1.04, 1.05, 1.05, 1.06, 1.06, 1.07, 1.07, 1.08, 1.08, 1.09, 1.09, 1.1, 1.1, 1.11, 1.11, 1.12, 1.12, 1.13, 1.13, 1.14, 1.14, 1.15, 1.15, 1.16, 1.16, 1.17, 1.17, 1.18, 1.18, 1.19, 1.19, 1.2, 1.2, 1.21, 1.21, 1.22, 1.22, 1.23, 1.23, 1.24, 1.24, 1.25, 1.25, 1.26, 1.26, 1.27, 1.27, 1.28, 1.28, 1.29, 1.29, 1.3, 1.3, 1.31, 1.31, 1.32, 1.32, 1.33, 1.33, 1.34, 1.34, 1.35, 1.35, 1.36, 1.36, 1.37, 1.37, 1.38, 1.38, 1.39, 1.39, 1.4, 1.4, 1.41, 1.41, 1.42, 1.42, 1.43, 1.43, 1.44, 1.44, 1.45, 1.45, 1.46, 1.46, 1.47, 1.47, 1.48, 1.48, 1.49, 1.49, 1.5, 1.5, 1.51, 1.51, 1.52, 1.52, 1.53, 1.53, 1.54, 1.54, 1.55, 1.55, 1.56, 1.56, 1.57, 1.57, 1.58, 1.58, 1.59, 1.59, 1.6, 1.6, 1.61, 1.61, 1.62, 1.62, 1.63, 1.63, 1.64, 1.64, 1.65, 1.65, 1.66, 1.66, 1.67, 1.67, 1.68, 1.68, 1.69, 1.69, 1.7, 1.7, 1.71, 1.71, 1.72, 1.72, 1.73, 1.73, 1.74, 1.74, 1.75, 1.75, 1.76, 1.76, 1.77, 1.77, 1.78, 1.78, 1.79, 1.79, 1.8, 1.8, 1.81, 1.81, 1.82, 1.82, 1.83, 1.83, 1.84, 1.84, 1.85, 1.85, 1.86, 1.86, 1.87, 1.87, 1.88, 1.88, 1.89, 1.89, 1.9, 1.9, 1.91, 1.91, 1.92, 1.92, 1.93, 1.93, 1.94, 1.94, 1.95, 1.95, 1.96, 1.96, 1.97, 1.97, 1.98, 1.98, 1.99, 1.99, 2, 2], "zaxis": null, "z": null, "metadata": {"shouldEnableSmartZoom": false, "smartZoomParams": {"minCount": 25000, "maxCount": 401, "currentCount": 401}}}], "config": {"showlegend": true, "xaxis": {"showticklabels": true, "gridwidth": 0.5, "range": [-0.06000000000000005, 2.06], "domain": [0.043880626032857, 0.9956255468066493], "mirror": false, "tickangle": 0, "showline": true, "zeroline": false, "tickfont": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 11}, "zerolinecolor": "rgba(0, 0, 0, 1)", "anchor": "y", "visible": true, "ticks": "inside", "tickmode": "array", "linecolor": "rgba(0, 0, 0, 1)", "showgrid": true, "title": {"text": "", "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 15}}, "gridcolor": "rgba(0, 0, 0, 0.1)", "tickcolor": "rgb(0, 0, 0)", "type": "linear"}, "paper_bgcolor": "rgba(255, 255, 255, 1.000)", "annotations": [], "height": 300, "margin": {"l": 0, "b": 20, "r": 0, "t": 20}, "plot_bgcolor": "rgba(255, 255, 255, 1.000)", "yaxis": {"showticklabels": true, "gridwidth": 0.5, "range": [-0.3000000000000007, 10.3], "domain": [0.050160396617089535, 0.9868766404199475], "mirror": false, "tickangle": 0, "showline": true, "zeroline": false, "tickfont": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 11}, "zerolinecolor": "rgba(0, 0, 0, 1)", "anchor": "x", "visible": true, "ticks": "inside", "tickmode": "array", "linecolor": "rgba(0, 0, 0, 1)", "showgrid": true, "title": {"text": "", "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 15}}, "gridcolor": "rgba(0, 0, 0, 0.1)", "tickcolor": "rgb(0, 0, 0)", "type": "linear"}, "legend": {"yanchor": "bottom", "xanchor": "right", "bordercolor": "rgba(0, 0, 0, 1)", "bgcolor": "rgba(255, 255, 255, 1.000)", "borderwidth": 1, "tracegroupgap": 0, "y": 0.07, "font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 11}, "title": {"font": {"color": "rgba(0, 0, 0, 1)", "family": "sans-serif", "size": 15}, "text": ""}, "traceorder": "normal", "x": 1}, "width": 667}}