В последнее время все чаще можно видеть, как люди в своих проектах используют Arduino, поскольку там есть, например, Ethernet-шилд или Wi-Fi-шилд. А целый компьютер в этом плане обычно совершенно избыточен. В этой статье я покажу, что использовать Wi-Fi в своем проекте можно и без Arduino. Мы сделаем часы с Wi-Fi и монохромной матрицей, которые будут еще и показывать погоду в нужном городе.

 

Ключ на старт

В первую очередь перечислю то, что использовал:

  1. Отладочная плата STM32F3DISCOVERY.
  1. Экран на базе контроллера KS0108 (в моем случае это MT-12864A российского производства).
  1. Wi-Fi-модуль WizFi220.

Разрабатывать прошивку можно как минимум в двух IDE: Keil Embedded Development Tools for ARM и IAR Embedded Workbench. Я использую первую, а если тебя заинтересует вторая, то из-за специфики IAR тебе нужна будет IAR Embedded Workbench for ARM.

Само собой, не стоит забывать о бесплатных средствах разработки под ARM, например о связке Eclipse + GNU ARM Eclipse Plug-In + GCC + GDB + OpenOCD. Мануалы по настройке данной связки можно без труда найти в интернетах.

Итак, почему же именно STM? Потому что у них есть такая замечательная вещь, как Standard Peripherals Library (для STM32F303XX качать тут). Это библиотека, которая позволяет работать с периферией, не касаясь регистров. Вот пример настройки USART1.

USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);

Все очень просто и понятно, не так ли? Не надо лазить по Reference Manual’у (у STM32F303XX он занимает почти тысячу страниц). Сразу видно, что ребята из STMicroelectronics хорошо поработали.

Однако использование данной библиотеки — повод для холивара (одни говорят, что она генерирует кучу лишнего кода, другие — что она делает из программиста ленивца, и так далее), поэтому я не буду призывать тебя, читатель, использовать ее. Делай, как тебе удобнее.

Начать разработку с использованием этой библиотеки очень просто по двум причинам:

  1. В ней уже есть проекты, пригодные для открытия в IDE (для Keil он лежит в Projects/STM32F30x_StdPeriph_Templates/MDK-ARM/Project.uvproj).
  1. Начальный код для работы с периферией можно добавить в проект, заменив все файлы из Template на файлы нужного нам примера.

Что же будет делать контроллер в нашей задаче?

  1. Инициализировать и настраивать Wi-Fi и дисплей.
  1. Запрашивать погоду с OpenWeatherMap.
  1. Выводить на экран текущее время, дату и прогноз на несколько дней.
 

Начинаем. OpenWeatherMap

OpenWeatherMap был запущен в 2012 году группой энтузиастов, загоревшихся целью обеспечить свободную и доступную информацию о погоде для любой точки земного шара. С тех пор этот сайт непрерывно развивается, и о его возможностях можно судить по предоставляемому API: прогнозы на пять дней с расчетом метеоусловий на каждые три часа, прогнозы на срок до 16 дней, получение исторических данных, получение карт, например облачности.

Мы будем использовать прогноз на семь дней с частотой обновления один раз в сутки. В случае большого числа запросов API OpenWeatherMap может требовать ключа, который можно получить после регистрации. Но нам это не страшно. Сомневаюсь, что у нас будет больше сотни запросов в 24 часа.

Для получения прогноза необходимо послать GET-запрос на специально сформированный адрес, который для Москвы выглядит так:

http://api.openweathermap.org/data/2.5/forecast/daily?q=Moscow,ru&units=metric&cnt=7

Итак, мы хотим от API OpenWeatherMap получить прогноз для города Москвы q=Moscow,ru, в метрической системе мер units=metric и на семь дней cnt=7. Если же необходимо получить ответ в JSON, то нужно добавить еще один параметр: mode=json. Но для нас XML удобнее.

{"cod":"200","message":0.2284,"city":{"id":524901,"name":"Moscow","coord":{"lon":
37.615555,"lat":55.75222},"country":"RU","population":0,"sys":{"population":0}},"cnt":7,
"list":[{"dt":1407142800,
"temp":{"day":28.06,"min":23.9,"max":28.06,"night":23.9,"eve":28.06,"morn":28.06},
"pressure":1011.4,
"humidity":38,
"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],
"speed":4.61,"deg":38,"clouds":64},...]}

