Programando un Arduino remotamente con el módulo ESP8266

IMG_20141108_002938

Una de mis viejas aspiraciones cuando construyo robots es poder programarlos sin tener que recogerlos, enchufarles un cable usb o un programador ICSP y volverlos a dejar en su sitio.

En este artículo explicaré cómo con un Arduino UNO y un módulo ESP8266 (llamado WI07C) se puede programar un sketch de Arduino en la placa sin tener que estar cerca de esta, todo mediante wifi y sockets tcp/ip.

Descripción general

Como se puede ver en el vídeo tenemos un Arduino UNO conectado a un display HD44780 y a un módulo wifi ESP8266. Arduino envía cadenas hello world al servidor sockettest que está escuchando por el puerto 49000. Se modifica el código Arduino en el IDE poniendo hello folks, se compila y el fichero .hex generado (se puede ver donde está activandolo en Archivo/Preferencias/compilación) se copia a la carpeta del programa en python. Cuando Arduino recibe un comando reboot se conecta al servidor python por el puerto 50000 entrando en modo de datos, acto seguido se reinicia y empieza la programación remota de Arduino. El proceso se reliza dos veces en el vídeo.

Lo que se aprovecha es el método que tiene Arduino para programarse, ya que usando el puerto serie después de un reset se puede programar un Arduino si se sigue el protocolo STK500 implementado en el bootloader.

Esquema de conexiones

esquema

El Arduino y el display HD44780 se alimentan a 5 voltios, el módulo ESP8266 se alimenta a 3,3 voltios. Como el pin rx del módulo wifi sólo puede funcionar a 3,3V se usa un diodo zener de 3,3 voltios junto con una resistencia de 100 Ω.

En las placas Arduino, el pin de reset del microcontrolador está conectado a una resistencia y esta a su vez está conectada a VCC, con lo que para el microcontrolador el pin está a nivel alto. Cuando se pulsa el botón de reset lo que se hace es derivar la corriente hacia masa (ya que el bóton está conectado a esta) y el microcontrolador, al estar a nivel bajo el pin de reset, realiza un reseteo. Cuando el microcontrolador arranca, todos los pines están configurados como entradas (alta impedancia) y por eso no le afecta que el pin de reset esté conectado directamente al pin 12. Si se configura el pin 12 como salida y luego se conecta a masa (nivel bajo o LOW) se provoca el mismo efecto que si se pulsase el botón de reset, además, al existir la resistencia anteriormente mencionada, no hay que preocuparse de que se produzca un cortocircuito al unir VCC con masa (GND).

Sketch de Arduino

Lo que hace el skecth de Arduino es:

  • Inicializa el puerto serie a 115200 bps.
  • Elimina los caracteres que hubiera en el buffer de entrada del puerto serie.
  • Inicializa el módulo wifi en modo estación, lo resetea, se conecta al punto de acceso normal, configura las conexiones como simples en modo normal y se conecta al servidor normal por el puerto 49000. Si hubiese algún fallo en algún punto pararía la ejecución y lo indicaría en el display.
  • Envia una cadena de texto y un número consecutivo tanto al servidor como al display. Si hubiese algún fallo en algún punto pararía la ejecución y lo indicaría en el display.
  • Si entre el envío de las cadenas de texto se recibe una cadena reboot entonces resetea el módulo, se conecta al punto de acceso bootloader, configura las conexiones como simples en modo normal y se conecta al servidor de programación por el puerto 50000. Acto seguido envía una cadena hello y espera a recibir una cadena welcome, si ha sido así, entonces envía el nombre del fichero .hex con el que quiere ser programado el Arduino y espera a recibir una cadena ok, momento en el cual configura el pin 12 como salida y lo pone a nivel bajo, conectándolo a masa y provocando un reset en el Arduino. Si hubiese algún fallo en algún punto pararía la ejecución y lo indicaría en el display.

Aquí cabe destacar cómo funciona el sistema de reseteo: Justo después del reseteo, el pin de TX de Arduino está a nivel bajo durante un tiempo, lo que provoca que el módulo wifi vea eso como un byte 0, que enviará a través de la conexión TCP/IP. El servidor de programación aprovechará esta circunstancia para saber cuando ha empezado el reseteo e iniciar el protocolo STK500. El bootloader de Arduino entra en acción después del reseteo y espera a recibir ordenes del protocolo STK500, si las recibe actúa en consecuencia, si no, ejecuta el programa principal.

