Pull to refresh

Программирование и обмен данными с «ARDUINO» по WI-FI посредством ESP8266 Часть Вторая

Reading time 17 min
Views 31K
Предлагаю вам, уважаемые читатели GeekTimes, следующую статью из цикла (надеюсь, что не последнюю) по использованию микросхемы ESP8266 в качестве беспроводного моста для AVR микроконтроллеров, на примере аппаратной платформы Arduino Uno (Nano).

После первой статьи на эту тематику я получил множество дружелюбных откликов как-то: “А на кой чёрт ты взял ардуину, если всё можно было сделать исключительно на ESP8266” или “Почему ты, болван, не использовал esp-link ”. “Действительно почему?” — подумал я и накатал уже эту статью и обновил несколько своих программ в качестве новых аргументов.

Итак, встречайте беспроводной программатор для AVR микроконтроллеров BABUINO version 0.9



Подробности под катом:

Исходная версия программы была опубликована в прошлой статье и в принципе, делала почти то же самое, что и новая версия. Но отличалась крайним неудобством в работе. Как, впрочем, и конкурирующая esp-link. Поэтому, вздохнув, я привинтил GUI на JAVA. Теперь вы можете спокойно выбирать нужный вам для загрузки файл через оконный менеджер, а также редактировать IP адрес целевого устройства. Номер TCP порта я все-таки вшил намертво (он редактируется только в коде), но по идее, его нужно изменять только, если этот порт используется где-то у вас ещё (но тогда придется менять его номер и в прошивке ESP8266).

Микроконтроллер используется тоже пока один с размером FLASH памяти 32 кбайт, это, к примеру, всем известный Mega328P. Как, я уже упоминал в прошлой статье, теоретически мой программатор шьет версии и в 16 кбайт, а может даже и 8 кбайт. Ну и естественно в 64 кбайт и 128 кбайт версиях AVR он даже может, вероятно, прошить первые 32 кбайт памяти. Но таких микроконтроллеров у меня на руках нет и как оно на самом деле, сказать не могу. Там все дело в том, что 16 бит адреса в стандартных командах программирования по SPI настолько хитро делятся и режутся (битик сюда, битик туда), что понять, как всё будет при другом размере памяти отличном от 32 кбайт, совсем непросто.

Да, программа после записи не верифицируется, специальные регистры AVR не читаются, EEPROM тоже не пишет. Но все это теоретически возможно сделать без проблем (в отличие от принципиальных ограничений загрузки кода через UART, как в esp-link). Можете дописать, я не возражаю, продукт не коммерческий.

Здесь исполняемый файл для 64 бит Windows. Из него же можно выдрать код на Java. Или взять на Гитхабе

Итак, запускаем.

Дальше всё просто, открываете нужный вам HEX файл (да, программка научилась преобразовывать формат BIN в HEX, ура!) вписываете IP адрес ESP8266, которая вам нужна и жмёте “Upload” (как найти IP адрес ESP модуля — история отдельная). Если программатор находит ESP8266, то он быстренько (намного быстрее, чем esp-link), запихивает в неё ваш код, а та уже сама отправляет его дальше в AVR микроконтроллер по интерфейсу SPI. Программатор подробно описывает свои действия в окне и ему даже можно верить, за исключением того, что программа записалась в AVR. Как я уже говорил, верификации записи нет, а интерфейс SPI чисто синхронный, ему вообще по барабану, есть ли кто на втором конце линии или нет. Он закидывает данные, а ответа вообще не ждёт.

После закрытия программы (через “Stop” или просто через закрытие окна) ввёденные вами данные (если вы жали кнопку Upload до этого) сохраняются в файле tcp_dat.txt в корневом каталоге диска C, чтобы при последующем открытии вам не надо было долго мучиться, снова набирать. А вообще, окошко программы можно и не закрывать. По опыту, оно никому не мешает.

Теперь же обратимся в сторону ESP модуля и снова вспомним, теперь уже подробно, каким образом его подключить к AVR и каким манером прошить, дабы иметь возможность пользоваться вышеупомянутым программатором, а также просто гонять данные по WI-FI без проводов.

Итак, первым делом схема подключения. Обращаем внимание, что нам нужны ESP8266 в версиях с достаточным количество GPIO. Нам будут необходимы свободные выводы под RESET, MOSI и SLK для программирования по SPI интерфейсу, не считая того, что для обмена данными мы будем использовать ещё и обычный UART с его RX и TX. Для этого удобнее всего по цене и качеству мне показались ESP8266-07.

