AVR goes ESP mit MicroPython - Teil 5 - AZ-Delivery

In the first sequence of this project series, I regretted that the Nano V3 is not WLAN capable. To change this, first of all, an ESP32 and an ESP8266 were wirelessly connected via NRF24L01 modules. This finally resulted in a MicroPython module, nRF24simple.py. In the second episode, I brought the Nano to talk about NRF24L01 modules with the ESP8266 as well. Episode 3 describes the coupling of a Nano V3 to the minimalistic ESP8266-01 via an I2C bus. This was the basis to connect the ATmega328 microcontroller to the WLAN. The program esp_i2c_master.py compiled functions, which send commands to the Nano could send, read and write parallel ports, output PWM signals, and measure voltages. Sequence 4 brought the module arduino_i2c.py, in which the previous functions were integrated, improved, and supplemented by additional methods.

Today we will extend the project with the WLAN interface. So welcome to the series

MicroPython on the ESP32 and ESP8266

today

AVR goes ESP with MicropPython (Part 5) - WLAN

To make a device WLAN capable, two steps beyond the "economy class" are required. First, the connection to an access point has to be established. Alternatively, an ESP8266 or an ESP32 can be an access point itself. This has advantages and disadvantages. We will come to that later.

The fact that a device has logged into a network is not enough. We want to access the infrastructure of the device. This requires a client-server structure. In our case, the ESP8266-01 will be the server and mediator to the AVR, and the Nano will play. As clients, we will use a web browser as the first approach. The server will generate a web page with forms that we fill in and send back to the server.

Figure 1: The steps to the WLAN class

Image 1: The steps to the WLAN class

Alternatively, in a second approach, we can build a UDP server that we can radio, for example, with a cell phone app.

If you decide to use your own access point on the ESP8266-01, this has the advantage that you do not need a local network structure with a router. You then set one up yourself. A disadvantage is that there is no internet access. Another disadvantage is that the cell phone must first register on the ESP8266-01 access point.

Hardware

The hardware is still the same as in the fourth episode. I don't include a PC or cell phone, although it won't work without them.

1

Nano V3.0 CH340 chip + Breadboard adapter for ESP-01 or

1

ESP8266 01 esp-01 Wlan WiFi module with breadboard adapter

1

KY-009 RGB LED SMD module

2

KY-004 Button module

1

Logic Level Converter TXS0108E 8 channel (*)

1

MB-102 Breadboard plug-in board with 830 contacts

1

FT232-AZ USB to TTL Serial Adapter for 3.3V and 5V

1

4x4 Matrix Array Keypad Keyboard or

4x4 Matrix Keypad Keyboard

1

LED (blue or green, matching the 2.2kΩ resistor)

3

Resistor 2,2kΩ

1

Resistor 560Ω

1

Resistor 10kΩ

various

Jumper cable

2

suitable USB cables

1

battery 4,5V or 5V wall adapter

(*) alternative possibility further down in the text

Nothing has changed in the circuit either.

Figure 2: IO ports and PWM LED control

Figure 2: IO ports and PWM LED control

I don't want to miss to point out again that the ESP8266-01 can't stand voltages higher than 3.6V on its ports. To prevent it from going into nirvana, the jumper on the FTDI adapter must absolutely be jumpered to 3.3V and the I2C lines must go through a level shifter that can convert 5V to 3.3V and vice versa. An equivalent circuit for this, with single transistors, can be found in Sequence 4. When later in the production system the Nano V3 is also operated with 3.3V, the level converter can be omitted.

Figure 3: Nano V3 as I2C slave - application circuit for 3.3V

Image 3: Nano V3 as I2C slave - application circuit for 3.3V

In this episode, the ports of the ATMega328 microcontroller should again be able to be addressed in their respective full width. For the implementation of this intention, we use the class ARDINO from the module arduino_i2c.py. Here again, is the summary of the designations of ATMEL AVR and the Nano V3.

AVR Port

PD0

RXD

PD1

TXD

PD2

PD3

PD4

PD5

PD6

PD7

Nano V3

D0

RXD

D1

TXD

D2

D3

D4

D5

D6

D7


AVR Port

PB0

PB1

PB2

PB3

PB4

PB5

Nano V3

D8

D9

D10

D11

D12

D13


AVR Port

PC0

PC1

PC2

PC3

PC4

PC5



Nano V3

A0