Servidor de programación

Lo que hace el servidor de programación es:

  • Crea un socket que escuche por el puerto 50000
  • Cuando un cliente de conecta espera a la cadena hello, si la recibe reponde con una cadena welcome.
  • Espera a que el cliente le envíe un nombre de fichero .hex. Trata de abrir el fichero en el mismo directorio y lo lee interpretando todas las líneas para guardar los datos del programa en memoria. Si todo va bien envía una cadena ok.
  • Espera a recibir el byte 0, y cuando lo recibe empieza el protocolo STK500 para comunicarse con el bootloader de Arduino y programarlo. Básicamente lo que hace es entrar en modo de programación, indicarle a que páginas quiere acceder y enviar los datos de cada página, así hasta que ha enviado todos los datos del programa, después sale del modo de programación cerrando la conexión

Aquí cabe destacar que cuando se cierra la conexión TCP/IP con el cliente (ya sea por un error o porque el proceso de programación ya ha terminado), el módulo ESP8266 sale del modo de datos automáticamente y no es necesario que el nuevo sketch tenga que enviarle la cadena de escape +++ para poder entrar otra vez en modo de comandos.

Conclusiones

Espero que esto os sirva para que en vuestros proyectos podais programar remotamente vuestros Arduinos a través de una red local o Internet.

Habría que tener en cuenta que el proceso de actualización puede quedarse a medias si la conexión a la red wifi es lenta y provocaría que el programa no se ejecutara correctamente. Así que que una mejora sería acoplar un chip aparte que reprogramase el Arduino por ICSP con un programa preestablecido en caso de que detectase que la programación no fue finalizada correctamente.

Comentar que la versión de firmware del módulo ESP8266 que he usado es la 0019000902 y el Arduino es un UNO. Ambos funcionan a 115200 bps, pero si quisierais utilizar otras velocidades (en otros Arduinos el bootloader configura el puerto serie a 19200 o 57600 bps) habría que cargar un firmware que lo permitiese, como por ejemplo la versión 0.922 de electrodragon y su comando AT+CIOBAUD.