Берём её и сразу припаиваем два резистора номиналом 5-10 кОм. Первый к EN (CH-PD) и питанию, второй к GPIO15 и земле



Теперь можно цеплять её к адаптеру, через который мы будем заливать прошивку NodeMCU, а затем и нашу программу-загрузчик на LUA.



Адаптеров миллион, берите любой. Соединяем, как водится, RX c TX и наоборот. Землю делаем общей. Питание на ESP8266 подаем отдельное, а адаптеру хватит и своего с USB порта. Кидаем на GPIO0 ноль и заливаем свежую прошивку NodeMCU (взять её можно здесь, или собрать свою) через программку NODE MCU PyFlasher.



Всё это в принципе, описано в предыдущей статье и много раз в интернете. Далее, убираем ноль с GPIO0 (можно просто оставить висячим, ничего страшного) и открываем среду ESPlorer для отладки и загрузки программ на Lua. Правда, эта зараза без среды JAVA не работает. Так что эту штуку (Загрузите Java на свой настольный компьютер сейчас!) ставить предварительно придётся все равно.

После подключения к ESPlorer, тот немножко поформатирует ESP модуль, информируя вас соответствующими сообщениями. (главное в этот момент ничего не трогать), а затем ребутнет ESP. Можно начинать работу по загрузке LUA программ.



А программа будет у нас следующая:

Загрузчик для AVR на Lua в ESP8266
function InstrProgrammingEnable () -- instruction for MC "enable programming"

p=0
while p<31 do
p=p+1

pin=8  
gpio.write(pin, gpio.LOW)
spi.send(1, 0xAC,0x53)
read = spi.recv( 1, 8)
spi.send(1,0,0)
gpio.write(pin, gpio.HIGH)

     if (string.byte(read)== 83) 
        then     
        --print("connection established") 
        p=33
            if(p==31)
            then 
            --print("no connection")
            end
        end
    end
end



function ProgrammingDisable ()
pin=2--END OF RESET FOR MK GPIO4
gpio.mode(pin, gpio.INPUT)

pin=8  
gpio.mode(pin, gpio.INPUT) -- CE chip enable not used GPIO15

pin=5--CLK MASTER for SPI GPIO14 used
gpio.mode(pin, gpio.INPUT)

pin=6--MISO MASTER  for SPI GPIO 12 may not used
gpio.mode(pin, gpio.INPUT)

pin=7--MOSI MASTER for SPI //GPIO13 used
gpio.mode(pin, gpio.INPUT)
end



--PROGRAMMING ENABLE

function ProgrammingEnable ()
pin=2-- RESET FOR MK
gpio.mode(pin, gpio.OUTPUT)
gpio.write(pin, gpio.LOW)

pin=2--POZITIV FOR 4MSEC RESET FOR MK
gpio.mode(pin, gpio.OUTPUT)
gpio.write(pin, gpio.HIGH)

tmr.delay(4)
gpio.mode(pin, gpio.OUTPUT)
gpio.write(pin, gpio.LOW)

tmr.delay(25000)
end





function InstrFlashErase() 
pin=8  
gpio.write(pin, gpio.LOW)
spi.send(1,0xAC,0x80,0,0)
gpio.write(pin, gpio.HIGH)
tmr.delay(15000)

pin=2--RESET FOR MK
gpio.mode(pin, gpio.OUTPUT)
gpio.write(pin, gpio.HIGH)
tmr.delay(20000)
gpio.write(pin, gpio.LOW)

--print( "FLASH is erased")
InstrProgrammingEnable () 
end




function InstrStorePAGE(H, address, data)
pin=8  
gpio.write(pin, gpio.LOW)
spi.send(1,H,0,address,data)
gpio.write(pin, gpio.HIGH)
tmr.delay(500)
end




function InstrWriteFLASH(page_address_low,page_address_high)
pin=8  
gpio.write(pin, gpio.LOW)
spi.send(1,0x4C,page_address_high,page_address_low,0)
gpio.write(pin, gpio.HIGH)
tmr.delay(5000)-- иногда не прописываются флэш при малых задержках
end




function Programming (payload)

pin=8--CS MASTER for SPI
gpio.mode(pin, gpio.OUTPUT, gpio.PULLUP)
pin=4--LED LIGHTS ON LOW
gpio.mode(pin, gpio.OUTPUT)
gpio.write(pin, gpio.LOW)
--print(string.len(payload))
page_count = 7 -- пишем 1 килобайт 

for k =0  ,page_count ,1 do--quantity of pages

    for i=0 , 127, 2 do-- -1
    address = i/2
    data=payload:byte(i+1+128*k)
        if data == nil 
        then
        data = 0xff
        end
    InstrStorePAGE(0x40,address,data)
  --  tmr.delay(100)--  otherwise not in time write
    data =payload:byte(i+1+1+128*k)
        if data == nil then
        data = 0xff
        end
    InstrStorePAGE(0x48,address,data)
--    tmr.delay(100)
    end

page_address_low=bit.band(k ,3)*64 -- 3 это двоичное 11
page_address_high=k/4+frame1024*2

tmr.delay(1000)
InstrWriteFLASH(page_address_low,page_address_high)
tmr.wdclr()
end

pin=4--LED
gpio.mode(pin, gpio.OUTPUT)
gpio.write(pin, gpio.HIGH)
end



--MAIN BLOCK



wifi.setmode(wifi.STATION)
--wifi.sta.config("SSID","password ") -- set SSID and password of your access point
station_cfg={}
tmr.delay(30000)
station_cfg.ssid="SSID"
tmr.delay(30000)
station_cfg.pwd="Password"
tmr.delay(30000)
wifi.sta.config(station_cfg)
tmr.delay(30000)
wifi.sta.connect()
tmr.delay(1000000)
--print(wifi.sta.status())
--print(wifi.sta.getip())



while ( wifi.sta.status()~=1 ) do
if( wifi.sta.status()==5)
then
break
end
end



prog_address="";

sv=net.createServer(net.TCP,30)
tmr.delay(100) 
--print("SERVER READY")

sv:listen(40000,function(c)--Главный сервер, работает всегда
    c:on("receive", function(c, payload)
        --print(payload)
        if (payload =="program\r\n")
        then
            c:send("ready\r\n")
             --print("ready for program\r\n")

            tmr.wdclr()
            spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, spi.DATABITS_8,80,spi.FULLDUPLEX) -- настройка SPI 320 примерно 115 000 кБод
--на 80 еще работает это 1 мбит
            ProgrammingEnable ()
            tmr.delay(100)
            InstrProgrammingEnable ()
            tmr.delay(100)
            InstrFlashErase()
            tmr.delay(100)
            frame1024=0--номер переданного фрейма


        
            st=net.createServer(net.TCP,30)--Сервер для приема файла программы и трансляции ее в AWR, выключается командой stop program
            st:listen(40001,function(c)
        
            c:on("receive", function(c, payload)
            tmr.wdclr()
       
            Programming (payload)
            frame1024=frame1024+1
             
            end)
            end)
        end

          
        if (payload =="data\r\n")
        then
            
            tmr.wdclr()
            c:send("ready\r\n")
            --  print("ready for data\r\n")


    
        
            c:on("receive", function(c, prog_address_payload)

            prog_address=prog_address_payload--получаем IP адрес UDP хоста для отправки к нему данных
            --  print(prog_address)
            c:send(prog_address) 

     
            
            srv=net.createUDPSocket()-- Сервер для приема данных , выключается командой data stop
       
            srv:listen(50000)
            
            uart.setup(0,9600,8,0,1,0) 
              
            srv:on("receive", function(srv, pl) -- принимаем данные с компьютера по UDP
            pl=pl*1
            --     print(pl)
                 
            
            uart.write(0,pl) -- отправляем их по UART на AVR
            end)
          
            uart.on("data", 1, function(data) -- принимаем данные по UART из AVR

            srv:send(50000,prog_address,data) -- отправляем их по UDP на компьютер
                 
            end, 0)

            tmr.wdclr()   

            end)
            
       end

     
       if (payload =="stop data\r\n") -- здесь закрываем ненужные уже сервера
       then

             ready = false
             if(srv~=nil) 
             then
                srv:close()
                 --  print("stop data")
             end
                collectgarbage()   
       end


       if (payload =="stop program\r\n") 
       then 
            if(st~=nil)
            then
                st:close()
                frame1024=0
                ProgrammingDisable ()
                -- print("stop program")
            end

            collectgarbage()

           
       end

                
    end)
    
