5454# pylint: disable=bad-whitespace
5555_SET_NET_CMD = const (0x10 )
5656_SET_PASSPHRASE_CMD = const (0x11 )
57+ _SET_AP_NET_CMD = const (0x18 )
58+ _SET_AP_PASSPHRASE_CMD = const (0x19 )
5759_SET_DEBUG_CMD = const (0x1A )
5860
5961_GET_CONN_STATUS_CMD = const (0x20 )
@@ -410,6 +412,18 @@ def wifi_set_entenable(self):
410412 if resp [0 ][0 ] != 1 :
411413 raise RuntimeError ("Failed to enable enterprise mode" )
412414
415+ def _wifi_set_ap_network (self , ssid , channel ):
416+ """Creates an Access point with SSID and Channel"""
417+ resp = self ._send_command_get_response (_SET_AP_NET_CMD , [ssid , channel ])
418+ if resp [0 ][0 ] != 1 :
419+ raise RuntimeError ("Failed to setup AP network" )
420+
421+ def _wifi_set_ap_passphrase (self , ssid , passphrase , channel ):
422+ """Creates an Access point with SSID, passphrase, and Channel"""
423+ resp = self ._send_command_get_response (_SET_AP_PASSPHRASE_CMD , [ssid , passphrase , channel ])
424+ if resp [0 ][0 ] != 1 :
425+ raise RuntimeError ("Failed to setup AP password" )
426+
413427 @property
414428 def ssid (self ):
415429 """The name of the access point we're connected to"""
@@ -444,15 +458,30 @@ def is_connected(self):
444458 self .reset ()
445459 return False
446460
461+ @property
462+ def ap_listening (self ):
463+ """Returns if the ESP32 is in access point mode and is listening for connections"""
464+ try :
465+ return self .status == WL_AP_LISTENING
466+ except RuntimeError :
467+ self .reset ()
468+ return False
469+
447470 def connect (self , secrets ):
448471 """Connect to an access point using a secrets dictionary
449472 that contains a 'ssid' and 'password' entry"""
450473 self .connect_AP (secrets ['ssid' ], secrets ['password' ])
451474
452- def connect_AP (self , ssid , password ): # pylint: disable=invalid-name
453- """Connect to an access point with given name and password.
454- Will retry up to 10 times and return on success or raise
455- an exception on failure"""
475+ def connect_AP (self , ssid , password , timeout_s = 10 ): # pylint: disable=invalid-name
476+ """
477+ Connect to an access point with given name and password.
478+ Will wait until specified timeout seconds and return on success
479+ or raise an exception on failure.
480+
481+ :param ssid: the SSID to connect to
482+ :param passphrase: the password of the access point
483+ :param timeout_s: number of seconds until we time out and fail to create AP
484+ """
456485 if self ._debug :
457486 print ("Connect to AP" , ssid , password )
458487 if isinstance (ssid , str ):
@@ -463,17 +492,57 @@ def connect_AP(self, ssid, password): # pylint: disable=invalid-name
463492 self .wifi_set_passphrase (ssid , password )
464493 else :
465494 self .wifi_set_network (ssid )
466- for _ in range (10 ): # retries
495+ times = time .monotonic ()
496+ while (time .monotonic () - times ) < timeout_s : # wait up until timeout
467497 stat = self .status
468498 if stat == WL_CONNECTED :
469499 return stat
470- time .sleep (1 )
500+ time .sleep (0.05 )
471501 if stat in (WL_CONNECT_FAILED , WL_CONNECTION_LOST , WL_DISCONNECTED ):
472502 raise RuntimeError ("Failed to connect to ssid" , ssid )
473503 if stat == WL_NO_SSID_AVAIL :
474504 raise RuntimeError ("No such ssid" , ssid )
475505 raise RuntimeError ("Unknown error 0x%02X" % stat )
476506
507+ def create_AP (self , ssid , password , channel = 1 , timeout = 10 ): # pylint: disable=invalid-name
508+ """
509+ Create an access point with the given name, password, and channel.
510+ Will wait until specified timeout seconds and return on success
511+ or raise an exception on failure.
512+
513+ :param str ssid: the SSID of the created Access Point. Must be less than 32 chars.
514+ :param str password: the password of the created Access Point. Must be 8-63 chars.
515+ :param int channel: channel of created Access Point (1 - 14).
516+ :param int timeout: number of seconds until we time out and fail to create AP
517+ """
518+ if len (ssid ) > 32 :
519+ raise RuntimeError ("ssid must be no more than 32 characters" )
520+ if password and (len (password ) < 8 or len (password ) > 64 ):
521+ raise RuntimeError ("password must be 8 - 63 characters" )
522+ if channel < 1 or channel > 14 :
523+ raise RuntimeError ("channel must be between 1 and 14" )
524+
525+ if isinstance (channel , int ):
526+ channel = bytes (channel )
527+ if isinstance (ssid , str ):
528+ ssid = bytes (ssid , 'utf-8' )
529+ if password :
530+ if isinstance (password , str ):
531+ password = bytes (password , 'utf-8' )
532+ self ._wifi_set_ap_passphrase (ssid , password , channel )
533+ else :
534+ self ._wifi_set_ap_network (ssid , channel )
535+
536+ times = time .monotonic ()
537+ while (time .monotonic () - times ) < timeout : # wait up to timeout
538+ stat = self .status
539+ if stat == WL_AP_LISTENING :
540+ return stat
541+ time .sleep (0.05 )
542+ if stat == WL_AP_FAILED :
543+ raise RuntimeError ("Failed to create AP" , ssid )
544+ raise RuntimeError ("Unknown error 0x%02x" % stat )
545+
477546 def pretty_ip (self , ip ): # pylint: disable=no-self-use, invalid-name
478547 """Converts a bytearray IP address to a dotted-quad string for printing"""
479548 return "%d.%d.%d.%d" % (ip [0 ], ip [1 ], ip [2 ], ip [3 ])
0 commit comments