ATmega16 (семейство AVR) является дешевым 8 битным микроконтроллером и имеет достаточно большое число интерфейсов ввода-вывода общего назначения. Он поддерживает все часто используемые в настоящее время протоколы связи такие как UART, USART, SPI и I2C. Он достаточно широко применяется в робототехнике, автомобилестроении и автоматизированных производствах из-за своей простоты и универсальности.
Но, к сожалению, ATmega16 не поддерживает никакие беспроводные протоколы связи, такие как, например, Wi-Fi и Bluetooth. Это обстоятельство существенно ограничивает его применение в такой сфере как интернет вещей (IoT), которая считается очень перспективной в современных реалиях. Чтобы преодолеть это ограничение к микроконтроллеру ATmega16 необходимо подключить устройство, которое поддерживало бы беспроводные протоколы связи. Таких устройств сейчас существует много, но одним из наиболее широко используемых является ESP8266.
В этой статье мы рассмотрим подключение микроконтроллера AVR ATmega16 к устройству управления многосторонней связью (NodeMCU) ESP8266 чтобы при помощи беспроводной связи, обеспечиваемой данным устройством, получить доступ в интернет. ESP8266 представляет собой WiFi модуль с поддержкой различных протоколов связи и множеством доступных библиотек.
Мы рассмотрим передачу email (электронной почты) с использованием ESP8266 и микроконтроллера ATmega16. ATmega16 будет давать команды ESP8266, которое будет пересылать email выбранному корреспонденту. ATmega16 и ESP8266 будут взаимодействовать через универсальный асинхронный приемопередатчик (UART), хотя можно использовать и другие доступные ATmega16 протоколы связи, такие как SPI и I2C.
Некоторые особенности поставленной задачи
Необходимо помнить о том, что микроконтроллер ATmega16 работает от напряжения 5В, а микросхема ESP8266 – от напряжения 3.3В. То есть логические уровни между этими двумя устройствами отличаются, что может вызвать "недопонимание" между ними и даже потерю данных если необходимые логические уровни не будут поддерживаться.
Но, тем не менее, если внимательно изучить технические характеристики (datasheet) микросхемы ESP8266, можно обнаружить что она терпимо (толерантно) относится к напряжению до 6В на своих контактах, поэтому ESP8266 сможет "понимать" логический уровень напряжения 5В. Также в даташите на ATmega16 указано, что уровень напряжения больший 2В он воспринимает как логическую единицу и, поскольку, ESP8266 оперирует напряжением 3.3В, то сигнал такого уровня будет восприниматься микроконтроллером как логическая "1". Поэтому взаимосвязь между этими устройствами можно организовать без переключения логических уровней напряжения.
Необходимые компоненты
- Микроконтроллер ATmega16 (купить на AliExpress).
- Программатор AVR-ISP (купить на AliExpress), USBASP (купить на AliExpress) или другой подобный.
- Устройство управления многосторонней связью (NodeMCU) ESP8266 (купить на AliExpress).
- Кварцевый генератор на 16 МГц (купить на AliExpress).
- Конденсатор 100 нФ (2 шт.) (купить на AliExpress).
- Конденсатор 22 пФ (2 шт.) (купить на AliExpress).
- Светодиод (купить на AliExpress).
- Кнопка.
- Макетная плата.
- Соединительные провода.
- Источник питания с напряжением 5 Вольт.
Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158
Схема подключения ESP8266 к микроконтроллеру ATmega16
Схема устройства приведена на следующем рисунке.
Конфигурирование SMTP2GO сервера для передачи Email
Перед тем как начать программировать микроконтроллер нам нужно сконфигурировать SMTP сервер чтобы осуществлять передачу Email (электронной почты) при помощи ESP8266. Для выполнения этой задачи можно использовать сайт smtp2go.com.
Для начала нам будет необходимо имя пользователя и пароль для SMTP. Для их получения выполните следующие шаги:
Шаг 1: Кликните на “Try SMTP2GO Free” чтобы зарегистрировать бесплатный аккаунт.
Шаг 2. Откроется окно, в котором вы должны будете ввести имя, email и пароль.
Шаг 3. После заполнения всех этих полей и нажатия кнопки "sign up" вам на указанную электронную почту придет запрос на активацию вашего аккаунта. Кликните на полученную ссылку для активации вашего аккаунта и затем войдите на smtp2go.com используя ваш email и пароль.
Шаг 4. После того как вы войдете на сайт со своими учетными данными сразу же после этого вам будет предоставлен ваш SMTP Username (имя пользователя) и ваш SMTP Password (пароль). Запомните или запишите где-нибудь эти данные. После этого нажмите на ‘finish’.
Шаг 5. Затем в левом меню кликните сначала на “Settings”, а потом на “Users”. В этих разделах вы можете посмотреть информацию про ваш SMTP Server и номер порта (PORT number). К примеру, как показано на следующем рисунке.
После этого необходимо декодировать имя пользователя (Email) и пароль из формата ASCII в формат base64. Для этой цели можно использовать сайт BASE64ENCODE. Конвертированные имя пользователя и пароль сохраните чтобы можно было ими пользоваться в дальнейшем.
После выполнения всех этих действий можно приступить к программированию микроконтроллера ATmega16 и модуля беспроводной связи ESP8266.
Программирование микроконтроллера AVR ATmega16 и ESP8266
Необходимы будут две программы: одна для ATmega16, который будет действовать как передатчик команд, и вторая для ESP8266, который будет работать как приемник команд. Обе программы приведены в конце данной статьи. Для загрузки программы в ATmega16 можно использовать программатор USBasp, а для загрузки программы в модуль ESP8266 - Arduino IDE.
Также к микроконтроллеру ATmega16 подключим одну кнопку – при ее нажатии микроконтроллер передает команды в ESP8266, а ESP8266 в соответствии с ними осуществляет передачу электронной почты. Светодиод будет показывать статус (состояние) процесса передачи данных.
Исходный код программы для ATmega16 с пояснениями
Начнем с задания тактовой частоты микроконтроллера и подключения всех необходимых библиотек. Все используемые библиотеки находятся в Atmel Studio Package.
1 2 3 4 5 |
#define F_CPU 16000000UL #include<avr/io.h> #include<util/delay.h> #include <stdlib.h> #include <stdio.h> |
После этого необходимо определиться с бодовой скоростью передачи последовательного порта, который мы будем использовать для связи микроконтроллера с ESP8266. Эта скорость будет одинаковой и для ATmega16, и для ESP8266. Выберем скорость передачи равную 9600 бод/с.
1 |
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1) |
Регистры UBRRL и UBRRH будут использоваться для загрузки значения скорости передачи. Нижние 8 бит скорости передачи будут загружаться в UBRRL, а верхние 8 бит – в UBRRH. Для упрощения нашей задачи запрограммируем функцию для инициализации универсального асинхронного последовательного приемопередатчика (UART), в которой входным параметром будет требуемая скорость передачи. Эта функция будет включать:
- Установку битов приема и передачи в регистре UCSRB.
- Выбор 8-битного размера символа в регистре UCSRC.
- Загрузка нижних и верхних бит бодовой скорости в регистры UBRRL и UBRRH.
1 2 3 4 5 6 7 |
void UART_init(long USART_BAUDRATE) { UCSRB |= (1 << RXEN) | (1 << TXEN); UCSRC |= (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1); UBRRL = BAUD_PRESCALE; UBRRH = (BAUD_PRESCALE >> 8); } |
Далее запрограммируем функцию, которая будет отвечать за передачу одного символа. Данная функция будет ждать до тех пор, пока буфер не будет чист, а затем будет загружать значение символа в регистр UDR.
1 2 3 4 5 |
void UART_TxChar(char c) { while (! (UCSRA & (1<<UDRE))); UDR = c; } |
Вместо того чтобы передавать каждый символ по отдельности, запрограммируем функцию, которая будет передавать сразу целую строку символов.
1 2 3 4 5 6 7 8 |
void UART_sendString(char *str) { unsigned char s=0; while (str[s]!=0) { UART_TxChar(str[s]); s++; } } |
В главной функции main() сделаем вызов UART_init() чтобы начать передачу, а также сделаем эхотест (echo test) при помощи передачи строки "TEST" на модуль ESP8266.
1 2 |
UART_init(9600); UART_sendString("TEST"); |
Начнем конфигурирование контактов ввода-вывода общего назначения для светодиода и кнопки.
1 2 3 |
DDRA |= (1<<0); DDRA &= ~(1<<1); PORTA |= (1<<1); |
Если кнопка не нажата оставляем светодиод во включенном состоянии. Если кнопка нажата, то начинаем передачу данных (передаем команду “SEND”) на модуль ESP8266 и выключаем светодиод.
1 2 3 4 5 6 7 8 9 10 11 12 |
if(bit_is_clear(PINA,1)) { PORTA |= (1<<0); _delay_ms(20); } else { PORTA &= ~(1<<0); _delay_ms(50); UART_sendString("SEND"); _delay_ms(1200); } |
Исходный код программы для модуля беспроводной связи ESP8266
Программирование модуля ESP8266 будет заключаться в приеме команд от микроконтроллера Atmega16 и передачи Email используя полученный нами SMTP сервер.
Сначала подключим библиотеку WiFi чтобы у нас был доступ к интернету для передачи электронной почты. Определите ваш WiFi идентификатор (ssid) и пароль для осуществления успешного соединения с интернетом. Также определите ваш SMTP сервер.
1 2 3 4 |
#include <ESP8266WiFi.h> const char* ssid = "xxxxxxxxxxxx"; const char* password = "xxxxxxxxxxx"; char server[] = "mail.smtp2go.com"; |
В функции setup() задайте бодовую скорость передачи 9600 бод/с (такую же как и у Atmega16), произведите соединение с WiFi и отобразите IP адрес.
1 2 3 4 5 6 7 8 |
Serial.begin(9600); Serial.print("Connecting To: "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } |
В функции loop() прочтите принятые байты с контакта Rx и конвертируйте их в строку.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
if ( Serial.available() > 0 ) { while (Serial.available() > 0 && index1 < 6) { delay(100); inChar = Serial.read(); inData[index1] = inChar; index1++; inData[index1] = '\0'; } variable.toUpperCase(); for (byte i = 0 ; i < 6 ; i++) { variable.concat(String(inData[i])); } Serial.print("variable is = "); Serial.println(variable); Serial.print("indata is = "); Serial.println(inData); delay(20); } String string = String(variable); |
Если принимаемая команда опознана, то выполните передачу email адресату при помощи вызова функции sendEmail().
1 2 3 4 5 |
if (string == "SEND") { sendEmail(); Serial.print("Mail sent to:"); Serial.println(" The recipient"); Serial.println(""); } |
Необходимо отметить, что очень важно сконфигурировать SMTP сервер потому что без этого шага email не будет отправляться. Также необходимо установить одинаковые скорости передачи данных для микроконтроллера Atmega16 и модуля ESP8266.
Если вы успешно осуществите все действия, которые описаны в данной статье, то вы сможете успешно интегрировать микроконтроллер AVR в любой проект, связанный с интернетом вещей (IoT).
Полный текст программы
Для модуля беспроводной связи ESP8266
В представленном коде программы там, где вы видите последовательность симоволов "// ********************", необходимо изменить значения переменных на свои данные: имя пользователя, пароль, email и т.д.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
#include <ESP8266WiFi.h> const char* ssid = "xxxxxxxxx"; // введите SSID вашей WiFi сети. const char* password = "xxxxxxxxxx"; // введите пароль вашей WiFi сети. // ******************** char server[] = "mail.smtp2go.com"; // напишите адрес вашего SMTP сервера // ******************** WiFiClient Client; //определите wifi клиент в качестве клиента void setup() { delay(1000); Serial.begin(9600); // установка скорости 9600 бод/с Serial.println(""); Serial.print("Connecting To: "); Serial.println(ssid); WiFi.begin(ssid, password); // соединение с WIFI while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi Connected."); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } void loop() { char inChar = 0; char inData[6] = ""; // длина данных 6 символов String variable = ""; String variable1 = ""; int index1 = 0; if ( Serial.available() > 0 ) { // Read from Rx from atmega16 while (Serial.available() > 0 && index1 < 6) // read till 6th character { delay(100); inChar = Serial.read(); // старт последовательной передачи и сохранение в переменную inData[index1] = inChar; index1++; inData[index1] = '\0'; // добавляем ноль в конец } variable.toUpperCase(); // конвертируем в заглавные буквы (uppercase) for (byte i = 0 ; i < 6 ; i++) { variable.concat(String(inData[i])); // объединяем строки } Serial.print("Variable = "); Serial.println(variable); // печатаем входные данные delay(20); } String string = String(variable); // строка, которая будет использоваться для сравнения if (string == "SEND") { // если принимаемые данные = "SEND" то передаем email sendEmail(); // передаем email Serial.print("Mail sent to:"); Serial.println(" The recipient"); // debug if sent Serial.println(""); } } byte sendEmail() { if (Client.connect(server, 2525) == 1) // connect to smtp server with port address 2525 { Serial.println(F("connected to server")); } else { Serial.println(F("connection failed")); return 0; } if (!emailResp()) // если соединение не удалось, то возврат return 0; // Serial.println(F("Sending EHLO")); Client.println("EHLO http://www.example.com/"); // Send command EHLO previosly it was HELO if (!emailResp()) return 0; Serial.println(F("Sending auth login")); Client.println("AUTH LOGIN"); if (!emailResp()) return 0; // Serial.println(F("Sending User")); // в этом месте (в операторе Client.println ) вам будет необходимо заменить строку на ваше имя пользователя SMTP в формате base64 // к примеру адрес email dummy@gmail.com в формате base64 будет выглядеть следующим образом: djfdBDBEDEJD545616vfbSJHB= Client.println("Y2lyY3VpdGRxxxxxxxxxxxxxdtYWlsLmNvbQ=="); //base64, ASCII encoded SMTP Username ******************** if (!emailResp()) return 0; Serial.println(F("Sending Password")); // аналогично меняем в операторе Client.println строку на ваш SMTP пароль в формате base64 // к примеру пароль "password" будет выглядеть в формате base64 IBjbjHUInOUi4654== Client.println("Y2lyY3VpdxxxxxxzdA=="); //base64, ASCII encoded SMTP Password ******************** if (!emailResp()) return 0; // Serial.println(F("Sending From")); // изменить на нужный вам адрес email (ваш email) Client.println(F("MAIL From: dummy@gmail.com")); // ******************** if (!emailResp()) return 0; // изменить на адрес email вашего адресата (получателя) Serial.println(F("Sending To")); Client.println(F("RCPT To: test@gmail.com")); // ******************** if (!emailResp()) return 0; // Serial.println(F("Sending DATA")); Client.println(F("DATA")); if (!emailResp()) return 0; Serial.println(F("Sending email")); // изменить на адрес получателя Client.println(F("To: test@gmail.com")); // ******************** // изменить на ваш адрес Client.println(F("From: dummy@gmail.com")); //******************** Client.println(F("Subject: ESP8266 test e-mail\r\n")); Client.println(F("This is is a test e-mail sent from ESP8266.\n")); Client.println(F("Second line of the test e-mail.")); Client.println(F("Third line of the test e-mail.")); // Client.println(F(".")); if (!emailResp()) return 0; // Serial.println(F("Sending QUIT")); Client.println(F("QUIT")); if (!emailResp()) return 0; // Client.stop(); Serial.println(F("disconnected")); return 1; } byte emailResp() { byte responseCode; byte readByte; int loopCount = 0; while (!Client.available()) { delay(1); loopCount++; // ждем 20 секунд и если ничего не принимаем в это время, то останов if (loopCount > 20000) { Client.stop(); Serial.println(F("\r\nTimeout")); return 0; } } responseCode = Client.peek(); while (Client.available()) { readByte = Client.read(); Serial.write(readByte); } if (responseCode >= '4') { // efail(); return 0; } return 1; } |
Для микроконтроллера ATmega16
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
#define F_CPU 16000000UL // задание тактовой частоты микроконтроллера #include<avr/io.h> #include<util/delay.h> #include <stdlib.h> #include <stdio.h> #define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1) // задание коэффициента деления предделителя для установки нужного значения бодовой скорости void UART_init(long USART_BAUDRATE) { UCSRB |= (1 << RXEN) | (1 << TXEN); // установка битов RX и Tx последовательного порта UCSRC |= (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1); // используем 8 битный размер символа UBRRL = BAUD_PRESCALE; // загружаем нижние 8 бит значения бодовой скорости UBRRH = (BAUD_PRESCALE >> 8); // загружаем верхние 8 бит значения бодовой скорости } void UART_TxChar(char c) { while (! (UCSRA & (1<<UDRE))); // ждем опустошения буфера передачи UDR = c; } void UART_sendString(char *str) { unsigned char s=0; while (str[s]!=0) // цикл до конца строки { UART_TxChar(str[s]); // send s to UART_TxChar(s) function s++; } } int main(void) { UART_init(9600); // инициализируем UART для скорости 9600 бод/с UART_sendString("TEST"); //DDR – регистр направления данных DDRA |= (1<<0); //устанавливаем pin0 of PORTA в режим вывода поскольку к нему подключен светодиод DDRA &= ~(1<<1); //устанавливаем pin1 of PORTA в режим ввода поскольку к нему подключена кнопка PORTA |= (1<<1); // на pin 1 устанавливаем высокий потенциал while(1) { if(bit_is_clear(PINA,1)) // если кнопка не нажата { PORTA |= (1<<0); // включаем светодиод _delay_ms(20); } else { // если кнопка нажата, то передаем команду "SEND" по последовательному порту PORTA &= ~(1<<0); // выключаем светодиод _delay_ms(50); UART_sendString("SEND"); // передаем строку _delay_ms(1200); } } } |