end)


Она почти такая же, как и описанная в первой части, но теперь умеет не только транслировать поток данных с компьютера на AVR микроконтроллер, но и делать теперь это в обратном направлении.

В процессе написания пришлось разрешить интересную загвоздку. Когда мы по протоколу UDP сыпем данные с компьютера на ESP8266, то проблем никаких нет. IP адрес ESP модуля известен (мы его сами вбиваем в начале, если что), порт известен, всё хорошо. Но когда мы пытаемся отправить данные в обратном направлении, то сделать мы этого не можем, так как ESP модуль не знает IP адрес компьютера, на котором запущен программатор. Вернее, он его знает, поскольку до этого мы устанавливаем контакт по TCP протоколу для пересылки управляющих команд (program, data, stop). А при использовании этого протокола устройства обмениваются своими адресами, ибо он двунаправленный. Но вот нам он его не скажет. Во всяком случае, я не нашел в API NodeMCU функции для его вытаскивания. Можно конечно, вбить IP компьютера прямо в загрузчик на ESP8266, но и это не выход. Вдруг мы запускаем программу для обмена данными на другом компьютере. Или у него не одна сетевая карта.

Поэтому я сделал костыль. Мы передаем IP компьютера, на котором запущена программа явно перед началом обмена данными. В API JAVA к счастью есть функция определения сетевого адреса хоста, на котором работает программа. ESP8266, получив этот адрес, теперь спокойно может слать данные не только в AVR, но и из него. Что касается программы на LUA, то она очень дубова, наивна и проста, а связано это с тем, что в этом языке я ориентируюсь пока еще очень плохо.

Итак, с помощью ESPlorer мы прописываем обновленный загрузчик в ESP8266. Не забудьте либо изначально обозвать файл, как init.lua или переименуйте его через ESPlorer, а то не взлетит. Ну, и естественно, забейте в тело (там, где MAIN BLOCK) загрузчика имя вашей сети и пароль.

Дальше нам надо определить и зафиксировать IP адрес ESP модуля в нашей внутренней сети. Заходим в местный роутер под правами админа и видим что-то в этом роде.



Это адрес пока динамический (то есть после переподключения может быть назначен другой) поэтому мы делаем его связывание с MAC-адресом ESP8266 там же в роутере.



Его можно даже написать (последние цифры) на самом модуле, если у вас склероз.



ESP модуль готов к работе, можно коннектить его с AVR микроконтроллером.



Запускаем BABUINO, пишем IP адрес ESP модуля, выбираем HEX файл (какой-нибудь BLINK), жмём «Upload» и наслаждаемся миганием светодиода. Программатор напишет в своём окошке, что-то вроде этого:



Но иногда может быть и такое, если ESP модуль по какой-то причине помалкивает:



Теперь посмотрим, как наш загрузчик зашитый в ESP8266 может обмениваться данными в обе стороны. Для этого мы будем управлять робо-тележкой с клавиатуры компьютера, а она будет отправлять нам свой пройденный путь. Первая моя четырехколесная робо-тележка погибла смертью храбрых, получив сигнал из Космоса и упав со стола (этот астральный факт будет объяснен позже). Поэтому пока не придет новая акриловая рама из Китая, эксперименты будут проводиться над двухколесной робо-тележкой, которую, к слову, уже как с год я периодически пытаюсь заставить балансировать на двух колесах, но пока безуспешно. По схеме подключения электроники и программе для AVR, тележки эти не отличаются, чем мы и воспользуемся.



Программа для работы телеги написана на С и больших затруднений в понимании вызывать не должна. Пишем постоянно новое значение скорости в регистры ШИМ регулятора, а он выдает сигналы на моторы. Есть цепь обратной связи на внутреннем АЦП микроконтроллера. При снижении напряжения батарей скорость программно увеличивается. Благодаря этому вплоть до полной просадки, тележка катается с постоянной скоростью. Суть в том, что чем плотнее заполнение ШИМ, тем быстрее крутятся моторы, но поскольку напряжение батареи со временем падает, то они крутятся медленнее. Тогда увеличивается заполнение ШИМ и они снова крутятся быстрее. И так пока заполнение ШИМ не будет 100%, то есть на выходе будет постоянно логическая “1”. Тут уж ничего поделать нельзя. На зарядку!

