Programmierung und Datenaustausch mit ARDUINO über WIFI über ESP8266 Teil Eins

Wie viele andere hausgemachte Produkte verwende ich regelmäßig AVR-Mikrocontroller für alle Arten von Amateurhandwerk. Und dank des Arduino-Konzepts sehen diese Handarbeiten jetzt auch elegant aus. In der Tat erhalten wir für etwa 300-400 Rubel eine mehrschichtige Miniaturplatte mit einer Maske, einem Siebdruck und den Peripheriegeräten für den Mikrocontroller, die vollständig darauf gezüchtet wurden (außerdem in der SMD-Version!). Ich spreche nicht von allen Arten von Plug-Ins der gleichen "Arduino" -Serie: Sensoren, Controller, Displays und ganze Sets, die zusätzlichen Peripheriegeräte, die wir so sehr brauchen. Und wieder ist alles preiswert und in hervorragender Leistung. Sie müssen praktisch nichts mehr züchten und auf das "Knie" löten.


Aber all diese verschiedenen Amateurhandwerke erfordern natürlich eine vorläufige Programmierung. Und später, mit verschiedenen Verbesserungen, muss ich diese Handwerke ständig neu flashen. Es ist klar, dass es bequemer ist, dies aus der Ferne zu tun, als sie ständig auf einen normalen Programmierer zu ziehen. Dank derselben Arduino-Plattform gibt es hier im Allgemeinen viele Optionen: Bluetooth, ZigBee, ein Funkkanal mit Ihrem persönlichen Protokoll, IR und sogar Wi-Fi. Mit allen können Sie einen drahtlosen Kontakt mit Ihrem Mikrocontroller herstellen. Aber wir werden bei der letzten Option aufhören. Es gibt vier Hauptgründe:

1: modern, das Internet der Dinge!

2: In jeder Wohnung gibt es einen WLAN-Router. Registrieren Sie Ihre Geräte im Heimnetzwerk und voila!

3: Ihr Handwerk macht einen revolutionären Sprung in ihrer Entwicklung; Sie können nicht nur aus der Ferne programmiert werden, sondern auch mit der Welt um sie herum kommunizieren: Die elektronische Uhr nimmt unabhängig die genaue Zeit von den NTP-Serveruhren ab, Executive-Geräte werden vom anderen Ende der Stadt oder des Landes aus gesteuert, Registrierungsgeräte speichern die gesammelten Daten in Wolke etc. usw.

4: Es gibt eine wunderbare Serie von ESP8266-Chips, auf denen dies nicht einfach zu implementieren ist.

In diesem Artikel werden am Beispiel eines mechanischen Arms an Servos die Fernprogrammierung und der Datenaustausch mit einem PC (oder was auch immer) mit Geräten, die auf AVR-Mikrocontrollern basieren, zerlegt und demonstriert. Ich möchte sofort darauf hinweisen, dass alle nachfolgend aufgeführten Programme rein demonstrativ sind und keinen kommerziellen Wert haben. Behauptungen, wie zum Beispiel, warum der Programmierer so kastriert und schlecht funktionsfähig ist oder warum es keine zusätzlichen Dienste gibt, die überall sind, werden daher nicht akzeptiert. Da die Codes offen sind, kann jeder sie nach eigenem Ermessen beenden, aber ich habe immer noch genug für die Arbeit.

Es wird davon ausgegangen, dass der Leser bereits mit den Arduino-Modulen (Shields) sowie dem Anschluss und der Firmware des ESP8266 vertraut ist. Tatsächlich wurde im Web eine große Menge an Material veröffentlicht, das die Grundlagen der Arbeit mit diesen Geräten erklärt, und ich möchte dies hier nicht wiederholen. Für Anfänger gibt es am Ende des Artikels eine Liste nützlicher Links zu diesen Themen, unter denen Sie viele Informationen finden , warum bei Ihnen alles nicht funktioniert . Aus meiner Erfahrung als ehemaliger Elektronikingenieur kann ich verantwortungsbewusst erklären, dass 99% der Probleme wie folgt sind:

1. Schlechte Kontakte. Da die "Arduino" -Schilde bedeuten, dass sie durch Drähte vom Typ "Vater-Mutter" und nicht durch Löten miteinander geschaltet werden, verschwindet sehr oft irgendwo etwas. Schau es dir an. Und tatsächlich ist Elektronik, wie sie sagen, die Wissenschaft der Kontakte.

2. Stromprobleme. Liefern Sie keine 5 Volt, wenn 3.3 erforderlich ist. Manchmal kommt Rauch vom ESP8266. Auf der anderen Seite werden logische Signale von Fünf-Volt-Geräten problemlos verarbeitet.

3. Probleme mit ausreichender Leistung. ESP8266 hat eine abscheuliche Natur und kann manchmal fast dreihundert Milliampere verbrauchen, obwohl es vorher mit dreißig zufrieden sein konnte. Dementsprechend ist die gebrechliche Stabilisator 3,3 Volt Platine "Arduino", an die man nichts addieren kann, angeschlossen, sie sinkt sofort auf mikroskopische Werte. Und Sie können nicht verstehen, warum es funktioniert, dann nicht.

4. Verwechslung mit Schlussfolgerungen. Überprüfen Sie immer, welche Signale wohin gehen. Der RXD-Empfänger muss eine Verbindung zum TXD-Sender sowie von TXD zu RXD herstellen, aber MOSI muss eine Verbindung zu MOSI und MISO zu MISO usw. herstellen.

5. Verlassen Sie sich beim ESP8266 nicht auf Pull-up-Widerstände im Stromkreis, sondern ziehen Sie die Kabel immer über externe Widerstände mit 5 bis 10 Kilo Ohm und nicht nur über einen Jumper auf Null oder Strom. Andernfalls können Sie bestenfalls einen beispiellosen Stromverbrauch erzielen und dann den unangenehmen Geruch von verbranntem Kunststoff riechen.