A1

A2

A3

A4

A5

A6*

A7*

* Only TQFP variant and only analog inputs there as well

Each port group has three registers, an output register called PORT, an input register PIN and a data direction register DDR. Our goal is to describe and query all three types in full width. Also single, specific bit operations should be possible. Commands and data travel over the I2C bus, the ESP8266-01 is the client, i.e. the master, which sends the SCL clocks.

Group C dances a little bit out of line. The C-pins serve their function as digital ports and also as inputs of the analog mutiplexer which in turn feeds the signals to the ADC (analog-to-digital converter). Today we will use the input A3 in this sense. We connect it to the operating voltage of the ESP8266-01. A4 and A5, aka PC4 and PC5, connect to the I2C hardware. On the Nano V3 the functions of this interface are realized by hardware, the I2C interface of the ESP8266-01 is a software solution.

We connect the RGB LED to IO lines of port C via three resistors. The pins PC0 (blue), PC1 (green), and PC2 (red) are used for this purpose. The single LED at port PD3 is connected to a PWM output and can therefore be dimmed.

The software

For flashing and programming the ESP32:

Thonny or

µPyCraft

For the Nano V3:

Arduino IDE

arduino_as_slave.ino For communication with the ESP8266-01 and for processing commands

Used firmware for the ESP8266/ESP32:

MicropythonFirmware

Please choose a stable version (ESP8266-01 with 1MB Version 1.18 Status: 05.03.2022)

The MicroPython programs for the project:

arduino_i2c.py: Module with the class ARDUINO

ardutest.py : Test program for the module

arduino_goes_wlan_TCP.py: For communication with the Arduino via browser

arduino_goes_wlan_UDP.py: For communication with the Arduino via mobile app

Other software:

Packet transmitter Download page

Packet transmitter Windows Install Version

Packet Sender Windows Portable

Packet Sender Linux

MicroPython - Language - Modules and Programs

For the installation of Thonny you find here a detailed manual (English version). In it there is also a description of how the MicropythonFirmware (as of 02/05/2022) on the ESP chip. burned is burned.

MicroPython is an interpreter language. The main difference to the Arduino IDE, where you always and only flash whole programs, is that you only have to flash the MicroPython firmware once at the beginning to the ESP32 so that the controller understands MicroPython instructions. You can use Thonny, µPyCraft, or esptool.py to do this. For Thonny, I have described the process described here 

At this point a few words about flashing the ESP8266-01. Unlike its bigger siblings, the ESP8266-01 does not have an automatic flashing function on board. Manual work is required here.