Программа на С для микроконтроллера AVRmega328P
/*
 * TWO_WEELS_ROBOT_NEW.c
 *
 * Created: 22.09.2017 23:48:49
 * Author : User
 */ 

#define F_CPU 16000000

#include <avr/io.h>
#include <stdint.h>// стандартные целые числа

#include <avr/interrupt.h>
#include <math.h>	// математика
#include <stdio.h> //стандартный ввод-вывод
#include <setjmp.h>
#include <stdlib.h> //стандартные возможности


volatile uint8_t Speed_of_ADC_conversion=0;
volatile uint8_t U_BATTERY; //среднее напряжение  подаваемое на моторы деленное на три

volatile uint8_t avr_speed=30;// граница скорости, к которой сводится обратная связь с напряжения
// при 8 вольтах батареи макс скорость равна где-то 110
// средняя , берите 53. На 10 еле ползет

volatile uint8_t komanda_s_kompa = 0;

volatile uint8_t transmition_ready = 0;
volatile uint8_t wheel_counter=0;





#define Left_Speed  OCR0A	// скорость левых моторов
#define Right_Speed  OCR0B  // скорость правых моторов


	void time_delay(long dell)// передается время в миллисекундах
							//   функция для временных задержек
					
	{ long i;
		cli();
		sei();
		dell=dell*1500;
		for(i=0;i<dell;i++){;;};

	}


ISR(USART_RX_vect)  //получаем команды управления через UART
{

	komanda_s_kompa=UDR0;

}



ISR(PCINT1_vect )//PC2 int 10 //вход счетчика оборотов колеса
{
		
	transmition_ready=1;
	wheel_counter++;
	
}



ISR(TIMER0_OVF_vect)// старт цикла запускаем ацп каждые 30 мс, 
	// на первом цикле запускается АЦП, на втором корректируется скорость раз в 90 мс
{
	
	Speed_of_ADC_conversion++;
		if (Speed_of_ADC_conversion<2)
	
		{ADCSRA |=(1<<ADSC);}// запускаем АЦП
	
	
		if(Speed_of_ADC_conversion>2)// запускаем коррекцию скорости 
	{
		ADCSRA &=~(1<<ADSC);
		Speed_of_ADC_conversion=0;
		U_BATTERY = ADCH;////регистр данных с ацп
						// в него заносится напряжение с моторов, через делитель 1/3 и
						// и интегрирующий конденсатор поскольку напряжение с ШИМ импульсное 
						//т.е макс U = 8 вольт (2 литиев батарейки - LN298 1 вольт)   = 7 вольт / 2
						// равно 3,5 т.к. вокруг нуля и еще делитель ,то на вход АЦП попадет  около 1 вольт 
			if(U_BATTERY<=avr_speed)// коррекция скорости
				
				{Right_Speed++;// если притормозили из-за расхода батарей, то увеличиваем скорость
				Left_Speed++;}
				else
				{Right_Speed--;// если разогнались, то притормаживаем
				Left_Speed--;}
	
	}

}



void stop()

{
	
	PORTD|=(1<<PORTD3);
	PORTD|=(1<<PORTD2);
	PORTD|=(1<<PORTD4);
	PORTD|=(1<<PORTD7);
	
}



void go_left()
{
	
	PORTD|=(1<<PORTD3);// правый вперед
	PORTD&=~(1<<PORTD2);
	PORTD|=(1<<PORTD4);// левый  назад
	PORTD&=~(1<<PORTD7);
	
}



void go_right()
{	
	
	PORTD|=(1<<PORTD2);// правый назад
	PORTD&=~(1<<PORTD3);
	PORTD|=(1<<PORTD7);//левый вперед
	PORTD&=~(1<<PORTD4);
	
}



void go_ahead()// движение вперед  
{
	
	PORTD|=(1<<PORTD3);// вперед
	PORTD&=~(1<<PORTD2);
	PORTD|=(1<<PORTD7);//вперед
	PORTD&=~(1<<PORTD4);
	
}



void go_back()// движение назад
{
		
	PORTD|=(1<<PORTD2);// правый назад
	PORTD&=~(1<<PORTD3);
	PORTD|=(1<<PORTD4);// левый  назад
	PORTD&=~(1<<PORTD7);
	
}








int main(void)