6. Schwärme von Software. Da Software für einzelne Benutzer von denselben Enthusiasten geschrieben wurde, treten beim Aktualisieren von Versionen derselben Firmware regelmäßig Störungen in der Firmware selbst und Fehler auf. Es wird durch Crawlen in den entsprechenden Foren behandelt, manchmal sogar auf Englisch. Einige Genossen behaupteten sogar, der ESP-Chip selbst sei feucht wie das Wetter in St. Petersburg, aber andererseits gibt es auch die Meinung, dass sich die Situation seit 2014 (dem Jahr, in dem er erstmals veröffentlicht wurde) dramatisch verbessert hat (im Gegensatz zum Wetter).

7. Geheimnisvolle Störungen. Dies ist ein seltenes, aber nervenaufreibendes Phänomen. Zum Beispiel hatte ich kein entferntes "Arduino" -Gerät. Vielmehr geschah es aber mit Fehlern. Aber es ging ohne Fehler, wenn ein Kabel vom Programmierer daran hing (aber ohne den Programmierer selbst). "AHA", sagte ich mir und löte einen 15 pF-Kondensator zwischen den Datenübertragungsstift und den Synchronisationsstift. Alles hat funktioniert. Aber der Tag ist vorbei.

Beginnen wir also mit dem einfachsten. Wir haben einen MechArm für mechanische Gliedmaßen (aber nicht das, was Howard Volovits zusammengebaut hat), hergestellt in China, und einen PC mit Windows. Die Aufgabe besteht darin, das Programm aus der Ferne zu flashen und vom Computer aus zu verwalten.

Für den Steuerungscontroller nehmen wir einen niedlichen Miniatur-Arduino-Nano-Schal mit einem ATmega328P-Stein. Dieses Board wird perfekt in den mechanischen Arm gedrückt.

Jetzt entscheiden wir, wie wir es programmieren wollen. Es gibt drei Hauptmethoden, die für Remote-Firmware am besten geeignet sind: über die SPI-Schnittstelle, über den integrierten Bootloader und über den JTAG-Port.

Die einfachste Option ist natürlich der eingebaute Bootloader (Bootloader). Dies ist eine Speichervoreinstellung in FLASH, einem Programm, das einen Code gemäß einem bestimmten Protokoll empfängt (z. B. unter Verwendung des einfachsten UART) und ihn mit speziellen Befehlen an den Speicherort des geladenen Programms schreibt. Dies funktioniert beispielsweise mit dem Bootloader ARDUINO IDE. Nach einem Zurücksetzen oder Starten wartet der Bootloader einige Zeit auf den Empfang von Daten. Wenn er nicht wartet, startet er die Ausführung des Programms von der Nulladresse aus. Wenn die Daten ankommen, schreibt er sie in den Programmabschnitt. Nach dem nächsten Zurücksetzen wird das heruntergeladene Programm gestartet. Im Detail habe ich vielleicht falsch beschrieben, aber das Wesentliche ist genau das. Daher benötigen wir nur drei Ausgänge für die Programmierung: RTD-Empfänger, RESET-Reset und GND-Masse. Im Allgemeinen wird der TRD-Sender auch zur Überprüfung des aufgezeichneten Programms verwendet. Bei einfachen Demonstrationsanwendungen (nicht für ein Kernkraftwerk) kann die Überprüfung jedoch weggelassen werden.

Der Loader selbst ist in Assemblersprache geschrieben. Beispiele für einfache Loader finden Sie in den Datenblättern von AVR. Sie können einen vorhandenen Bootloader graben, wenn er gemeinfrei ist, und ihn einfach in einer vorgefertigten Form verwenden, wenn das Protokoll bekannt ist, nach dem er funktioniert. Die einzige Einschränkung besteht darin, dass Sie AVR in einem speziellen Modus konfigurieren müssen, indem Sie spezielle Sicherungsbits blinken lassen, was von einem normalen Programmierer ausgeführt wird. Anschließend können Sie den Bootloader auch in den Speicher des Mikrocontrollers nähen (dh Sie können nicht einmal auf einen Programmierer verzichten).

Die zweite Option ist die Programmierung über die serielle SPI-Schnittstelle. Hier gibt es keinen internen Bootloader, aber wir programmieren, indem wir spezielle Befehle und dann Daten über die oben genannte Schnittstelle senden. Hier haben wir einen externen Bootloader, aber Sie müssen ihn noch schreiben. Neben RESET und GND werden vier zusätzliche MOSI-Ausgänge für Übertragung, MISO-Daten, SLK-Synchronisation und CS-Chip-Auswahl verwendet. Im Allgemeinen können Sie jedoch auch MISO und CS entfernen. Daten werden nur akzeptiert (dann erfolgt keine Überprüfung des Programms), und wir haben nur einen Kristall.

Jeder Ansatz hat seine Vor- und Nachteile (und ich habe JTAG überhaupt nicht in Betracht gezogen, da das menschliche Leben kurz ist). Aber am Ende neigte ich mich zu SPI, weil ich zu faul war, um in Assembler zu schreiben, aber ich fand keine offenen, vorgefertigten Bootloader (ich sah einfach nicht gut aus).

Um einen Funkkanal aufzubauen, habe ich mich, wie bereits erwähnt, für den derzeit weithin bekannten ESP8266-Chip entschieden - einen Mikrocontroller bzw. einen ganzen SoC (System-on-Chip) des chinesischen Herstellers Espressif mit Wi-Fi-Schnittstelle. Neben Wi-Fi zeichnet es sich durch die Möglichkeit aus, Programme aus einem externen Flash-Speicher auszuführen. Und speziell für mein Projekt habe ich den ESP8266-07 mit 512 KB Speicher an Bord genommen.


