Problem with UDP recvfrom

All ESP8266 boards running MicroPython.
Official boards are the Adafruit Huzzah and Feather boards.
Target audience: MicroPython users with an ESP8266 board.
Post Reply
User avatar
curt
Posts: 25
Joined: Thu Jul 29, 2021 3:52 am
Location: Big Lake, Alaska

Problem with UDP recvfrom

Post by curt » Thu Aug 05, 2021 2:29 am

The following program does not run on my ESP8266 board but does run on my ESP32 board:

Code: Select all

#
# pollprob.py
#

import machine
import utime as time
import network

import gc
import usocket as socket
import os
import ujson as json

#from loggerconfig import *

my_hostname = "ESP32test"

ACTIVITY_PIN = 2
BLINK_MS = 2000       # 2 seconds

#---------------------------------------------------------------------------
# Globals
#---------------------------------------------------------------------------
class Globals:
    def __init__(self, poll_interval_ms) :
        #print ("Globals: init")
        self.poll_interval_ms = poll_interval_ms
        self.poll_init ()
        self.message_data = {}
        self.states = {
            'running' : True
            }
    
    def running (self) :
        return self.states['running']
    def shutdown (self) :
        self.states['running'] = False

    def poll_init (self) : # ???
        self.poll_time_ms = time.ticks_ms ()
        self.poll_time_next_ms = time.ticks_add (self.poll_time_ms, self.poll_interval_ms)

    def poll_wait (self) :
        #print ("Globals: ======> poll_wait:", time.time())
        current_time_ms = time.ticks_ms ()
        if time.ticks_diff (self.poll_time_next_ms, current_time_ms) <= 0 :
            print ("Poll loop too much time")
            self.poll_time_ms = current_time_ms
        else :
            #print ("Poll loop OK")
            sleep_time_ms = self.poll_time_next_ms - current_time_ms
            time.sleep_ms (sleep_time_ms)
            self.poll_time_ms = self.poll_time_next_ms
        self.poll_time_next_ms = time.ticks_add (self.poll_time_ms, self.poll_interval_ms)

    def seconds_to_ms (self, interval_seconds) :
        return interval_seconds * 1000
    def minutes_to_ms (self, interval_minutes) :
        return self.seconds_to_ms (interval_minutes) * 60
    def hours_to_ms (self, interval_hours) :
        return self.minutes_to_ms (interval_hours) * 60

    def active_next_ms (self, interval_ms) :
        return time.ticks_add (self.poll_time_ms, interval_ms)
    def active_now (self, next_active_ms) :
        return time.ticks_diff (self.poll_time_ms, next_active_ms) >= 0
    def message_set (self, mess_id, mess_dict) :
        if not mess_id in self.message_data :
            self.message_data[mess_id] = mess_dict
        else :
            for mess_key in mess_dict :
                self.message_data[mess_id][mess_key] = mess_dict[mess_key]
    def message_get (self, mess_id) :
        if mess_id in self.message_data :
            return (self.message_data[mess_id])
        else :
            return {}

# end Globals

#---------------------------------------------------------------------------
# GetCommand
#---------------------------------------------------------------------------
class GetCommand:
    def __init__(self, global_data) :
        #print ("GetCommand: init")       
        self.global_data = global_data

        self.s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.my_ip = network.WLAN().ifconfig()[0]
        #print (self.my_ip)
        self.address = socket.getaddrinfo(self.my_ip, 5010)[0][-1]
        self.address = ("", 5010)
        #print ("addr:", self.address)
        self.s.bind(self.address)
        self.s.settimeout(0)

    def poll_it (self) :
        #print ("GetCommand: poll_it")
        while True :
            try :
                mess_address = self.s.recvfrom (2000)
                print ("GetC:", mess_address)
                message = mess_address[0]
                address_port = mess_address [1]
                #request_json = message.decode(encoding="ascii", errors="ignore")
                request_json = message.decode ()
                request_dict = json.loads (request_json)
                #print ("Cmd:", request_dict)
            except OSError :
                #print ("GetC: no data")
                return
        # test for incomming messages

#---------------------------------------------------------------------------
# Heartbeat
#---------------------------------------------------------------------------
class Heartbeat:
    def __init__(self, global_data) :
        #print ("Heartbeat: init")
        self.global_data = global_data
        # Respond to 'poll_it' every 15 seconds
        self.active_interval_ms = self.global_data.seconds_to_ms (15)  # activity  milliseconds
        self.active_next_ms = self.global_data.active_next_ms (0)   # Send 1st message now
        self.client_sender_socket = socket.socket (socket.AF_INET, socket.SOCK_DGRAM)
        self.serial_number = 0
    
    def poll_it (self) :
        global my_hostname
        #print ("Heartbeat: poll_it")
        if not global_data.active_now (self.active_next_ms) :
            return
        self.active_next_ms = self.global_data.active_next_ms (self.active_interval_ms)

        ##### TEST!!!!!!!
        self.serial_number += 1
        action = 'heartbeat'
        heartbeat_dict = {
            'action' : action ,
            'id' : my_hostname ,
            action : {}
            }
        heartbeat_dict [action]['machine'] = {
            "serial_number" : self.serial_number ,
            "freq" : machine.freq () ,
            "gc_mem_alloc": gc.mem_alloc () ,
            "gc_mem_free": gc.mem_free ()
            }

        result_json = json.dumps (heartbeat_dict)   # dictionary to json
        bytesToSend = str.encode (result_json)      # message length
        print ("Sending to:", ('127.0.0.1', 5010))
        self.client_sender_socket.sendto (bytesToSend, ('127.0.0.1', 5010))

# end Heartbeat #
    
#---------------------------------------------------------------------------
# main
#---------------------------------------------------------------------------

global_data = Globals (250)            # 0.25 second poll interval
get_command = GetCommand (global_data)
heartbeat = Heartbeat (global_data)

#----
#---- Poll loop
#----
while global_data.running () :
    #blink_status_led.poll_it ()
    get_command.poll_it ()
    #send_status.poll_it ()
    heartbeat.poll_it ()
    global_data.poll_wait ()
It is assumed the network connection has been established.
For testing the heartbeat instance sends a UDP message to 5010 on localhost. Normally the message would be sent to an external server.
The get_command instance should receive the UPD message(s) and display it. The recvfrom timeout is set to zero to prevent blocking

The ESP8266 board never receives the UDP message displaying:

Code: Select all

Sending to: ('127.0.0.1', 5010)
Sending to: ('127.0.0.1', 5010)
Sending to: ('127.0.0.1', 5010)
Sending to: ('127.0.0.1', 5010)
The ESP32 board receives the UDP message displaying:

Code: Select all

[200~Sending to: ('127.0.0.1', 5010)
GetC: (b'{"heartbeat": {"machine": {"gc_mem_free": 101616, "serial_number": 1, "freq": 160000000, "gc_mem_alloc": 9552}}, "id": "ESP32test", "action": "heartbeat"}', ('127.0.0.1', 52539))
Sending to: ('127.0.0.1', 5010)
GetC: (b'{"heartbeat": {"machine": {"gc_mem_free": 76096, "serial_number": 2, "freq": 160000000, "gc_mem_alloc": 35072}}, "id": "ESP32test", "action": "heartbeat"}', ('127.0.0.1', 52539))
I'm fairly new to python/micropython so I'm not sure if this is a coding/esp limitation/micropython problem.

Thanks for your help,
Curt

Post Reply