{ cli();
	//инициализация UART на 9600
	UCSR0A=0;
	UCSR0B=0b10011000;
	UCSR0C=0b00000110;
		
	UBRR0L=103;
	UBRR0H=0;
	
	//инициаализация внешнего прерывания INT0 на порту С2 номер прерывания 10
 

	PCICR|=(1<<PCIE1);// разрешение группы прерываниС14-8
	PCMSK1|=(1<<PCINT10);// разрешение конкретного прерывания INT10
	DDRC&=~(1<<PORTC2); // порт на вход для прерывания от  геркона
	PORTC|=(1<<PORTC2);
		
	
	
	
	
	
	// инициализация АЦП напряжение подаем на ADC1, 
	
	ADMUX= 0b01100001;  // V ref питание 5 в, сдвигаем результат ADC1  преобразования влево в старший регистр, канал 2
	ADCSRA=0b10010110;// прерывания от ацп запрещены опрашиваем программно
	ADCSRB=0;
	DDRC&=~(1<<PORTC1);//порт ацп/

	
	// инициализация счетчика Т0 для ШИМ модуляции регистр А левые моторы, регистр B правые
	
	
	TCCR0A |=(1<<COM0A1)|(1<<COM0B1);//внешние выводы включены
	TCCR0A &=~(1<<COM0A0)&~(1<<COM0B0);
	TCCR0A |=(1<<WGM00);
	TCCR0B &=~(1<<WGM02)&~(1<<WGM01);// режим ШИМ c точной фазой
	TCCR0B|=0b00000101; // частота переполнения 30 гц
	// CS02 CS01 CS00 - 000 - отключен; 001  без делителя; 010 c делителем 8; 
	// 011 -64; 100 -256; 101 -1024
	TIMSK0|=(1<<TOIE0);// разрешаем прерывание от Т0 по переполнению
	

	
	DDRB|=(1<<5);// зеленый светодиодный выход
	DDRD=0b11111110; // порты для моторов и ТХ на выход, RX на вход
	PORTD&=~(1<<PORTD5)&~(1<<PORTD6); // останов моторов после сброса
	
	
	Left_Speed=10;// начальная скорость моторов маленькая для плавного разгона
	Right_Speed=10;//максимальная скорость моторов определяется напряжением батареи ( 8-12 в)
	
	sei();
	
	PORTB |=(1<<5);//помигаем после сброса
	time_delay(500);
	PORTB &=~(1<<5);
	time_delay(500);
	PORTB |=(1<<5);
	time_delay(500);
	PORTB &=~(1<<5);
	time_delay(500);
	PORTB |=(1<<5);
	
	while (1)
		


				{	
	
				
				
				if( (UDRE0)){
				if(transmition_ready==1)// отправляем данные если путь увеличился
					{
					
					UDR0=wheel_counter;	
					
					
					transmition_ready=0;
				
					}
				}
				
		
		
	
			
				switch (komanda_s_kompa)
				{
				
				
				case 2:
				go_right();
				break;
				
				case 1:
				go_left();
				break;
				
				case 3:
				go_ahead();
				break;
					
				case 4:
				go_back();
				break;
				
				case 5:
				avr_speed++;
				if (avr_speed>100)
				{
					avr_speed=100;
				}
				time_delay(200); // газуем
				
				break;
				
				case 6:
				avr_speed--;
				if (avr_speed<0)
				{
					avr_speed=0;
				}
				time_delay(200);// тормозим
				break;
				
				case 0:
				stop();
				break;
		
					
				
				}
		
				}
		
}	


Пройденный путь считается герконом, который выдает внешнее прерывание INT10. Как только путь инкрементируется, то данные сразу сыпятся в UART. Соответственно сигналы управления (вперед, назад, влево, вправо, газ, тормоз, стоп) поступают из UART в обратном направлении.

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

Сама программа руления телегой взята из предыдущей статьи, где она ранее управляла механической рукой. Сделаны лишь небольшие дополнения: два текстовых окошка, где можно видеть в режиме реального времени, получаемые и отправляемые данные (1,2,3,4,5,6,0 — вперед, вправо, влево, назад, газ, тормоз, стоп), а также возможность редактирования и сохранения IP адреса ESP модуля через GUI и кнопка «Connect». Управляем тележкой при помощи стрелок либо с клавиатуры, либо мышкой в окне. Правда поскольку все кнопки в одном цикле, изменять скорость и направление одновременно не получится, только поочередно. Но понятное дело, это только из-за того что программа демонстрационная.