Im Allgemeinen ist jeder ESP8266 geeignet, wenn zusätzliche Beine für die Implementierung von SPI vorhanden sind. Daher passt der einfachste ESP8266-01 nicht zu uns, da er nur sehr wenige Abschnitte für Eingangs- / Ausgangsanschlüsse hat. Andererseits beträgt der Preisunterschied weniger als einhundert Rubel, und sie sind gleichermaßen verfügbar. Große Debug-Boards mit ESP, bei denen eine Reihe von Peripheriegeräten der Einfachheit halber verwendet werden, sind ebenfalls nicht für uns geeignet, da sie nicht dort ankommen, wo wir sie in unseren mechanischen Arm schieben möchten.

Das globale Wesen der Idee im Allgemeinen war wie folgt. Der in den Mikrocontroller geladene Programmkörper wird drahtlos über WI-FI (innerhalb Ihres Heimnetzwerks) vom Computer zum ESP übertragen. Und ESP schreibt dieses Programm bereits per Kabel über die SPI-Schnittstelle direkt in den FLASH-Speicher des Mikrocontrollers. Dann wird es natürlich zurückgesetzt und das geladene Programm kann ausgeführt werden. Darüber hinaus muss das ESP über eine unabhängige Einheit verfügen, die auch den Datenaustausch mit dem Mikrocontroller verwaltet, da wir nicht nur programmieren, sondern auch Daten mit ihm austauschen möchten. Insbesondere für ein Projekt mit MechArm senden wir nach der Aufzeichnung des Programms immer noch Servosteuerungssignale, um diese Hand in Bewegung zu setzen. Daher ist es für uns auf dem ESP selbst ratsam, einen TCP-Server für die Programmübertragung und einen UDP-Server für die Steuerung von MechArm zu erstellen. Dementsprechend treten diese Server dem Heimnetzwerk bei und hören genau zu, ob es jemanden gibt, der neuen Code auf MechaArm hochladen oder jemandem zuwinken möchte.

Ich habe im Web festgestellt, dass Sie mit der Firmware bereits AVR über die Luft programmieren können. Das Hauptproblem besteht jedoch darin, dass diese Firmware für andere Zwecke nicht mehr verwendet werden kann. Nach dem Programmieren möchten wir auch remote mit AVR kommunizieren.

Welche Software werden wir verwenden:

Für den PC habe ich alles in JAVA, IntelliJ IDEA geschrieben . Grundsätzlich können Sie jedoch alles tun. Die Hauptsache für uns ist, einen Client zu schreiben, der das Programm zum Flashen von AVR an ESP8266 sendet.

Ich selbst schreibe Programme für AVR in ATMEL STUDIO , in C-Sprache, selten in Assembler. Ich verwende im Prinzip keine Arduino-Skizzen, fast jede notwendige Bibliothek wird in ein oder zwei Stunden geschrieben, mit einem vollständigen Verständnis ihrer Arbeit. Ich habe Skizzen ausprobiert, aber solange Sie kein Betriebssystem auf dem AVR haben, werden Skizzen einem Freund Peripheriegeräte wegnehmen und regelmäßig fehlschlagen. Ja, die Arduino IDE selbst ist im Vergleich zu ATMEL STUDIO natürlich eine sehr primitive Sache. Aber hier ist die Frage natürlich umstritten, für Geistes- und Schulkinder wird es wahrscheinlich mit Skizzen lustiger und einfacher.

Für die Programmierung des ESP8266 habe ich die NodeMCU-Firmware verwendet und Programme in Lua geschrieben. Nein, ich würde gerne in Java und C schreiben, aber es gibt keine in ESP. Die Sprache Lu, wie sie auf unsere Aufgabe angewendet wird, ist nicht schwierig, ein paar Kleinigkeiten zu meistern. Und tatsächlich habe ich zum Herunterladen und Debuggen von Programmen auf ESP den IDE ESPlorer verwendet . Ein kostenloses Inlandsprodukt (aber Sie können es dem Autor spenden), das natürlich nicht mit den oben genannten Umgebungen verglichen werden kann, aber wie das Geschenkpferd sagt ... Um ESPlorer zu verwenden und auf LUA zu schreiben, müssen wir zuerst die Basis-Firmware (vom Hersteller geliefert) im ESP8266-Chip auf ändern neue. Das NODE MCU PyFlasher-Programm wird uns bei diesem Vorhaben helfen. Ich meine, es wird helfen, es erneut zu flashen. Und wir werden die Firmware selbst erstellen und in die Hände der Ersteller-Website bekommen: NodeMCU . Und mehr über diesen Prozess können Sie hier lesen :

Alles ist sehr zugänglich und verständlich. Wir fügen den Basisbibliotheken SPI-Unterstützung und Bitoperationen hinzu (in LUA sind in unserem Fall Bitoperationen überladen und von diesen unbrauchbar). Viele Bibliotheken sollten nicht in die Firmware der Bibliotheken verschoben werden, da aufgrund des Vorhandenseins verschiedener Software auf dem ESP8266 nur noch sehr wenig Speicher übrig ist, eine Art erbärmliche 20 kB.

Natürlich können Sie auch nur die fertige Firmware nehmen, von der viele bereits im Internet hängen, aber ich empfehle sie nicht. Zumindest, weil einige von ihnen keine Unterstützung für Bitoperationen haben (und wir sie brauchen) und es keine Regulierung der Datenübertragungsrate über SPI gibt.
Dementsprechend werden sie standardmäßig mit einer Geschwindigkeit von 40 MHz geteilt durch einen kleinen Koeffizienten übertragen, und daher hat der AVR keine Zeit, sie zu verdauen.

Wer zu faul ist, um Firmware zu erstellen, kann meine aus der Cloud herunterladen.

Jetzt haben wir die Firmware und müssen sie in den ESP8266 anstatt in den Basis-Firmware laden. Dazu benötigen wir einen einfachen USB-Adapter - UART.

Wir verbinden die TXD-Beine mit RXD und RXD mit TXD, wir machen die gemeinsame Masse, verwenden aber anscheinend keinen praktischen 3,3-V-Ausgang am Adapter. In den meisten Fällen wird der ESP8266 ihn vollständig entleeren. Deshalb füttern wir es separat. Dann versetzen wir das ESP in den Programmiermodus (GP0 auf den Boden, falls jemand es vergessen hat) und führen den NODE MCU PyFlasher aus .