OpenWeatherMap вернет нам для каждого дня следующее:

  • температуру днем, ночью, вечером и утром, а еще минимальную и максимальную;
  • давление в гигапаскалях;
  • влажность воздуха;
  • текстовое описание погоды и даже имя иконки для отображения "icon":"04d";
  • скорость и направление ветра;
  • процент облачности.

Нам остается только правильно распарсить ответ сервера.

 

Продолжаем: WizFi220

WizFi220

Различают два варианта подобных модулей: WizFi220 и WizFi210. Они похожи во всем, кроме одного пункта: у первого модуля выше мощность передатчика и, как следствие, выше потребление.

Обладают они следующими свойствами:

  • Размеры 32 x 23,5 x 3 мм.
  • Поддержка только 802.11b.
  • Шифрование: WEP, WPA/WPA2-PSK, Enterprise (EAP-FAST, EAP-TLS, EAP-TTLS, PEAP).
  • Протоколы: UDP, TCP/IP (IPv4), DHCP, ARP, DNS, HTTP/HTTPS Client and Server.
  • Напряжение питания: 3,3 В.
  • Потребление:



  • Все управление происходит через UART.

Если обратиться к распиновке, то можно увидеть, что там присутствуют пины ALARM, ADC, GPIO. Для того чтобы заставить их работать, WIZnet, производитель модулей, рекомендует изменять их прошивку самостоятельно.

Включить модуль очень просто: пины 1, 18, 31 и 48 (все GND) подключаем к земле, 32, 33 и 34 (VIN_3V3, EN_1V8, VDIO) — к +3,3 В, а 40 и 42 (UART0_RX и UART0_TX) — к контроллеру или к ПК через конвертер UART <-> USB.

Чтобы этот модуль правильно инициализировать, ему надо послать данную последовательность команд (каждая команда должна заканчиваться символом возврата каретки CR, 0x0D):

AT
ATE0
AT+WD
AT+NDHCP=1
AT+WPAPSK=SSID,passphrase
AT+WA=SSID
AT+NCLOSEALL
AT+NCTCP=144.76.102.166,80
  1. AT — команда нужна для проверки правильности работы модуля. В первый раз после включения модуль должен вернуть AT\r\r\n[OK]\r\n.
  1. ATE0 отключает эхо команд. Теперь модуль больше не возвращает только что принятую команду, а присылает только ответ.
  1. AT+WD заставляет модуль отключиться от всех Wi-Fi-сетей. Нужно в том случае, если по какой-то причине приходится инициализировать WizFi220 заново без сброса.
  1. AT+NDHCP=1 включаем DHCP-клиент. Я думаю, не надо объяснять, что это.
  1. AT+WPAPSK=SSID,passphrase требует от WizFi220 посчитать PSK (Pre-Shared Key) для сети и ключа.
  1. AT+WA=SSID запускает процесс ассоциации с сетью.
  1. AT+NCLOSEALL закрывает все соединения.
  1. AT+NCTCP=144.76.102.166,80 подключает TCP-клиент к IP-адресу и порту TCP-сервера. В данном случае это адрес openweathermap.org.

Итак, мы подключились к серверу и готовы качать погоду терабайтами. Но как это сделать? А вот тут необходимо вспомнить, что наш WizFi220 служит лишь мостом между нашим контроллером и сервером. Затем надо осознать всю тленность ситуации и пойти в Википедию читать про GET-запросы. Да, все верно. Получать погоду мы будем с помощью отправки запроса серверу.

GET /data/2.5/forecast/daily?q=Moscow&units=metric&cnt=7 HTTP/1.1\n"
"Host: openweathermap.org\n"
"Connection: keep-alive\n"
"\n"

Пустая строка в конце должна быть обязательно!

Осталось совсем немного — понять, как заставить WizFi220 обработать данный запрос корректно. Для этого существуют escape-последовательности. Их всего три, но использовать мы будем наиболее простую:

<ESC>S<CID>data<ESC>E