Исполняемый файл для 64 бит Windows. Из него же можно выдрать код на Java. Или взять на Гитхабе.

Теперь, когда программатор и обмен данными протестированы (вообще, наверное, я не меньше сотни раз прошивал AVR по WI-FI таким макаром), то можно вернуться к вопросу, почему я выбрал для себя этот путь, а не esp-link.

Итак, начнём с инсталляции.

Прошивка ESP модуля у меня чуть сложнее, чем у конкурента. Сначала шьем NodeMCU, потом заливаем загрузчик на LUA. В esp-link шьём только одну прошивку. Но затраты времени здесь однократные. В дальнейшем мы ESP модуль не трогаем. С другой стороны, в нашем случае мы можем допиливать мою дубовую программу на LUA, как нам хочется, добавлять свои модули и т.д. и т.п. С esp-link это сложнее. Там знанием азов LUA и API NodeMCU уже не отделаешься.

На стороне компьютера преимущества полностью у BABUINO. Просто запускаем исполняемый файл и работаем. Даже среды JAVA не надо, если у вас 64-разрядная версия Windows (но зато тогда надо 200 Мбайт диска). А если у вас Линух или МакОсь, то тогда вообще можете проверить слоган компании Oracle про свою Java, «Написано в одном месте, работает везде», ибо виртуальная машина Java и байт-код же. Но, если честно, не проверял, не знаю.

С esp-link вас ждут знатные танцы с бубнами, по установке Tibbo manager (знаю по опыту). Это такая программа для поддержки виртуального COM порта. Требует настройки кучи параметров и постоянного присутствия в системе. Сразу вряд ли заработает, крепитесь. Потом через браузер надо настроить сам ESP модуль. Важно везде, в том числе и в Tibbo выставить правильные скорости обмена данными и всяких битов стоп и четности.

После чего, уже через стандартный Arduino Uploader (с которого я спи… взял дизайн) или через среду Arduino IDE (опять таки настроив COM), начинаем очень до-о-олго грузить программу в AVR. Нет, правда, она реально грузиться вечность. Даже, если маленькая. Можно чай сходить заварить за это время. Минус, периодически отваливается, оставляя вас в полном недоумении, почему загрузка не состоялась. И тут выход один — сброс, перезапуск, сброс, перезапуск…

А BABUINO закидывает намного быстрее, как и обычный SPI программатор (а файлы, умещающиеся в один пакет 1024 байт, вообще мгновенно). Правда, не верифицирует. Но это поправимо и много времени все равно занимать не будет, так как будет вестись одновременно с прошивкой (хорошая особенность протокола SPI). Плюс нам доступны все SPI команды программирования: фьюзовые и локовые биты, прошивка EEPROM и т.д. А если запись не получается, то вы все причины видите в текстовом окне. Примечание. Команды-то доступны, но реализации пока нет. Упс.

Это то, что касается беспроводного программирования. Теперь перейдем к пересылке данных. В этом плане я esp-link не пользовался, поэтому рассуждения мои будут чисто теоретическими.

Итак, esp-link использует, насколько мне теперь известно MQTT протокол. По сути это всего лишь абстракция следующего уровня над TCP.



Не вдаваясь в тонкости, посмотрим, зачем он вообще нужен.

Ну, к примеру, когда у вас много устройств в каком-нибудь умном доме, которые работают на этом протоколе. Когда у вас что-то там уходит в облако и обратно. Когда вы интегрируетесь в какую-то сеть, работающую на MQTT. Когда у вас совместный проект, чтобы не выдумывать что-то свое, а пользоваться чем-то готовым и известным коллегам.

А если вам просто надо отправить байтовый поток туда обратно без проводов, то нафига вам такие сложности? Зачем громоздить ещё один протокол сверху?

Хотя, я естественно, не отрицаю полезности MQTT протоколов вообще и даже попробую интегрировать его поддержку в свой загрузчик-обменщик в следующих наработках и статьях. Но пока он мне не нужен, дальше поглядим. А нужен ли он вам, решайте сами.

Тем временем тележка моя послушно крутит колёсами в нужную сторону и высылает телеметрию пройденного пути на компьютер (обратите внимание, брандмауэр компьютера может не пущать пакеты с ESP). В следующий раз попробуем поуправлять ею с мобильного телефона. Я пробовал балансировать её на двух колесах с клавиатуры, но опыт оказался неудачным. Есть мысль использовать для этого акселерометры смартфона (пробовал использовать и отдельную плату с гироскопом и акселерометром, но не взлетело).