Vergessen Sie nicht, den Flash-Speicher zu löschen (ja, löscht alle Daten). Andernfalls verbleibt je nach Firmware-Version nach der Programmierung möglicherweise unnötiger Müll im Speicher, der bei weiteren Arbeiten den Müll in die Konsole wirft. Vorher habe ich Software verwendet, bei der es keine Möglichkeit gab, den Speicher vorher zu löschen. Ich war schrecklich gequält, da nichts funktionierte. Und der Sarg wurde gerade geöffnet, nur die Wahrheit im englischen Forum der Macher von NODE MCU.

Nachdem wir die erforderliche Firmware erworben haben, können wir jetzt LUA-Programme (es gibt auch MicroPython, aber ich habe es nicht verwendet) mit sehr praktischen APIs von NODE MCU schreiben und debuggen. Wir starten den bereits erwähnten ESPlorer.



Wir konfigurieren es auch für die Verwendung mit ESP8266 und stellen die Parameter der seriellen Verbindung ein. Alles ist ganz einfach und wird im Internet immer wieder angegeben.

Jetzt schreiben wir das Programm auf LUA, das wir dann auf ESP8266 hochladen:

Lua Bootloader für AVR geschrieben auf ESP8266
<b>function InstrProgrammingEnable () -- instruction for MC "enable programming"</b> 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 <b>function ProgrammingDisable ()</b> pin=2--END OF ESET FOR MK gpio.mode(pin, gpio.INPUT) pin=8 gpio.mode(pin, gpio.INPUT) pin=5--CLK MASTER for SPI gpio.mode(pin, gpio.INPUT) pin=6--MISO MASTER for SPI gpio.mode(pin, gpio.INPUT) pin=7--MOSI MASTER for SPI gpio.mode(pin, gpio.INPUT) end <b>function ProgrammingEnable ()</b> 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 <b>function InstrFlashErase() </b> 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 <b>function InstrStorePAGE(H, address, data)</b> pin=8 gpio.write(pin, gpio.LOW) spi.send(1,H,0,address,data) gpio.write(pin, gpio.HIGH) tmr.delay(500) end <b>function InstrWriteFLASH(page_address_low,page_address_high)</b> 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 <b>function Programming (payload)</b> 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 <b>--MAIN BLOCK</b> wifi.setmode(wifi.STATION) --wifi.sta.config(" ","") -- set SSID and password of your access point station_cfg={} tmr.delay(30000) station_cfg.ssid=" " tmr.delay(30000) station_cfg.pwd="" 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 sv=net.createServer(net.TCP,30) tmr.delay(100) print("SERVER READY") sv:listen(4000,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") spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, spi.DATABITS_8,320,spi.FULLDUPLEX) ProgrammingEnable () tmr.delay(100) InstrProgrammingEnable () tmr.delay(100) InstrFlashErase() tmr.delay(100) frame1024=0--    st=net.createServer(net.TCP,30) st:listen(4001,function(c) c:on("receive", function(c, payload) tmr.wdclr() Programming (payload) frame1024=frame1024+1 end) end) end if (payload =="data\r\n") then c:send("ready\r\n") print("ready for data\r\n") srv=net.createServer(net.UDP) tmr.delay(1000) pin=10 gpio.write(pin, gpio.HIGH) uart.setup(0,9600,8,0,1,0) srv:listen(5000) srv:on("receive", function(srv, pl) pl=pl*1 --print(pl) uart.write(0,pl) tmr.wdclr() end) end if (payload =="stop\r\n") then if(st~=nil) then st:close() frame1024=0 ProgrammingDisable () print("stop program") end if(srv~=nil) then srv:close() print("stop data") end end end) end) end) 



Wo die relevanten Funktionen die folgenden Aktionen ausführen:

Funktion InstrProgrammingEnable () - Versetzt den Mikrocontroller mit einem speziellen Befehl, der über SPI gesendet wird, in den Programmiermodus.

Funktion ProgrammingEnable () - Setzen Sie den AVR einfach für 25 ms zurück, bevor Sie mit der Programmierung beginnen

Funktion ProgrammingDisable () - Nach dem Ende der Programmierung übersetzen wir die SPI-Ausgänge in ESP8266 in einen inaktiven Zustand, damit sie uns bei der Ausführung von Code auf dem Mikrocontroller nicht stören (plötzlich werden sie dort verwendet).

Funktion InstrFlashErase () - Wir löschen den Flash-Speicher auf dem Mikrocontroller vor dem Programmieren. Warum dies erklärt werden muss, ist nicht notwendig.

Funktion InstrStorePAGE (H, Adresse, Daten) - Dieser Befehl schreibt das Byte des Programms in den internen Puffer des Mikrocontrollers. Dies ist jedoch nicht die Flash-Aufzeichnung selbst, da der Flash hier Seite für Seite mit 128 Bytes geschrieben wird.

Funktion InstrWriteFLASH (page_address_low, page_address_high) - aber dies ist eine Flash-Aufzeichnung und es braucht Zeit, achten Sie auf die Zeitverzögerung von 5.000 μs.

Funktionsprogrammierung (Nutzlast) - die größte und wichtigste Funktion mit den oben genannten Funktionen. Es nimmt das übertragene Programm in Blöcken von 1024 Bytes, teilt sie in Bytes auf und bildet die Adressen für sie, sendet es dann an den Mikrocontroller im internen Puffer und initialisiert den Flash-Datensatz alle 128 Bytes. Dann nimmt er das nächste Kilobyte Code und wiederholt den Vorgang, natürlich mit einem Versatz in den Adressen, um weiter zu schreiben und den aufgezeichneten nicht zu überschreiben. Zuerst habe ich versucht, das gesamte Programm weiterzuleiten, aber wenn ich in ESP8266 6 Kilobyte überschreite, endet der verfügbare Speicher einfach und es stürzt ab. Ein Kilobyte erwies sich als die bequemste Einheit, da es ordentlich in Teile unterteilt und bequem über TCP übertragen wird (wir müssen es noch vom Computer beziehen). Eine größere Größe wird auch nicht benötigt, TCP, wissen Sie, begrenzt in der aktuellen Version das übertragene Paket auf 1500 oder Bytes (aber aus irgendeinem Grund wurde 1440 irgendwie an mich übertragen).

Als wäre nichts kompliziert, aber ein paar Fallstricke mussten überwunden werden.

Als nächstes kommt MAIN BLOCK. Darin wir:

Wir sind in einem drahtlosen Netzwerk registriert.

Zuerst erstellen wir einen TCP-Server, der drei Befehle abhört:

1. "Programm" (wir werden programmieren)

2. "Daten" (wir werden die Daten ändern),

3. "Stop" (wir stoppen alles).

Wenn wir programmieren, initialisieren wir zuerst das SPI und erstellen einen weiteren TCP-Server, der die Daten (Firmware-Code) pro Kilobyte erfasst und die Mikrocontroller-Programmierfunktionen für diese aufruft. Ich verstehe, dass es albern aussieht, einen zweiten Server zu erstellen, aber dies ist eine Notwendigkeit, da die lokale API das Erstellen nur eines Sockets unterstützt und wir die Befehle "program" und "data" von den übertragenen Daten trennen müssen, da sie sich nach Augenmerk nicht unterscheiden, es gibt Bytes und Hier sind Bytes.

Wenn wir nicht programmieren, sondern Daten austauschen und in unserem Fall an den Mikrocontroller senden möchten, senden wir zuerst die Zeichenfolge „Daten“ über TCP. Als Reaktion darauf wird ein UDP-Server erstellt (ich erinnere Sie daran, dass wir nicht dynamisch mit einer mechanischen Hand verwalten und dass wir keine Verzögerungen bei der Bildung von TCP-Paketen benötigen und tatsächlich ein Byte als ganzen TCP-Frame über den Zähler senden). Und UDP-Datagramme werden klein sein und sich schnell bilden.

Nachdem der UART initialisiert wurde und jedes drahtlos empfangene Byte bereits über die TXD-Leitung an den Mikrocontroller gesendet wurde, muss dieser empfangen werden, wenn das entsprechende Programm dort aktualisiert wird. Der Datenaustausch in eine andere Richtung ist ebenfalls nicht schwer zu organisieren, aber ich habe ihn noch nicht implementiert.

Nun, mit dem Befehl "stop" schließen die oben genannten Server (mit Ausnahme des allerersten) die Verbindungen und der Hauptserver wechselt wieder in den Wartezustand der Befehle "program" und "data".

Da die SPI-Schnittstelle in ESP8266, E / A-Ports für die Signale CS, CLK, MISO, MOSI, RESET (für AVR) programmgesteuert emuliert wird, können Sie alle verfügbaren verwenden und nicht die in meinem Bootloader angegebenen. Außerdem stellte sich heraus, dass CS und MISO grundsätzlich auch in diesem Fall unterbrochen werden können, es wird ohne sie funktionieren. Nun, ein Pin wird auf der in die ESP8266-Platine eingebauten LED verwendet, so dass sie manchmal blinkt und anzeigt, dass das Programm noch aktiv ist.

Überprüfungen auf Aufnahmefehler werden nicht durchgeführt (mit Ausnahme der ersten Anforderung an AVR, aber diese Informationen werden einfach auf der Konsole angezeigt), das EEPROM ist nicht programmiert, mehr als 32 KB sind nicht genäht - kurz gesagt, es gibt noch viel zu tun. Die SPI-Austauschgeschwindigkeit beträgt ungefähr 115 Kbit. In wenigen Sekunden wird alles geflasht, ungefähr wie bei einem normalen seriellen Programmierer wie ISP500.

Nehmen Sie den Code, geben Sie Ihre Netzwerke und Kennwörter ein, kompilieren Sie ihn im ESplorer, nennen Sie ihn "init" (damit er beim Neustart beginnt) und senden Sie ihn an ESP8266. Es sollte funktionieren. Zumindest im Sinne einer Arbeit als drahtloser Programmierer.

Jetzt werden wir uns mit der geschäftsführenden Partei befassen - einem Personal Computer.

Tatsächlich müssen wir die HEX-Datei, in die Ihre in der ATMEL STUDIO-Umgebung geschriebenen Programme umgewandelt werden, über WI-FI an den uns bekannten Socket-Port senden (in diesem Fall 4000). Der kleine Haken ist, dass wir eine binäre BIN-Datei für die Übertragung benötigen und ATMEL STUDIO uns nur mit einem HEX gefällt. Es gibt zwei Auswege; oder konvertieren Sie es mit einem speziellen Programmkonverter wie WinHex in das BIN-Format oder machen Sie es selbst in Ihrem Programm. Ich habe es noch nicht getan, aber es scheint nicht schwierig zu sein, da muss man den Titel abschneiden und etwas anderes tun.

Aus diesem Grund habe ich das Bootloader-Programm in JAVA geschrieben (hauptsächlich, weil ich nicht weiß, wie ich etwas anderes machen soll) und in der einfach schönen und kostenlosen IntelliJ IDEA-Umgebung gearbeitet. Es wird ein TCP-Client erstellt, der nach einem Server sucht, der auf ESP8266 ausgeführt wird. Wenn es findet, kontaktiert es ihn und sendet ihm eine Datei, die sich an einer solchen Adresse befindet. Der Code ist unten.

PC-basierter JAVA-Datei-Downloader
 import java.io.*; import java.net.*; import java.util.ArrayList; import java.util.List; public class Net { <b> public static void main(String args[]) { new Http_client(4000); }</b> } class Http_client extends Thread { int port; String s; String Greetings_from_S; Http_client(int port){ this.port = port; start(); } public void run() { //192.168.1.113 -  ESP8266   .  ,      //    ,    try (Socket socket = new Socket("192.168.1.113", port)) { PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true); pw.println("program");// Greetings with SERVER System.out.println("program"); BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); Greetings_from_S = br.readLine(); System.out.println(Greetings_from_S); if(Greetings_from_S.equals("ready")) { try { File file = new File("d:BlinkOUT.bin");//    BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); byte [] data = new byte[bis.available()]; bis.read(data); byte [] data_buffer = new byte[1024]; int frames = data.length/1024; System.out.println(frames); int residy = data.length%1024; for (int i = 0; i < frames;i++) { for (int k = 0; k< (1024); k++) { data_buffer[k] = data[k+1024*(i)]; } sendingChunk(data_buffer); } byte [] data_buffer2= new byte[residy]; for (int i = 0; i < residy;i++) { data_buffer2[i] = data[i+1024*(frames)]; } sendingChunk(data_buffer2); pw.println("stop");// System.out.println("stop program"); } catch (Exception e) { System.out.println(e); } } } catch (Exception e) { System.out.println(e); } } public void sendingChunk (byte [] data_buffer){ try (Socket socket = new Socket("192.168.1.113", 4001)){ BufferedOutputStream bos = new BufferedOutputStream((socket.getOutputStream())); bos.write(data_buffer); bos.flush(); System.out.println(data_buffer.length); } catch (Exception e) { System.out.println(e); } } } 



Hier wurde natürlich zu viel verwundet, alle Arten von Ready werden grundsätzlich nicht benötigt. Wenn die TCP-Verbindung hergestellt ist, wird sie hergestellt. Das einzige Problem war, dass die Datei in keiner Weise in geraden Teilen von 1024 Bytes gesendet werden wollte, wie ich es wirklich brauchte, obwohl ich die Größe ausdrücklich angegeben habe. Anscheinend gibt es eine Art endgültigen Puffer, auf den von JAVA nicht zugegriffen werden kann, und er sendet Pakete in der gewünschten Größe, was für die empfangende Seite völlig inakzeptabel ist. Zuerst habe ich versucht zu verzögern, damit der Puffer es leid wird, auf die nächsten Teile zu warten und sie so zu senden, wie sie sind. Aber die Verzögerung begann zu wirken, als sie 10 Sekunden erreichte, was für ein übertragenes Kilobyte irgendwie etwas zu viel schien.

Aber dann bemerkte ich, dass aus irgendeinem Grund das erste Stück immer glatt läuft, welches bestellt wurde, und bereits ab dem zweiten beginnt eine unvorhersehbare Bacchanalia. Daher habe ich den Client dazu gebracht, die Verbindung zu öffnen, einen Teil des Codes in 1024 Bytes zu senden und die Verbindung zu schließen. Und so weiter, bis die gesamte Datei gesendet wird. Alles hat gut funktioniert.

Sie müssen lediglich die JAVA-Laufzeit auf dem Computer installieren. Normalerweise beginne ich jedoch sofort mit IntelliJ IDEA, da Sie dort immer sehen können, was in der Konsole passiert (aber hier benötigen Sie eine JAVA-Umgebung). Obwohl Sie natürlich auf intelligente Weise eine GUI erstellen müssen. Das heißt, das Fenster, in das der Pfad zur Datei fällt, die Möglichkeit, die Portnummern im Fenster zu ändern, und andere notwendige Dinge. Und all dies in Form einer ausführbaren Datei zu sammeln.

Und Tapericha, wie Koroviev sagte, kehren wir zu den Bürgern zurück, und zwar zu dem mechanischen Glied MechArm, das ganz am Anfang erwähnt wurde. Wir haben jetzt die Möglichkeit, es remote zu programmieren und dann zu verwalten. Fahren wir mit dem Steuerprogramm auf der Seite des Mikrocontrollers fort.

In diesem Fall müssen wir vier Servos steuern. Hier sind die.

Ein solcher Antrieb wird durch Rechteckimpulse einer Periode von 20 ms (50 Hz) mit einem Tastverhältnis von 2 bis 4 Prozent gesteuert. Das heißt, 2% sind eine vollständige Drehung in die eine Richtung, 4% in die andere. Die Aufgabe ist nur für die im AVR integrierte PWM.



Ein Servoantrieb wird verwendet, um sich nach links und rechts zu bewegen. der zweite auf sich selbst - von sich selbst; drittens auf und ab; Die vierte ist die Klaue selbst, die zusammengedrückt und expandiert werden muss. Alles ist in C geschrieben und in ATMEL STUDIO zu einer HEX-Datei kompiliert. Eine etwas seltsame Art von Programm ist auf die Tatsache zurückzuführen, dass die Hand anfangs von der Tastatur aus gesteuert wurde, die mit Drähten an den Mikrocontroller gebunden war. Aber die Drähte von gestern müssen wir weiterentwickeln.

Sie können natürlich Skizzen für Servos von "ARDUINO" verwenden, aber ich mochte sie nicht. Es ist interessanter, sich selbst zu schreiben. Außerdem sollten alle vier Servos gleichzeitig und nicht im Multiplexmodus arbeiten, wenn die PWM nacheinander auf jedes Servo umschaltet. Denn niemand hat die Schwerkraft aufgehoben, und ein angehobenes Glied senkt sich sofort ab, wenn die Steuerimpulse nicht mehr zum entsprechenden Servoantrieb gelangen. Ich bin mir nicht sicher, ob die Skizze „ARDUINO“ den gleichzeitigen Betrieb von vier Servos ermöglicht. Aber wir können selbst ein Programm schreiben, das die notwendigen Anforderungen erfüllt. In Ermangelung eines Betriebssystems, das die Lämmer von den Ziegen trennt, ist die Verwendung von Skizzen, die um die Peripheriegeräte des Mikrocontrollers konkurrieren (und wir wissen nicht einmal, welche im Voraus), zu fehlerhaft.

Hier ist der Code selbst, den wir mit dem ESP8266-07 in den Arduino Nano schreiben.

Programm zur Steuerung von MechArm für Mikrocontroller AVRmega328P
 #define F_CPU 16000000 #include <avr/io.h> #include <stdint.h>//    #include <avr/interrupt.h> #include <math.h> //  #include <stdio.h> // - #include <avr/eeprom.h> #include <setjmp.h> #include <stdlib.h> //  #define UART_BAUD_RATE 115200 //  1    20 #define COUNTER1_OFF TCCR1B=0b00000000 // CS02 CS01 CS00 - 000 - ; 001  ; 010 c  8; 011 -64; 100 -256; 101 -1024 #define COUNTER1_ON TCCR1B=0b00000011 //  0       0  1 #define COUNTER0_OFF TCCR0B=0b00000000 // CS02 CS01 CS00 - 000 - ; 001  ; 010 c  8; 011 -64; 100 -256; 101 -1024 #define COUNTER0_ON TCCR0B=0b00000100 //  2       B2(PD6)  3(PD7) #define COUNTER2_OFF TCCR2B=0b00000000 // CS02 CS01 CS00 - 000 - ; 001  ; 010 c  8; 011 -64; 100 -256; 101 -1024 #define COUNTER2_ON TCCR2B=0b00000110 volatile uint16_t period_20ms; volatile uint8_t State_of_keyboard; volatile uint8_t start_position [6]; volatile int8_t number_servo; ISR(USART_RX_vect)//   UART { State_of_keyboard=UDR0; return; } ISR(TIMER0_COMPA_vect)//  0    { PORTB &=~(1<<0); TIMSK0&=~(1<<OCIE0A); TIFR0 |=(1<<OCF0A); return; } ISR(TIMER0_COMPB_vect) //  1    { PORTB &=~(1<<1); TIFR0 |=(1<<OCF0B); TIMSK0 &=~(1<<OCIE0B); return; } ISR(TIMER2_COMPA_vect)//  2(PD6)    { PORTD &=~(1<<6); TIFR2 |=(1<<OCF2A); TIMSK2 &=~(1<<OCIE2A); return; } ISR(TIMER2_COMPB_vect)//  3(PD7)    { PORTD &=~(1<<7); TIFR2 |=(1<<OCF2B); TIMSK2 &=~(1<<OCIE2B); return; } ISR(TIMER1_OVF_vect){//   20      COUNTER1_OFF; COUNTER0_OFF; COUNTER2_OFF; TIFR0 |=(1<<OCF0A); TIFR0 |=(1<<OCF0B); TIFR2 |=(1<<OCF2A); TIFR2 |=(1<<OCF2B); TIFR1 |=(1<<TOV1); PORTB |=(1<<0)|(1<<1); PORTD |=(1<<6)|(1<<7); TCNT1 = period_20ms; //  20  TCNT0 = 0; TCNT2 = 0; TIMSK0|=(1<<OCIE0A)|(1<<OCIE0B); TIMSK2|=(1<<OCIE2A)|(1<<OCIE2B); OCR0A=start_position[1];//  0  0 OCR0B=start_position[2];//  0  1 OCR2A=start_position[3];//  0  2 OCR2B=start_position[4];//  0  3 COUNTER1_ON; COUNTER2_ON; COUNTER0_ON; return; } void time_delay(long i) { cli();sei(); long k; i*=2000; for(k=0;k<i;k++){;;}; } void timer_counter0_1_2_INIT()//   0,1,2 { // 1 TCCR1A &=~(1<<COM1A0)|~(1<<COM1A1)|~(1<<COM1B0)|~(1<<COM1B1);//   TCCR1A &=~(1<<WGM10)|~(1<<WGM11); TCCR1B &=~(1<<WGM12)|~(1<<WGM13);//    period_20ms=60575; TCNT1 = period_20ms; TIMSK1|=(1<<TOIE1);//|    //TIFR0   TOV0 // 0 TCCR0A &=~(1<<COM0A0)|~(1<<COM0A1)|~(1<<COM0B0)|~(1<<COM0B1);//   TCCR0A &=~(1<<WGM00)|~(1<<WGM01); TCCR0B &=~(1<<WGM02);//    // 2 TCCR2A &=~(1<<COM2A0)|~(1<<COM2A1)|~(1<<COM2B0)|~(1<<COM2B1);//   TCCR2A &=~(1<<WGM20)|~(1<<WGM21); TCCR2B &=~(1<<WGM22);//    COUNTER1_ON; } void servo_reset() { start_position[1]=97;//  0  0 start_position[2]=70;//  0  1 start_position[3]=92;//  0  2 start_position[4]=124; // 0  3 COUNTER1_ON; time_delay(100); } void servo_go( int8_t moven, uint8_t servo_position_max, uint8_t servo_position_min)// { switch (moven){ case 1: start_position[number_servo]++; if(start_position[number_servo]==servo_position_max){start_position[number_servo]--;};//  +90  break; case 2: start_position[number_servo]--; if(start_position[number_servo]==servo_position_min){start_position[number_servo]++;};//6  -90  break; }; time_delay(20); return; } //PORTB-0,1, PORTD - 6,7 -  , 8-  COUNTER 0 int main(void) { uint8_t servo_positionmin=0, servo_positionmax=0; int8_t const servo_position1max = 122, servo_position1min=58; //  int8_t const servo_position2max = 120, servo_position2min=36;// int8_t const servo_position3max = 125, servo_position3min=68;// int8_t const servo_position4max = 129, servo_position4min=108;// 128 108 sei(); DDRD = 0B11000010; //   D2-D5  , D0  RX, D1  TX, D6 D7   3  4 PORTD = 0B00111110; //     DDRB |=(1<<0)|(1<<1);//         PORTB &=(~1<<0)|(~1<<1); UCSR0A=0;// UART UCSR0B=0b10010000; UCSR0C=0b00000110; UBRR0L=103;//  115200 UBRR0H=0; timer_counter0_1_2_INIT(); servo_reset(); PORTB |=(1<<5); while (1) { switch (State_of_keyboard) { case 1://   1 PD0(PB0) number_servo=1; servo_positionmin=servo_position1min; servo_positionmax=servo_position1max; break; case 2: //   1 PD0(PB0) number_servo=1; servo_positionmin=servo_position1min; servo_positionmax=servo_position1max; break; case 5: number_servo=2; //   2 PD1(PB1) servo_positionmin=servo_position2min; servo_positionmax=servo_position2max; break; case 6: number_servo=2; //   2 PD1(PB1) servo_positionmin=servo_position2min; servo_positionmax=servo_position2max; break; case 7: number_servo=3;//   3 PD6 servo_positionmin=servo_position3min; servo_positionmax=servo_position3max; break; case 8: number_servo=3;//   3 PD6 servo_positionmin=servo_position3min; servo_positionmax=servo_position3max; break; case 3: number_servo=4; //   4 PD7 servo_positionmin=servo_position4min; servo_positionmax=servo_position4max; break;//  case 4: number_servo=4; //   4 PD7 servo_positionmin=servo_position4min; servo_positionmax=servo_position4max; break;//  // c    - ,       4-  //        ,         } if(State_of_keyboard==1||State_of_keyboard==3||State_of_keyboard==5||State_of_keyboard==7) { servo_go(1,servo_positionmax,servo_positionmin);// } if(State_of_keyboard==2||State_of_keyboard==4||State_of_keyboard==6||State_of_keyboard==8) //     { servo_go(2,servo_positionmax,servo_positionmin);// } time_delay(20); } } 



Das Wesentliche des Programms geht aus dem Text und den Kommentaren hervor. Wir verwenden einen T1-Zähler für einen beispielhaften Zeitraum von 20 ms und T0, T2-Zähler für die Ausgabe von PWM-Signalen an vier Leitungen des E / A-Ports, da jeder dieser beiden Zähler auf zwei Geräten arbeiten kann.
Das Programm setzt die Anfangspositionen der Servos durch Laden der Zählregister OCR0A, OCR0B, OCR2A, OCR2B. Es werden auch Einschränkungskonstanten eingeführt, da wir nicht immer eine 180-Grad-Spanne benötigen. Durch Unterbrechung von UART fängt das Programm die von ESP8266 gesendete Nummer (von 1 bis 8) ab und übersetzt sie in einen Befehl für das entsprechende Servo. Es gibt vier Laufwerke, die jeweils in zwei Richtungen arbeiten, sodass Ganzzahlen von eins bis acht ausreichen. Sobald die Nummer ausgewählt ist, wird der Inhalt der obigen Zählerregister entweder inkrementiert oder dekrementiert, wodurch sich das Tastverhältnis des Steuerimpulses und der Drehwinkel des ausgewählten Servoantriebs ändern. Die von uns nicht ausgewählten Antriebe behalten den alten Wert des Drehwinkels bei (da sich der Inhalt der entsprechenden Register, obwohl sie aktualisiert wurden, nicht geändert haben) und halten den mechanischen Arm weiterhin in derselben Position.

Jetzt müssen wir nur noch ein Steuerungsprogramm schreiben, entschuldigen Sie die Tautalogie, um eine mechanische Hand direkt vom Computer über WI-FI zu steuern.
Der Code ist ebenfalls in JAVA geschrieben, aber ein wenig geadelt. Es gab eine grafische Benutzeroberfläche und die Möglichkeit, die Portnummern und die Netzwerkadresse des ESP8266 zu bearbeiten.



Was dort passiert, ist aus dem Fenster ersichtlich. Ich stelle den Text des Programms hier nicht zur Verfügung (er ist auf Github verfügbar), und zwar aus folgendem Grund: Ungefähr 95% seines Volumens entfallen auf die Fenstererstellung und Signalverarbeitung über die Tastatur. Aber das Wesentliche ist das gleiche wie im vorherigen Programm auf JAVA. Es wird ein Client erstellt, nur UDP, der abhängig von der gedrückten Taste eine Nummer von 1 bis 8 an die angegebene Adresse am angegebenen Port sendet.
Oder Sie können die ausführbare Datei sofort von hier erhalten . Für 64-Bit-Computer mit Windows. Auch eine installierte JAVA-Umgebung ist nicht erforderlich. Alles wurde bereits in 178 MB verschoben.

Also wurde der mechanische Stift zusammengebaut, getestet und seinem Bruder zu seinem Jubiläum präsentiert. Kann Plastikstapel mit Wodka über Skype aus einer anderen Stadt abholen. Obwohl für den mechanischen Arm von Howard Volovitsa aus der Serie "The Big Bang Theory", ist sie noch weit weg.

In den folgenden Artikeln (falls jemand interessiert ist) können wir es jedoch von einem Mobiltelefon aus verwalten, dasselbe mit einem Allrad-Roboterwagen tun und die Uhrzeit in elektronischen Uhren von Uhrenservern im Internet aktualisieren. Dann legen wir das alte Smartphone auf den Wagen und fahren das Video mit Mustererkennung zum neuronalen Netzwerk und geben die Steuersignale an die Motoren zurück, oh, etwas trägt mich schon ...

Und das alles mit dem schönen ESP8266.
Ich würde mich freuen, wenn jemand den Artikel interessant finden würde.

[1] Pinbelegung und Spezifikationen von ESP8266
[2] Anschließen des ESP8266. Schnellstart.
[3] NodeMCU-Firmware-Update über die Cloud
[4] NODE MCU PyFlasher
[5] ESPlorer - IDE für ESP8266
[6] C Programmierung für AVR
[7] Artikelübersicht - „Programmieren von Mikrocontrollern in C-Sprache“
[8] Beschreibung der NodeMCU-API
[9] Lua-Referenz
[10] Lua-Skripte und -Module
[11] IntelliJ IDEA
[12] Laden Sie Java jetzt auf Ihren Desktop-Computer herunter!
[13] Atmel Studio

Source: https://habr.com/ru/post/de406559/


All Articles