Здесь <ESC>0x1B, S и E — сокращения от Start и End, CID — номер соединения Connection ID, а data — данные для передачи.

Если мы в качестве данных подставим описанный выше GET-запрос, в качестве CID — 0, потому что соединение у нас всего одно, с OpenWeatherMap, и пошлем последовательность в модуль, то этот запрос будет отослан серверу и в ответ вернется запрошенный прогноз вместе с HTTP-заголовком.

 

Основная часть. Экран

MT-12864J (взято с официального сайта МЭЛТ)

Экран представляет собой ЖК-панель с разрешением 128 х 64 точки и два контроллера управления К145ВГ10, произведенные ОАО «Ангстрем», аналогичные KS0108 фирмы Samsung. Почему два? Потому что данный контроллер может управлять панелью размером лишь 64 х 64 точки. ОЗУ контроллера подразделяется на страницы, колонки и строки. Страница — область памяти размерностью 128 х 8 бит. Экраны могут требовать как +5 В, так и +3 В, и это явно указано в маркировке. В моем случае напряжение питания +5 В.

Разъем для подключения экрана содержит в себе 20 пинов, описание представлено в списке по следующей маске: <номер> — <название из даташита> — <описание из даташита> — <куда подключается>.

  • 1 — Ucc — питание — к 5V на Discovery.
  • 2 — GND — земля — к GND на Discovery.
  • 3 — Uo — вход питания ЖК-панели для управления контрастностью — к подстроечному резистору.
  • 4..11 — DB0..DB7 — шина данных — к PD0..PD7 на Discovery.
  • 12, 13 — E1, E2 — выбор контроллера — к PD8,PD9 на Discovery.
  • 14 — RES — сброс — к PD10 на Discovery.
  • 15 — R/W — выбор: чтение/запись — к PD11 на Discovery.
  • 16 — A0 — выбор: команда/данные — к PD12 на Discovery.
  • 17 — E — стробирование данных — к к PD13 на Discovery.
  • 18 — Uee — выход DC – DC преобразователя — к подстроечному резистору.

Управление контрастностью (взято из даташита)

Кстати, будь очень внимателен при поиске документации на подобные экраны: например, у MT-12864J и MT-12864A изменены распиновки разъемов: если у первого 1 — Ucc, а 2 — GND, то у второго наоборот!

В плане программирования данного контроллера нет ничего сложного: МЭЛТ предоставляет для своих экранов примеры. Например, вот процедура ожидания готовности экрана, предлагаемая производителем.

void WaitReady(bit l, bit r) {


    Delay(>140ns);      
    LCD.E=1;        
    Delay(>450ns);  



}

А вот та же самая процедура, но уже написанная мной.

#define LCD_SET_RESET_LINE()      GPIO_WriteBit(GPIOD, GPIO_Pin_10, Bit_RESET)
#define LCD_RESET_RESET_LINE()    GPIO_WriteBit(GPIOD, GPIO_Pin_10, Bit_SET)
#define LCD_READ_DATA()           GPIO_WriteBit(GPIOD, GPIO_Pin_11, Bit_SET)
#define LCD_WRITE_DATA()          GPIO_WriteBit(GPIOD, GPIO_Pin_11, Bit_RESET)
#define LCD_SEND_DATA()           GPIO_WriteBit(GPIOD, GPIO_Pin_12, Bit_SET)
#define LCD_SEND_COMMAND()        GPIO_WriteBit(GPIOD, GPIO_Pin_12, Bit_RESET)
#define LCD_SET_STROBE_LINE()     GPIO_WriteBit(GPIOD, GPIO_Pin_13, Bit_SET)
#define LCD_RESET_STROBE_LINE()   GPIO_WriteBit(GPIOD, GPIO_Pin_13, Bit_RESET)
#define LCD_CHOOSE_CRYSTAL_0()      GPIO_WriteBit(GPIOD, GPIO_Pin_8, Bit_SET); GPIO_WriteBit(GPIOD, GPIO_Pin_9, Bit_RESET)
#define LCD_CHOOSE_CRYSTAL_1()      GPIO_WriteBit(GPIOD, GPIO_Pin_8, Bit_RESET); GPIO_WriteBit(GPIOD, GPIO_Pin_9, Bit_SET)
void waitForLCDReady(uint8_t crystalId) {

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_5 | GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    GPIO_Init(GPIOD, &GPIO_InitStructure);
    LCD_READ_DATA();
    LCD_SEND_COMMAND();
    if (crystalId == 0) {
         LCD_CHOOSE_CRYSTAL_0();
    } else {
         LCD_CHOOSE_CRYSTAL_1();
    }
    Delay(1);
    LCD_SET_STROBE_LINE();
    Delay(1);

    while (GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_7) == Bit_SET) {
        ;
    }

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_5 | GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_Init(GPIOD, &GPIO_InitStructure);
    LCD_RESET_STROBE_LINE();
}