Вернемся теперь ко второму вопросу, неоднократно поднимавшемуся в ходе обсуждения статьи в комментариях. «А почему не сделать всё на ESP? Она же может! А AVR пусть будет как расширитель портов и хватит с него».

Конечно, может! Хотя все равно, как видим без AVR не обойтись.

Да, она может, если:

1. Вы потомственный объектно-ориентированный программист обожающий заворачиваться во всякие обёртки, делать колбэки, и не мыслящий жизнь без мета таблиц.

2. Вы неплохо знаете детали работы разных ОС. И вам раз плюнуть изучить новую ОС теперь уже RT (реального времени) её системные вызовы и библиотеки, написанные китайцами за миску риса.

3. В университете ваше знакомство с микроконтроллерами ограничилось одной-двумя лабораторными работами, и вы даже знать не желаете какие-сякие там биты и периферийные устройства. И вообще, вам для ШИМ, например, проще взять программную библиотеку, чем чего-то там использовать аппаратно.

4. Вам не нужна реакция устройства в микросекунды. Нет, конечно, RTOS может попробовать вам её обеспечить, на то она и операционная система реального времени. Но не факт, что обеспечит.

5. У вас нет сотен килобайт уже написанного кода, а главное уже работающего без глюков на AVR и вам не надо соответственно его портировать и отлаживать, а проще писать на нативном SDK (который тоже вам, раз плюнуть изучить по английским источникам) с нуля.

Тогда, да. Не читайте эту статью. А главное, не пишите комменты.

Но если:

1. Вы много лет ковыряетесь с микроконтроллерами и знаете их архитектуру наизусть.

2. Вам не нужны непонятного китайского происхождения баги.

3. Вы написали и отладили уже мегабайты кода за свою долгую жизнь разработчика, и вам совершенно не хочется заново всё переписывать и отлаживать.

4. У вас нет времени или желания изучать на языке вероятного противника нативный SDK и RTOS не очень широко известной в миру компании (это ж все-таки не Микрософт), а также ждать и верить в их патчи и апдейты.

5. Программируя, вы никуда особо за «begin if then do while switch end» не вылазили, а слова лямбда-функция и корутина считаете матерной латынью.

6. Вам, по сути, к вашему и так прекрасно работающему устройству, но в соответствии с веяниями новых времён, нужен просто беспроводной мост для идентификации, программирования и обмена данными.

Ну и используйте для этого ESP8266. Просто, как беспроводной мост. Они, понимаешь, используют AVR как расширитель портов для ESP. А мы сделаем наоборот!



На самом деле, прошу не относиться к последним моим высказываниям слишком серьезно. По сути это всего лишь шутка. Любой достаточно опытный разработчик делает свой выбор на основании многих факторов как-то: параметры быстродействия и энергопотребления, жизненный цикл устройства, преёмственность с прошлыми наработками, стоимость самого изделия и стоимость перевода на новую платформу, надёжность, время требующееся на изучение новых архитектур, SDK, операционных систем, наличие сотрудников с таким опытом работы в совместном проекте и так далее.

Поэтому лучше когда:



Буду рад, если статья вам понравилась. Библиографии не привожу, она та же, что и в предыдущей статье.

P.S.

А напоследок про сигналы из Космоса. Весь секрет в том, что ESP модуль с прошивкой NodeMCU очень любит отправлять всякую информацию на консоль. К примеру, после аварийной перезагрузки (а она у ESP случается иногда, уже поверьте, глюкоза ещё та). Или, например, если вы забыли убрать print («чего-то там») из самой программы на Luа после отладки. Или когда у вас вылазит Deprecated (устаревший) API (вы сменили прошивку и должны теперь использовать новое написание, к примеру, при запуске UDP сервера) и ESP теперь будет всегда напоминать вам об этом. Пока не перепишете код.

И вот проблема в том, что всё это аккуратно и методично отправляется на консоль, то есть в порт UART. Ну, а если ваш порт UART ждет в это время команду или данные, чтобы указать вашей тележке поехать вперёд? То тогда ваша тележка может упасть со стола.
Так, что этот момент тоже стоит учитывать.
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+19
Comments 52
Comments Comments 52

Articles