The flash process is divided into two parts, first erase flash memory and second transfer firmware. The following list is an excerpt from the Description of the flash process:

    a) Complete the preparations in Thonny
    b) Press the reset and flash button
    c) Start the flash process in Thonny
    d) Release the reset key, and hold the flash key until progress is displayed
    e) Release flash key
    f) Wait until access to the COM interface is reported again.
    g) Then go through points b) to f) again and
    h) Finally, close the installer window and exit the options with OK.

    Once the firmware is flashed, you can casually talk to your controller one-on-one, test individual commands, and immediately see the response without having to compile and transfer an entire program first. In fact, that's what bothers me about the Arduino IDE. You simply save an enormous amount of time if you can do simple tests of the syntax and the hardware up to trying out and refining functions and whole program parts via the command line in advance before you knit a program out of it. For this purpose, I also like to create small test programs from time to time. As a kind of macro, they summarize recurring commands. From such program fragments sometimes whole applications are developed.

    Autostart

    If you want the program to start autonomously when the controller is switched on, copy the program text into a newly created blank file. Save this file as boot.py in the workspace and upload it to the ESP chip. The program will start automatically at the next reset or power-on.

    Test programs

    Manually, programs are started from the current editor window in the Thonny IDE via the F5 key. This is faster than clicking on the Start button, or via the menu Run. Only the modules used in the program must be in the flash of the ESP32.

    In between times Arduino IDE again?

    If you want to use the controller together with the Arduino IDE again later, simply flash the program in the usual way. However, the ESP32/ESP8266 will then have forgotten that it ever spoke MicroPython. Conversely, any Espressif chip that contains a compiled program from the Arduino IDE or the AT firmware or LUA or ... can easily be flashed with the MicroPython firmware. The process is always here described.

    The XMitter program and the server construction kit

    For the WLAN access of my ESPs, I put together a set of rudimentary Python scripts in a directory.

    accesspoint.py, multiple_WLANs.py, single_WLAN.py, TCP webserver.py, UDP server_blank.py, UDP server.py

    They have half the character of modules but are still not used as such because they need certain changes or adaptations from case to case.

    Let's start with the compilation of a program. In the beginning, we will use the test program from episode 4 as a basis, ardutest.py which we immediately extend a little bit. From the module machine, we need the operation of the I2C interface and Pin. From arduino_i2c we get everything, including the constants defined there into our namespace (aka Scope).

     # arduino_goes_wlan_TCP.py
     # Rev1.1 - 02.03.2022
     from machine import SoftI2C, Pin
     from arduino_i2c import *
     
     SCL=Pin(2)
     SDA=Pin(0)
     i2c=SoftI2C(scl=SCL, sda=SDA, freq=100000)
     a=ARDUINO(i2c,u0=4.73)
     a.writeIO(DDR,C,7)
     rt=2; gn=1; bl=0

    The choice of I2C pins is not very large: 0 and 2, there are only two. With this we instantiate the interface and right after that an ARDUINO object called a. We use this immediately to program the bits DDRC0, DDRC1, and DDRC2 of PORTC to output. In the following line we assign the colors.

    The WLAN access

    My module single_WLAN.py has to accept slight changes because I can't run an RGB LED directly on the ESP8266-01 due to chronic pin shortage. What is left looks like this. We start with a few more imports. The rest has plenty of comments, so further elaboration is probably not necessary. Just one thing, don't forget your own credentials to access the WLAN router at my SSID and myPass to enter.

     # Module for contact attempts to a WLAN router
     import os,sys       # System and file instructions
     from machine import reset
     from time import sleep, ticks_ms
     import network
     
     
     def hexMac(byteMac):
         """
      The hexMAC function takes the MAC address in bytecode
      and forms a string from it for the return
        """
         macString =""
         for i in range(0,len(byteMac)):    
             macString += hex(byteMac[i])[2:]
             if i <len(byteMac)-1 :          
                 macString +="-"
         return macString
     
     # ***************** Connect to WLAN **********************
     connectStatus = {
         0: "STAT_IDLE",
         1: "STAT_CONNECTING",
         5: "STAT_GOT_IP",
         2:  "STAT_WRONG_PASSWORD",
         3:  "NO AP FOUND",
         4:  "STAT_CONNECT_FAIL",
        }
     
     mySSID = 'Here_goes_your_SSID'
     myPass = 'Here_goes_your_Password'
     
     # Be sure to turn off the AP interface
     nac=network.WLAN(network.AP_IF)
     nac.active(False)
     nac=None
     
     # We create a network interface instance
     # We need the STATION interface
     nic = network.WLAN(network.STA_IF)
     nic.active(False) # reset first
     
     # Query the MAC address to enter in the router,
     # so that the release of the access can take place
     MAC = nic.config('mac')  
     myID=hexMac(MAC)
     print("Client ID",myID)
     
     # We activate the network interface
     nic.active(True)
     
     # Establish the connection
     # We set a static IP address
     nic.ifconfig(("10.0.1.91","255.255.255.0","10.0.1.20","10.0.1.100"))
     
     # Log on to the WLAN router
     nic.connect(mySSID, myPass)  
     
     if not nic.isconnected():
         # wait until the connection to the access point is established
         while not nic.isconnected():
             print("{}.".format(nic.status()),end='')
             sleep(1) # we ask in seconds rhythm
         
     # if connected, show connection status & config data
     print("\nConnectionStatus: ",connectStatus[nic.status()])
     if nic.isconnected():
         # Was the configuration successful? Control
         STAconf = nic.ifconfig()
         print("STA-IP:\t\t",STAconf[0],"\nSTA-NETMASK:\t",\
               STAconf[1],"\nSTA-GATEWAY:\t",STAconf[2] ,sep='')

    One more line is important:

    nic.ifconfig(("10.0.1.91","255.255.255.0","10.0.1.20","10.0.1.100"))

    The addresses specified here must of course correspond to your local network. Assuming your router has the IP 192.168.12.1/24 = 192.168.12.1 Net-Mask 255.255.255.0. Further assuming the IP 192.168.12.55 in your network is still free, the router is at the same time the gateway to the Internet and also does the name resolution (DNS). Then this line should look like this:

    nic.ifconfig(("192.168.12.55","255.255.255.0","192.168.12.1","192.168.12.1"))

    Please note the double brackets. The outer pair belongs to the function call, the inner one combines the 4 strings into a tuple, as the method ifconfig() requires.

    After this sequence has run, the terminal area should show an output of the following type.

    >>> %Run -c $EDITOR_CONTENT
         Constructor of Arduino-Interface
         Arduino @ 0x24
         Client-ID ec-fa-bc-bc-47-c4
         #32 ets_task(4020ee60, 28, 3fff92d0, 10)
         1.1.1.
         Connection status:  STAT_GOT_IP
         STA-IP: 10.0.1.91
         STA-NETMASK: 255.255.255.0
         STA-GATEWAY: 10.0.1.20

    So after three seconds, the connection was established. The IP address should always be fixed if the device is a server. You surely want to reach it always under the same URL and not have to guess every time which IP the DNS of the router has given the device.

    At this point, we have now created the connection to the local network. The only thing missing is a tool to use this channel. We can basically choose one from a whole range of services, TCP web server, TCP client, UDP server, UDP client, MQTT, FTP, Telnet...

    Let's start with a TCP web server, the UDP server comes next.

    The TCP web server

    This server is, just like the WLAN package, a file that I can easily include in applications. Depending on the environment, adjustments have to be made, like here. Because no RGB LED can be connected directly to the ESP8266-01, I delete all lines that would require that. In the end, this makes the code leaner and that is not wrong with the ESP8266.

    A web server has to send a web page to the browser, which has to contain appropriate controls, whose data we need to change the settings on the Nano V3 we need. In this case, I decided to use three forms with separate send buttons. So there's a lot of HTML code that we'll look at first.


    <HTML>
    <HEAD>
    <TITLE> Arduino control </TITLE>
    <link rel="icon" href="data:;base64,=">
    </HEAD>

    <BODY>
    <H1> ARDUINO-RC </H1>

    <FORM METHOD=GET ACTION="http://10.0.1.91:9009">
    LED control:
    <input type=text name=led size= 8 maxlength=6 value="120">
    <input type=submit value="FADE LED" name="LED">
    </FORM>

    <FORM METHOD=GET ACTION="http://10.0.1.91:9009">
    Switching the RGB LED:<BR>
    RED <INPUT TYPE="checkbox" NAME="RED" XXX><BR>
    GRÜN <INPUT TYPE="checkbox" NAME="GREEN" XXX ><BR>
    BLUE <INPUT TYPE="checkbox" NAME="BLUE" XXX ><BR>
    <INPUT TYPE="submit" value="SET_RGB" name="RGB">
    </FORM>

    <FORM METHOD=GET ACTION="http://10.0.1.91:9009">
    Operating voltage<INPUT TYPE="submit" value="GET_Ubat" name="UBAT"> is: XXX V
    </FORM>

    </BODY>
    </HTML>

    In black, you see the basic structure of an HTML page, the so-called tags. This text in the angle brackets is formatting instructions for the browser, which converts HTML documents into normally formatted text with images and links. HTML is the acronym for Hypertext Markup Language, which is a text-based, machine-readable language for formatting and structuring text. Most tags have an opening tag and a closing tag. The names are the same, except that the closing tag is preceded by a "/".

    So the minimum frame for an HTML document looks like this:

    <HTML>
    <HEAD>

    </HEAD>
    <BODY>

    </BODY>
    </HTML> 

    The individual forms are shown in different shades of green. Each one starts by specifying a transmission method, here it is GET. ACTION specifies the destination address of the request. The input tags define certain form elements. We work with a text field, 3 checkboxes, and the submit buttons. In between there is normal text outside the tags. In the browser, it looks like this - when it's completely finished.

    Figure 4: Form in the browser

    Image 4: Form in the browser

    As an attentive reader, you will have noticed the five places marked in red in the HTML text. This is where variable content must be inserted. So the text has to be composed of several blocks. For this, I use three features of MicroPython.

    • Text constants can be enclosed in quotation marks ('Text') or in quotation marks ('Text').
    • Delimiters in the text become part of the text if they differ from the surrounding ones.
      >>> a='This is a "special" property
      >>> print(a)
      This is a "special" property
    • Text constants that span multiple lines are enclosed in triple delimiters.

    Normal quotation marks occur in the page text, so I have enclosed the text blocks in three quotation marks. This is what it looks like in the middle section.

    ….
    <FORM METHOD=GET ACTION="http://10.0.1.91:9009">
    Switching the RGB LED:<BR>
    RED <INPUT TYPE="checkbox" NAME="RED"
    '''

    rgb_R=''
    ><BR>
    GRÜN <INPUT TYPE="checkbox" NAME="GREEN"
    '''

    rgb_G=''
    ><BR>
    BLUE <INPUT TYPE="checkbox" NAME="BLUE"
    '''
    ….

    All parts are stored in variables, so that at the end of the job routine they are webpage() job routine, they can be assembled with the variable elements.

    def web_page(pos):
       global pwm,chkR,chkG,chkB,voltage
       if request.find("GET / HTTP")!=-1:
           pwm="?"
       if request.find("LED")!=-1:
           start=request.find("=")
           end=request.find("&")
           pwm=int(request[start+1:end])
           a.writeAnalog(3,pwm)
           pwm=str(pwm)
       if request.find("RGB")!=-1:
           if request.find("RED")!=-1:
               chkR=checked
               a.setBit(PORT,C,rt,1)
           else:
               chkR=""
               a.setBit(PORT,C,rt,0)
           if request.find("GREEN")!=-1:
               chkG=checked
               a.setBit(PORT,C,gn,1)
           else:
               chkG=""
               a.setBit(PORT,C,gn,0)
           if request.find("BLUE")!=-1:
               chkB=checked
               a.setBit(PORT,C,bl,1)
           else:
               chkB=""
               a.setBit(PORT,C,bl,0)
               
       if request.find("UBAT")!=-1:
           voltage=str(a.readAnalog(3,True,3))
       response=head+pwm+fading+chkR+rgb_R+chkG+rgb_G+chkB+rgb_B+voltage+ubat
       return reply

    We start with the declaration of the variable pwm,chkR,chkG,chkB, and voltage as global because the values are to be changed in the function. If we do not do this, they are considered local variables. Their content is forgotten after leaving the function and is not passed outside.

    The four if constructs knock the first 50 characters of the query for the occurrence of certain text passages. A request sent in the browser with 10.0.1.91:9009/ contains the passage "GET / HTTP". As a result, the server sends back a bare home page to the browser (Figure 2). If one or more of the checkboxes were activated and the SET_RGB button was clicked, then the browser sends the following request.

    GET /?RED=on&GREEN=on&RGB=SET_RGB HTTP/1.1

    Only the fields that were checked appear in the query string. So we search for RED, GREEN and BLUE, set the chkX -variables to checked or the empty string and turn the corresponding LED on or off by setting the setBit-command to the Nano V3.

    Finds a UBAT in the request string, we get the value of the operating voltage of the ESP8266-01 and assign it to the variable voltage. Then the string for the complete page text is assembled and returned.

    After discussing the prerequisites (aka preparatory actions) let's see what the main part of the server code does.

    led=Pin(2,Pin.OUT,)
    led.value(1)  # LED at pin 2 follows negative logic

    try:
     import usocket as socket
    except:
     import socket

    checked='checked="checked"'
    pwm="0"
    chkR=''
    chkB=''
    chkG=''
    voltage="???"

    We hijack the LED on the ESP8266-01 for output, import the socket-module and preallocate the global variables. The third "checked" should appear in quotes, so we enclose the entire string in single quotation marks.

    Then we set up a socket as a receiving portal for requests. We already set up the IP address in the WIFI part: 10.0.1.91. Now we define a port number, 9009. We instantiate the socket object server from the IPv4 address family for TCP. The socket is told that the address 10.0.1.91:9009 is to be used repeatedly when the socket is restarted. The timeout for the receive loop is set to 0.1 seconds and then we bind the socket to the set address, as IP the existing one is used by the ' '. IP and port number form a tuple again, that's why the double round brackets. The server should respond to exactly one incoming request.

    portNum=9009
    print("Request server socket")
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    server.settimeout(0.1)
    server.bind(('', portNum))    # bind to local IP and port number 9009
    server.listen(1)     Accept until to 1 incoming Request(n)
    print("Receive requests on ",STAconf[0],":",portNum, sep='')

    This brings us to the server loop.

    Figure 5: Client-server principle

    Image 5: Client-server principle

    The receive loop, which starts with server.accept() is exited after 0.1 seconds if no request has arrived. An exception is then thrown, which we catch with try - except. If we had not set a timeout (blocking socket), then the receive loop would block all subsequent actions until a request arrives. This would be bad for the case when in the infinite loop there is still further actions are to be performed cyclically in the endless loop. If a message has arrived, then there is server.accept() returns a communication socket c and the socket address of the sender of the message. The further conversation is handled via the communication socket c (brown).

    while 1:                      # Infinite loop
     try:
       c, addr = server.accept()   # Accept request
       print('Got connection from {}:{}'.format(addr[0],addr[1]))
       led.value(0)
       # c is a bytes object and to be decoded as string
       # so that string methods can be applied to it.
       rawRequest=c.recv(1024)[0:50]
       request = rawRequest.decode("utf-8")
       print("*******Request**************XXX\n",request)
       getQuery=request.find("GET /?")
       getBlank=request.find("GET / ")
       if getQuery == 0 or getBlank == 0:
         print("**********GET-Position: {},{}\n\n".\
               format(getQuery,getBlank))
         response = web_page()
         #print("---------------\n",response,"\n-------------")
         c.send('HTTP/1.1 200 OK\n')
         c.send('Content-Type: text/html\n')
         c.sendall(response)
       else:
         print("#############\nQuery not valid\n#############")  
         response = web_page()
         c.send('HTTP/1.1 200 OK\n')
         c.send('Content-Type: text/html\n')
         c.sendall(response)
       sleep(0.1)
       c.close()
     except OSError:
       pass
     led.value(1)

    From the receive buffer we read rawRequest=c.recv(1024)[0:50] the first 50 characters. The bytes object is read with request = rawRequest.decode("utf-8") into a string. The print commands are not essential, but tell us what the server process is doing. They can also be omitted.

    A valid request must contain the characters "GET / " or "GET /?" at the very beginning at position 0. If this is the case, we pass the string to the parser web_page(). Its response goes to the variable response. We build a header for the page ('http/1.1 200 OK\n' ...) and then send the entire contents of the page to the requesting browser.

    But if the received text starts with something else, then it is not a valid request in our sense. To make the browser happy, we simply send it the last current page again, since the data is still in the variables. We have already solved the browser's annoying request for a file called favicon.ico with the line

    <link rel="icon" href="data:;base64,=">

    blocked in the header of our HTML code.

    After the communication is complete, we give the socket c a few more clocks to close the connection and then close the socket. There is nothing to handle as an exception if it was just a timeout. Exceptions based on other errors will still cause the program to terminate.

    If Nano V3 and the ESP8266-01 are ready for use, we load the module arduino_i2c.py into the flash and open the file arduino_goes_wlan_TCP.py in an editor window of Thonny. After starting with F5, the server reports.

    >>> %Run -c $EDITOR_CONTENT
       Constructor of Arduino-Interface
       Arduino @ 0x24
       Client-ID ec-fa-bc-bc-47-c4
       #52 ets_task(4020ee60, 28, 3fff92d0, 10)
       1.1.1.
       Connection status:  STAT_GOT_IP
       STA-IP: 10.0.1.91
       STA-NETMASK: 255.255.255.0
       STA-GATEWAY: 10.0.1.20
       Request Server-Socket to
       Receive Requests at 10.0.1.91:9009

    We start the first request with the URL: http://10.0.1.91:9009. Using the form elements we can now define the actions on the Nano V3. Pat on the back! AVR went ESP!

    The UDP server

    The UDP server does not send back a web page, but UDP packets, which it laces and sends to UDP clients based on previously received messages or on its own "decision".

    The whole part of the program, from setting up the ARDUINO-object to the end of the WLAN access, is identical to the one described under WLAN access described procedure.

    Setting up the socket also differs only slightly from its TCP brother. This time, however, we set up an abort button as well and instantiate a UDP socket.

    # udp_server.py
    # rudimentary UDP server non-blocking
    # a WLAN connection is required
    # **********************************
    #from machine import pin
    import socket

    button=Pin(0,Pin.IN,Pin.PULL_UP)

    #
    # Set up UDP server
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    s.bind(('', 9009))
    print("waiting on port 9009...")
    s.settimeout(0.1)

    The parser is a bit leaner, because it doesn't need to parse HTML code, but only command strings of the following structure, which is adapted to the class ARDUINO.

    command: parameter1;parameter2

    command

    parameter1

    parameter2

    FADE

    line

    value

    RGB

    (rgb)


    UBAT

    digits


    The incoming request is first separated at the ":" to separate the command. The if-elif structure checks for valid commands and initiates the examination of the parameters. The parsed contents are passed to the ARDUINO instance a in an appropriate form. In addition, a response is generated in each case. (rgb] states that any combination of the three characters can be specified in any order.

    def doit(request):
       reply="done"
       command,job=request.split(":")
       if command.upper()=="FADE":
           line,value=job.split(";")
           line=int(line); value=int(value)
           a.writeAnalog(line,value)
       elif command.upper()=="RGB": # gb
           a.writeIO(DDR,C,0b111)
           cols=0
           job=job.upper()
           if "B" in job:cols=1
           if "G" in job:cols+=2
           if "R" in job:cols+=4
           a.writeIO(PORT,C,cols)
           cols=a.readIO(PORT,C)[0] & 0b111
           reply="RGB set to {:03b}".format(cols)
       elif command.upper()=="UBAT":
           dig=int(job)
           value=a.readAnalog(3,digits=int(dig))
           reply="ESP86-01 U0 = {}".format(value)
       return reply

    The server loop is also very simple. Here, too, the timeout exception is caught by try - except.

    With UDP there is no communication socket as with TCP, because UDP works as an unsecured connection protocol without handshake and controls. Therefore the protocol is faster and uncomplicated, comparable to RS232. If the function recvfrom() detects the receipt of characters, the characters and the address of the sender are returned as a tuple. We extract the tuple, as with TCP, into the bytes object rec and the socket address adr. The byte object is decoded to the string and, if present, freed from interfering delimiters, \r = carriage return and \n line feed.

    Then we specify the string doit() to parse and execute the commands. We encode the response again as a bytes object and send it back to the client. With further sendto() commands, we could send the same message to more recipients. This is sometimes quite useful for debugging purposes.

    # Server loop
    while 1:
       gc.collect()
       try:
           # receive message
           rec,adr=s.recvfrom(150)
           rec=rec.decode().strip("\r\n")
           print(rec,adr)
           # parse message and
           # trigger actions
           answer=doit(rec)
           # Send results encoded or as string
           # it can be sent to multiple addresses
           reply=answer.encode()
           s.sendto(reply,adr)
           rec=""        
       except OSError:
           pass # timeout override
       if button.value()==0:
           sys.exit()

    The last two lines allow a clean exit from the loop without Ctrl+C when the Flash key is pressed.

    Nano V3 and ESP8266-01 are probably still built up from the previous attempt. Then we load the program arduino_goes_wlan_UDP.py into an editor window, the module arduino_i2c.py is already in the flash of the ESP8266-01. Well then, start with F5.

    Yes - and how should I test this now? You probably ask yourself, because you can't do that with the browser. Well, but there is a great tool that handles both TCP and UDP as server and client (and even more). The best thing to do is to download the portable version of the program "Packet Sender and unpack it in a directory of your choice. A directory "PacketSenderPortable" will be created. Then start the file contained in it packetsender.exe.

    Before you start, you must assign a custom port number for your PC.

    Figure 6: Packet sender settings

    Image 6: Packetsender Settings

    Figure 7: Port number assigned for UDP

    Image 7: Assign port number for UDP

    Then we prepare to send the first UDP command.

    Figure 8: The first UDP command

    Image 8: The first UDP command

    1. The name is only important in case you want to save the command.
    2. Here we enter the command for the ESP8266-01.
    3. The address of the destination
    4. The port address of the destination
    5. The transmission protocol is UDP
    6. And off goes the mail!

    The bottom two lines list our command and the response from ESP8266-01 in detail. Now you can test further commands. But one more candy is waiting for you.

    The UDP Android app

    Now of course it would be nice if the Nano V3 would not only be controllable via the PC. How about the cell phone? The MIT-AppInventor2 is a fine tool with which even people who have no idea about programming on the Android system can develop programs, excuse me, wanted to say apps, quite well and easily. Did you like to play with building blocks, wood or Lego in the past? Then you'll get along with AppInventor2 as well, because it only fits together what belongs together. You can find a tutorial to get started here.

    The tool is not installed on the PC, you simply work via your browser. On the cell phone, the app is downloaded from the Google Playstore. MIT AI2 Companion is downloaded and installed. Thus, after establishing a connection with your PC, you can develop your own apps on it and see 1:1 how it looks on your phone. The programmed functions and processes can also be tested at the same time.

    Figure 9: Connect to AI Companion

    Image 9: Connect to AI Companion

    If everything goes as you want, create an apk file that you can install permanently on your phone. This is what the screen of the app I built for our project looks like:

      Figure 10: Screenshot from the app:

    Image 10: Screenshot from the app:

    And this is the design in the AppInventor2 window:

    Figure 11: Design of the app

    Image 11: Design of the app

    And this is a (small) piece of the blueprint:

    Figure 12: A part of the blueprint

    Image 12: A piece of the blueprint.

    For the curious there is to the applet a aia-file. You can open this file in the MIT-AI2 via the Projects-menu to import, modify and add to them as you wish. Don't be confused by the variety of possibilities. Try to create small, manageable apps in the beginning. For each block, you can request help by right-clicking. If you use the list of my blogposts published so far, you will find more projects for which there is an app (for example Robot Car, Temperature monitor, Mosquito repellent), which you can take as an example.

    For those in a hurry I also have something, the finished apk-filewhich you can transfer to your cell phone via Bluetooth, for example. There you start the file for installation. How to do this in detail is also described in the manual in the last section.

    For the control to work, you must of course adapt the IP addresses to your network. For this, compare the chapter "The WLAN access". And one more thing, the UDP extension of Ulli's robot page has the same problem with the IP address as MicroPython. Only with MicroPython you can solve the problem by the line

    s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

    but not in the mobile app. The problem occurs when you have to restart the listener object while the app is running, for example after a network failure. Then there is error message 2, and it says that the already used address (actually the socket address) cannot be assigned a second time. The culprit is not the IP address, but the port number. So close the app and restart it.

    Another problem is again the socket address of the listener and again the culprit is the port number. The phone randomly dices itself a port number when sending a packet. The ESP8266-01 receives this number correctly and tries to send the result back to this socket. The sending also works, but the UDP listener in the cell phone has no idea that its own transmitter has assigned this port number and that as a result the listener should receive the packet. So we wait forever for the answer from ESP8266-01.

    The problem does not occur with the packet transmitter, because it always sends packets with the identical port number for transmitter and receiver, which we have set in the program.

    Figure 13: The cell phone says: wrong house number

    Image 13: The cell phone says: wrong house number

    So we assign a fixed port number both in the ESP8266-01 and in the mobile app.

    Figure 14: Now the house number is correct

    Image 14: Now the house number is correct

    In the server loop it looks like this.

    # Server loop
    while 1:
       gc.collect()
       try:
           # receive message
           rec,adr=s.recvfrom(150)
           rec=rec.decode().strip("\r\n")
           print(rec,adr)
           remoteIP,_= adr
           # parse message and
           # trigger actions
           answer=doit(rec)
           # Send results encoded or as string
           # it can be sent to multiple addresses
           reply=answer.encode()
           s.sendto(reply,(remoteIP,9091))
           rec=""        
       except OSError:
           pass # timeout override
       if button.value()==0:
           sys.exit()

    In the two files for the mobile app arduino_rc.aia and arduino_rc.apk the fixed port number is already taken into account, in the file arduino_goes_wlan_UDP.py as well. Only if you have entered the program text yourself, you still have to insert, respectively change the lines.

    Figure 15: Fixed port number at ESP8266

    Image 15: Fixed port number at ESP8266

    In the mobile app you can change the IP addresses and port numbers while the app is running. With the numeric keypad and an input function, changing the port number would also be possible on the ESP8266-01. With this suggestion the topic circle of this blog sequence closes.

    Have fun with the WLAN - ESP8266-01 - AVR - Bridge and good luck exploring the MIT-AppInventor2.

    Esp-8266Für arduinoProjekte für fortgeschritteneSmart home

    Leave a comment

    All comments are moderated before being published

    Recommended blog posts

    1. ESP32 jetzt über den Boardverwalter installieren - AZ-Delivery
    2. Internet-Radio mit dem ESP32 - UPDATE - AZ-Delivery
    3. Arduino IDE - Programmieren für Einsteiger - Teil 1 - AZ-Delivery
    4. ESP32 - das Multitalent - AZ-Delivery