Еще один важный момент: все символы, выводимые на экран, необходимо рисовать самостоятельно. Для этого я воспользовался замечательной программой от Петра Высочанского KS0108_4_0_1 (последняя версия от января 2010 года). Скачать ее можно вот тут.

Готовим символы

Ты можешь легко увидеть в правой части экрана под изображением редактируемого символа его шестнадцатеричный код, который можно скопировать и вставить в код.

Коды почти всех символов и всех пиктограмм находятся в файле symbols.h.

const uint8_t asciiTable[128][8] = {
    ...
    {0x00,0x00,0x7E,0x4A,0x4A,0x34,0x00,0x00}, //66, B
    {0x00,0x00,0x3C,0x42,0x42,0x24,0x00,0x00}, //67, C
    {0x00,0x00,0x7E,0x42,0x42,0x3C,0x00,0x00}, //68, D
    {0x00,0x00,0x7E,0x4A,0x4A,0x42,0x00,0x00}, //69, E
    {0x00,0x00,0x7E,0x0A,0x0A,0x02,0x00,0x00}, //70, F
    ...

В своей программе я объявил массив uint8_t displayArray[8][128] = {0x00};. Сначала все, что должно появиться на экране, пишется в этот массив, а лишь затем обновляется изображение.

 

Подъем! Алгоритм работы

Теперь собственно алгоритм работы:

  1. Настроить тактирование портов ввода-вывода, USART’ов и RTC — enableClocks();.
  1. Настроить режимы работы портов ввода-вывода — setUpGPIO();.
  1. Настроить USART’ы — setUpUsart();.
  1. Настроить часы реального времени — setUpRTC();.
  1. Настроить экран — initLCD();.
  1. Проинициализировать WizFi220 — initWizFi220(...);.
  1. Запросить погоду в первый раз — callWeather(...);.
  1. Распарсить ее и вывести на экран — parseAndSetDateTime(...); и parseAndSetWeather(...);.
  1. Каждую минуту обновлять показания часов на экране.
  1. Каждые 30 минут запрашивать новую погоду.

Результат

 

Заключение

Возможные варианты расширения проекта:

  1. Использовать цветной экран и выводить на него, например, графики погоды. Как ты помнишь, мы не использовали трехчасовые прогнозы.
  1. Сделать полноценную метеостанцию и посылать свои измерения в OpenWeatherMap.
  1. Разработать печатную плату и корпус (без него устройство выглядит страшно).

Общий вид устройства

Как ты теперь понял, использование Wi-Fi в своих проектах — задача совершенно не сложная. Единственное ограничение — довольно высокая стоимость модуля (в Москве, например, около 3000 рублей). Также я надеюсь, что вдохновил тебя на создание чего-нибудь своего, причем необязательно с Wi-Fi :).

Если возникли какие-нибудь вопросы, пиши мне на email, который можно найти в начале статьи.


 

DVD

Весь код ты можешь найти на диске. Просто скопируй с заменой файлы в папку Template из Standard Peripheral Library.


 

WWW


 

WARNING!

Не забывай заземляться! Помни, что разряд статического электричества может убить и модуль Wi-Fi, и экран, и контроллер!


1 комментарий

  1. 17.02.2015 at 16:15

    Просто огонь. Спасибо огромное. Побольше бы именно таких статей.

Оставить мнение

Check Also

Выносим всё! Какие данные о нас хранит Google и как их вернуть себе через Takeout

Как известно, Google хранит огромное количество данных о своих пользователях, чем его непр…