68 comentarios en “Programando un Arduino remotamente con el módulo ESP8266

  1. carlo

    Great idea man!
    I would like to point out that the zener diode is indispensable to make it work, not only because of level translation, but also because no zero byte is sent after reset without it..
    I’ve rewritten your script in c++ under Qt, with a GUI that let choose the hex file to upload, directly searching in favourite paths..
    My devices have a simple web interface with a server running on port 80, so I send the /upgrade command followed by the IP address and port of the progamming server. The device than reboots and connects to the server to be programmed.. Unfortunetely I’m experiensing some troubles during programming, which usually stops after 8-9 pages. So if I upload the blink sketch everything works because it’s about 5 pages, but for bigger sketch it fails, probably due to sync problems, because I receive no ack.
    If you are interested we can work on togheter..

    Responder
    1. carlo

      Trying to figure out the cause of my problem, I found that it depends on the responsivity of the router. In my network the delay is too high, whereas in my office it works pretty well with every kind of sketch… Again, nice job! I learned many things with your script, thank you..

      Responder
      1. admin Autor

        That is my problem too. I have a VPS where the python script is running and due to net delays, sometimes the arduino bootloader runs the main program while it is not loaded at all. I’m working in an alternative circuit to store the downloaded sketch in a 24AA1025 eeprom memory and then program the arduino with ICSP from a PIC12F1822.

        Responder
        1. carlo

          Your alternative solution seems similar to Moteino boards. I was minding to modify the esp8266 firmware instead to limit the need of new hardware. The sketch upload procedure may be driven by it: we can use it to parse the upload command and internally switch to programming mode. We can also use the GPIO2 pin to reset arduino and implement a little buffer to avoid delay problems.. Next week I wil try to read v0922 firmware to figure out if it’s feasible..

          Responder
          1. admin Autor

            Well, it is a bit different because the boot loader is the original from arduino. The Arduino sketch should program the eeprom by I2c with the new sketch and then the pic reads the eeprom and program the AVR by isp.

            Let me know your advances about the modified esp8266 firmware to program the Arduino. It seems interesting.

  2. Pingback: Módulo WIFI ESP8266 - El blog de Marcos

  3. Newton Isaak

    Hello, very interesting article!
    Could you write english version, cause did not understand details.
    Thank you.

    Responder
  4. eduardo mendez

    hola , alguien sabe si es posible controlar el arduino uno remotamente con el módulo ESP8266 conectado por wifi a intternet para activar un contacto seco ?

    Responder
    1. willar

      Hola Eduardo si es posible controlar tu arduino por el modulo esp solo tienes que colocar el modulo al arduino como aparecen en internet rx/ tx tx/rx y listo envias tu cadena es cual programa el chip de arduino para que la reciba y utilizar los comandos AT+CIMUX = 1 & Chr$(13) luego AT+CIPSERVER=1,80 & Chr$(13) y AT+CIPSEND=0,30 & Chr$(13) donde el el 30 es un ejemplo de 30 caracteres que estas enviando.

      Responder
  5. ikin

    Muchas gracias por compartir esta información, es muy útil. Lo he probado con un UNO y funciona de maravilla, mientras que con un Pro Mini no hay modo. ¿Qué puede ser?

    Responder
    1. admin Autor

      Me imagino que por la velocidad del puerto serie a la que funciona el bootloader, en el arduino UNO es de 115200, en el mini pro es de 57600, por lo que para que funcione sólo deberías cambiar en el sketch de arduino la velocidad del puerto serie a 57600 y configurarla también a esa velocidad en el módulo esp8266.

      Responder
      1. ikin

        Gracias por tan pronta respuesta. Lo he probado a 115200 y 57600 con el mismo resultado 🙁

        El mensaje que veo desde el equipo donde corre el programa python es este:

        Connection to Arduino bootloader: failed

        Es decir, parece que va bien la secuencia hello-wellcome-programa-ok-reincio y entra en la función «program_process», pero ahí dentro es donde falla.

        Responder
  6. ikin

    Ole ole, solucionado 🙂 Funciona efectivamente a 57600. El problema, era además de ese que el pin RST del Arduino no estaba bien soldado y cuando lo ponía a GND desde la salida 12 en realidad no se estaba produciendo el reinicio. Tras muchas pruebas, me di cuenta que era ese el problema….quitar vieja soldadura, soldadura nueva, y ya funciona 🙂

    Muchísimas gracias 🙂

    Responder
    1. Francesco

      Can you help me ?
      my circuit not function…
      i have problem with bootloader…
      i can upload new code…
      can you explain me the circuit and the correct code please?
      thanks

      Responder
  7. Pingback: Programming an Arduino via WiFi with the ESP8266 -Use Arduino for Projects

  8. THULASI RAM

    Hi your blog is really good..
    help me out..
    I am using arduino uno for this one
    AT version:0.23.0.0(Apr 24 2015 21:11:01)
    SDK version:1.0.1
    my cmd terminal says

    Listen to connections
    192.168.1.xx connected
    Traceback (most recent call last):
    File «arduino_remote_program_wifi.py»,line 247. in
    main()
    File «arduino_remote_program_wifi.py»,line 231. in main
    if wait_for(cli,»hello»,5000)[0]:
    File «arduino_remote_program_wifi.py»,line 231. in wait_for
    received +=cli.recv(1)
    socket.error:[Errno 10054] An existing connection was forcibly closed by the remote host

    help me out..

    Responder
  9. Alexander

    Hi, i’m trying to use this solution, all steps are working correct except for entering bootloader. TX is expected to be low for a while, but it dont, so python server do not recieve \x00 and do not start updating firmware. How can i solve this?

    Please, help, your article is very helpful, just one more step.

    Responder
      1. hardika

        i have same problem, python server do not recieve \x00 .
        i try sending 0 byte if (send_command(«\x00», 2000, «ok», «error»)) . but still not working
        please give me a hit..

        Responder
      2. Francesco

        Hello i try to send a byte 0, but the program remain looped in
        listen to connection
        192.168.1.185 connected
        listen to connection
        192.168.1.185 connected
        listen to connection
        192.168.1.185 connected

        where is the problem ?
        Can you answer me please?
        THANKS

        Responder
  10. Victor

    Un post genial, me gustaria preguntarte una duda,
    ¿Este sistema de programación es compatible con el arduino Mega 2560?
    He leido por la web el arduino mega dispone de un bootloder que usa el protocolo STK500v2 y no el STK500(v1)

    Responder
    1. admin Autor

      Pues sinceramente no me he mirado la versión 2 del protocolo. La diferencia estriba en el acceso a bloques de memoria por encima de los 128K. Siempre puedes probar ya que si algo falla se puede reflashear el microcontrolador con su firmware original.

      Responder
      1. +91-9033533754

        Really interesting tutorial, and i really appreciate your work!
        Here I’m trying to up date my arduino uno firmware same manner as you described here, but instead of ESP 8266 I’m using RN171 WiFly module from roving network along with arduino.
        every thing is working grate, but the problem is after sending «Reboot» command from my server, arduino get reset and connect the python server having different port, but at the last moments I’m getting message from python script as «connect to arduino boot-loader failed!»
        then after I’d try with different baudrate like 9600, 19200 but nothing happen and getting same result!!!!
        any suggestions
        Regards
        Mahendra sondagar

        Responder
  11. Grant Pinkert

    Hey, I don’t know if you’re still paying attention to this, but I was just wondering if you could give me some advice? For some reason when the arduino is sending out the «hello guys» string, my server is not picking it up. I have checked and I am sure they are on the same IP and port. Also, how are you inputting where the hex file is saved? Are you sending that to your python server? Those are my two questions, thanks so much for doing this!

    Responder
    1. admin Autor

      The first question: If you follow the code as is, I guess you first received the «Hello world» successfully, however when you switch to the new firmware you didnt receive the «Hello guys», do you?. I think the firmware was not programmed correclty. Did the script python returns a «Program memory address: success» and «Leaving programming mode: success»?

      The second question: The arduino sketch send the .hex file it wants to be programmed (Arduino_remote_example.cpp.hex); that file, after compiling in arduino ide, is stored in the same directory than python script is located.

      Responder
      1. Grant Pinkert

        OK, so I got the rest of that stuff to work. However, once the arduino connects with the Python script, nothing happens. Any idea what could be causing the server to not start sending the new info, or the arduino not entering the bootloader? This is a picture of the issue: https://drive.google.com/file/d/0B1f4r8iM9x2QdlA2eS1XRUl4VkU/view?usp=sharing

        This is after I have told it to reboot and after it connected to the python server. Thanks for the help!

        Responder
      2. Grant

        I know I already asked you for help, but I figured out wwhat the problem was. Unfortunately, I have a few other questions.

        1- Do I send the zero byte before the reset, or after the reset?
        2- What does pin 13 have to do with anything? I can’t see what it is being used for on the code or the schematic.
        3- when I pull pin 12 low, that triggers the reset, correct?

        Thanks so much!

        Responder
  12. scg

    Hey, so I am having a problem with entering the bootloader. Once I reset the board, it does not stay in the bootloader, and just keeps going in the loop without updating the board. I tried sending a zero byte, but that didn’t work either, I still kept timing out. Any suggestions for how to enter the bootloader?

    I was doing this for the zero byte:

    String zero = «\x00»;
    Serial.println(«AT+CIPSEND=1»);
    Serial.print(zero);
    pinMode ( 12 , OUTPUT ) ;
    digitalWrite ( 12 , LOW ) ;
    for ( ; ; ) ;

    Like I said, it does not enter the bootloader, and it does not echo anything back after the reset, it just keeps going with the original code. Thanks for the help!

    Responder
      1. ed

        OMG I HAVE IT WORKING ON MY UNO AND MEGA it took a few days but omg i just flashed the Blink sketch to it 🙂 oh happy days,

        https://lh5.googleusercontent.com/k4IbWjQk8LpKXx6EPMdNOUgi7qYmade7e22k5a_CipBzBuGTWXXi8mO3VzjEbtBJ7hKkH0UVZDOik4v8IF44=w2560-h1260-rw
        just remove the if wait for \x00 and call program_process right after clie.send(«ok»).

        and note the python code is created to run on python 2.7, you also need the program, socket test and set that to 4900, and have the python app running , running it in idle is fine.
        im forgetting something else i think but just pay around with the code.

        Responder
        1. SS

          I have tried several times with mega and uno but the connection to Arduino bootloader is always failed. I am unable to resolve the problems.Can u help please.
          Thanks for advance

          Responder
  13. Iker

    Una preguntilla, porque en el codigo de arduino pones a nivel alto y bajo el pin 13? No veo que este conectado a ningun sitio ni que sirva para nada. Tambien veo que lo has declarado como OUTPUT.
    Gracias.

    Responder
    1. admin Autor

      Como el pin 13 está asociado al led que incorpora Arduino, lo utilizo para saber que está encendido y esperando ordenes y cuando lo apago es porque ha habido algún fallo.

      Responder
      1. Iker

        Gracias, si, ya me di cuenta, ahora lo que me pasa es que no consigo que entre en modo programacion, el script the python me dice que no se puedo subir el bootloader.
        Sin embago el arduino si que e resetea, pero vuelve a empezar desde el principio, no entra a ningun modo de programacion. Alguna idea?
        Gracias!

        Responder
        1. Iker

          En vez de con un Arduino nano he probado con el UNO y funciona todo correctamente hasta el momenot de poner el pin 12 a LOW, el arduino se reinicia y no entra el modo de programacion. En el script pone Read hex file… y a continuacion se pone a esccuchar para volver a conectarse al arduino puesto que este se ha desconectado.
          No aparece nada de BOOTLOADER en el script. He probado a 57600bps y a 115200. Creo que el problema esta en como acceder al modo de programacion del arduino. Si declaro el pin que va al reset com OUTPUT y lo pongo a low se reinicia siempre.
          Alguna idea? Muchas gracias!

          Responder
          1. admin Autor

            Tengo que investigarlo, porque a más gente le está pasando. La clave es que se envía un 0 por el puerto serie cuando el arduino UNO se reinicia y este es el que toma el script en python para saber que se ha reiniciado y enviarle los datos del nuevo código.

          2. admin Autor

            He estado haciendo pruebas y he añadido modificaciones, a ver si te funcionan: https://www.sistemasorp.es/blog/programar_arduino_por_wifi.zip . Ahora puedes poner en los defines del sketch de Arduino la velocidad que funciona el puerto serie en condiciones normales (configurando el esp8266 en consecuencia) y la velocidad del puerto serie que utiliza el bootloader del arduino (configurando el esp8266 en consecuencia). En el script del servidor he cambiado el modo de saber cuando está el arduino en modo bootloader simplemente haciendo que envíe la secuencia de inicio hasta 5 veces antes de abortar. Si lo pruebas te agradecería que pusieras aquí tu experiencia para que pueda servir a otros. Gracias.

  14. paai

    Hi,
    not having a LCD screen handy, I made the following changes:
    first I included SoftwareSerial:

    #include
    SoftwareSerial esp8266(A1,A0);

    Then I replaced the Serial-calls by esp8266-calls and the lcd-calls by Serial calls, so that the output was displayed in the serial window of the Arduino IDE.
    Speed of the esp8266 put to 9600. I have the python program without changes running as root.

    The first AT commands are executed, and it connects to the SSID but the program stops at

    AT+CIPSTART=»TCP»,»192.168.178.15″,49000
    0,CONNECT Failed

    and now I am stuck…

    Paai

    Responder
  15. paai

    I see that the python program listens on port 50000, but I have no idea which program listens on port 49000.
    It would really help if you would write down the steps to take and the programs to start. Spanish would not be a problem, thanks to Google translate.

    Normally I would use a esp8266 based board, which has a reliable OTA system. But the esp8266 does only have seven or eight IO pins and I need more.

    Another thing, if you want people to really understand what is happening, a video is perhaps the worst vehicle to transmit such concepts. I really get frustrated that all kinds of information that can be read in three minutes, are hidden in hour long videos.

    Responder
    1. admin Autor

      Hi

      For the example there are two servers running on the PC:
      -The first is serversocket and it is listening on port 49000. It only receives the hello guys! string sent from arduino+esp8266. From this program you can send the text reboot, this command makes the arduino to:
      –connect to port 50000 and send the hello string.
      –to wait for the welcome response
      –to send the name of the .hex file to program
      –to wait for the ok string
      –to perform a reset to execute the bootloader.
      -The second is the python script and it is listening on port 50000:
      –it waits for the hello string
      –it sends welcome
      –it waits for the name of the .hex file
      –it sends the ok string
      –it waits for the byte 0 to begin the programming (however most people told me that the 0 byte is not received, so you can avoid it)

      I hope this is useful for you, I would like to read your success so don’t hesitate to ask more questions.

      The video is only a demo for what is written in the article. In the next months I’m evaluating to translate it to english to widespread the content. By the moment google translate as you mentioned, or better, deepl cant help you with the translation.

      Responder
  16. Hans Paijmans

    Thank you for your kind words. Not everybody has the time or patience to keep answering questions. If I can help you with translations or tutorials I will gladly do so.

    At the moment I tamed the esp8266 (always a temperamental component), using the SoftwareSerial library and got the UNO to attach to the socketserver, can type the ‘reboot’ and it will reboot. However, the UNO reboots in the main program, not the loader. Yes, in the Arduino IDE, I have the Atmel stk500 enabled and the new hex file is in the directory with the Python program. Immediatly before the reboot, I send a 0x00 byte to the esp8266, but that does not seem to make a difference.

    As I said, my debug output goes to the Serial console and the esp8266 is connected to Softwareserial on pin 7 and 8, not to the hardware RX and TX. Would that be an issue?

    My project is to make a Dirt Devil robot somewhat smarter: see http://www.paijmans.net/Dirtdevil/dirtdevil.html
    The WEMOS offers easy OTA updates and lots of memory, but not enough IO pins. The UNO/ESP8266 combo on the other hand has lots of pins, but no OTA and that is where you come in.

    Responder
  17. SS

    Hi,
    I followed your steps and codes for both Arduino and python but the bootloader didn’t work.
    I tried several times but in vain.The execution of the program access in python is failed.
    I tried also your new code but it’s the same.
    Please can you help me cause i need it to work.I am doing an internship actually and i am using your method to program a robot wirelessly.
    Thanks
    best regards

    Responder
  18. SS

    I’m facing a problem with Bootloader.My python code failed to connect with the arduino bootloader and i get a failed message when I run the python script.
    Please help me

    Responder
  19. Chami

    Buenos días! buscando y buscando he llegado hasta aquí, me ha parecido muy interesante el artículo!!
    Quería aclarar una duda, corrígeme si me equivoco, pero me parece que estoy en lo cierto por lo que he leído… Si se hace un programa que envíe/reciba los parámetros por socket a cualquier puerto (el que hallamos configurado en arduino) podría prescindir de Python, ya que lo único que hace es ser el intermediario para enviar al puerto, cierto? es decir, con esto se puede hacer un bootloader de arduino por wifi para windows evitando el python, no?
    Muchas gracias y repito muy interesante!

    Responder
    1. admin Autor

      Hola.

      El propio programa de python es el encargado de enviar el firmware al bootloader de arduino siguiendo las directrices el protocolo stk500, no es un mero intermediario, interpreta el fichero .hex y lo traduce en comandos del protocolo stk500.

      Responder
      1. Diego

        Buenísimo el tutorial, muchas gracias por compartirlo!

        Un par de comentarios:
        – Luego del comando AT+RST el arduino no detecta el ready, y tira error de timeout. Saqué ese comando del if, dejé que se ejecute pero sin hacer que finalice ahi el programa al tirar timeout.
        – Intenté pasar el programa en python a python 3 pero luego de inociar el bootloader tuve problemas en la detección de \x14\x10. El error que me daba era que no podia identificar el primer y ultimo byte antes de esos 2. Luego de varias horas abandoné la idea y lo usé con oython 2.7, pero quería saber si lo has mudado a python 3.
        – Utilicé una arduino nano, en un sketch bastante grande y no tuve ningún problema.

        Responder

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *