From 616c78bae1e9c011e363554f2159bdab03d19ef3 Mon Sep 17 00:00:00 2001 From: Geza Husi Date: Wed, 3 Mar 2021 17:36:31 +0100 Subject: [PATCH] Add documentation of espnow module and update coap documentation --- config.toml | 18 ++ content/firmwareapi/pycom/network/coap.md | 215 +++++++++++-------- content/firmwareapi/pycom/network/espnow.md | 154 +++++++++++++ content/tutorials/networkprotocols/coap.md | 119 ++++++++++ content/tutorials/networkprotocols/espnow.md | 207 ++++++++++++++++++ 5 files changed, 629 insertions(+), 84 deletions(-) create mode 100644 content/firmwareapi/pycom/network/espnow.md create mode 100644 content/tutorials/networkprotocols/coap.md create mode 100644 content/tutorials/networkprotocols/espnow.md diff --git a/config.toml b/config.toml index be224993..5d3b71e1 100644 --- a/config.toml +++ b/config.toml @@ -414,6 +414,18 @@ theme = "doc-theme" parent = "tutorials@networkprotocols" weight = 70 +[[menu.main]] + name = "CoAP" + url = "/tutorials/networkprotocols/coap/" + identifier = "tutorials@networkprotocols@coap" + parent = "tutorials@networkprotocols" + weight = 80 +[[menu.main]] + name = "ESP-NOW" + url = "/tutorials/networkprotocols/espnow/" + identifier = "tutorials@networkprotocols@espnow" + parent = "tutorials@networkprotocols" + weight = 90 [[menu.main]] name = "Expansion Boards" url = "/tutorials/expansionboards/" @@ -623,6 +635,12 @@ theme = "doc-theme" parent = "firmwareapi@pycom@network" weight = 20 +[[menu.main]] + name = "ESP-NOW" + url = "/firmwareapi/pycom/network/espnow/" + identifier = "firmwareapi@pycom@network@espnow" + parent = "firmwareapi@pycom@network" + weight = 20 [[menu.main]] name = "Bluetooth" url = "/firmwareapi/pycom/network/bluetooth/" diff --git a/content/firmwareapi/pycom/network/coap.md b/content/firmwareapi/pycom/network/coap.md index 120aaad8..e4153138 100644 --- a/content/firmwareapi/pycom/network/coap.md +++ b/content/firmwareapi/pycom/network/coap.md @@ -5,18 +5,37 @@ aliases: - firmwareapi/pycom/network/coap.md - chapter/firmwareapi/pycom/network/coap --- -This module implements a CoAp Server and Client, operating as both at the same time. +This module implements a CoAp Server and Client. -## Usage Example +## Usage Example of CoAp Server ```python from network import WLAN from network import Coap -import uselect -import _thread -# The callback that handles the responses generated from the requests sent to a CoAp Server +# Connect to the network +wlan = WLAN(mode=WLAN.STA) +wlan.connect('your-ssid', auth=(WLAN.WPA2, 'your-key')) + +# Initialize Coap module as CoAp Server, enable new resources to be added via PUT +Coap.init(str(wlan.ifconfig()[0]), service_discovery=True, dynamic_resources=True) + +# Add an example resource with URI "resource1" and value "default_value" and content format "plain text" +r = Coap.add_resource("resource1", media_type=Coap.MEDIATYPE_TEXT_PLAIN, value="default_value") +# Configure the possible operations on the resource +r.callback(Coap.REQUEST_GET | Coap.REQUEST_POST | Coap.REQUEST_PUT, True) + +``` + +## Usage Example of CoAp Client + +```python + +from network import WLAN +from network import Coap + +# Callback handling the responses to the requests sent to a Coap Server def response_callback(code, id_param, type_param, token, payload): print("Code: {}".format(code)) # The ID can be used to pair the requests with the responses @@ -25,83 +44,47 @@ def response_callback(code, id_param, type_param, token, payload): print("Token: {}".format(token)) print("Payload: {}".format(payload)) -# Thread handling the sockets -def socket_thread(p, coap_socket): - while True: - # Wait for any socket to become available - sockets = p.poll() - for s in sockets: - sock = s[0] - event = s[1] - if(event & uselect.POLLIN): - # Check if the socket belongs to the CoAp module - if(sock == coap_socket): - # Call Coap.read() which parses the incoming CoAp message - Coap.read() - - # Connect to the network wlan = WLAN(mode=WLAN.STA) wlan.connect('your-ssid', auth=(WLAN.WPA2, 'your-key')) -# Initialise the CoAp module -Coap.init(str(wlan.ifconfig()[0]), service_discovery=True) - -# Add a resource with a default value and a plain text content format -r = Coap.add_resource("resource1", media_type=Coap.MEDIATYPE_TEXT_PLAIN, value="default_value") -# Add an attribute to the resource -r.add_attribute("title", "resource1") -# Add an attribute to the resource -r.add_attribute("ct", str(Coap.MEDIATYPE_TEXT_PLAIN)) -# Configure the possible operations on the resource -r.callback(Coap.REQUEST_GET | Coap.REQUEST_POST | Coap.REQUEST_PUT, True) +# Initialize Coap module +Coap.init(str(wlan.ifconfig()[0])) -# Add a resource without default value, XML content format and E-Tag enabled -r = Coap.add_resource("resource2", media_type=Coap.MEDIATYPE_APP_XML, etag=True) -# Configure the possible operations on the resource -r.callback(Coap.REQUEST_GET | Coap.REQUEST_POST | Coap.REQUEST_PUT | Coap.REQUEST_DELETE, True) - -# Register the response handler for the requests that the module initiates as a CoAp Client +# Register the response handler for the requests the module initiates as a Coap Client Coap.register_response_handler(response_callback) +# Create a new Client Session to use to communicate with the CoAp Server +session = Coap.new_client_session("server-ip-address") -# Get the UDP socket created for the CoAp module -coap_server_socket = Coap.socket() - -# Create a new poll object -p = uselect.poll() -# Register the CoAp module's socket to the poll -p.register(coap_server_socket, uselect.POLLIN | uselect.POLLHUP | uselect.POLLERR) - -# Start a new thread which will handle the sockets of "p" poll -_thread.start_new_thread(socket_thread, (p, coap_server_socket)) - -# Send a request to a CoAp server -id = Coap.send_request("192.168.0.234", Coap.REQUEST_GET, uri_port=5683, uri_path=".well-known/core", payload="payload", token="token1", include_options=True) +# Send a GET Request to the CoAp Server asking for all resources via URI ".well-known/core" +id = session.send_request(Coap.REQUEST_GET, uri_path=".well-known/core") print(id) ``` ## Initialization -### Coap.init(address, *, port=5683, service_discovery=False) +#### Coap.init(address, *, port=5683, service_discovery=False, dynamic_resources=False, psk=None, hint=None) Initialize the CoAp module. The arguments are: -* `address` is the address where the CoAp module handles communication. -* `port` is the port where the CoAp module listens. If not set, the default CoAp UDP port is 5683. -* `service_discovery` is a Boolean argument that enables/disables service discovery. If enabled, the CoAp module will listen on the CoAp multicast address: 224.0.1.187. This is disabled by default. - -## Methods: +* `address` is the address where the CoAp module handles communication when it is a Server. If not set, the module can be used as a CoAp Client only. +* `port` is the port where the CoAp Server listens. If not set, the default CoAp UDP port 5683 is used if no PSK is given otherwise the default CoAp DTLS port 5684 is used. +* `service_discovery` is a Boolean argument that enables/disables service discovery. If enabled, the CoAp Server will listen on the CoAp multicast address: 224.0.1.187. This is disabled by default. +* `dynamic_resources` is a Boolean argument that enables/disables new resource creation via PUT operations. This is disabled by default. +* `psk` is a String representing the Pre-Shared Key to be used for the DTLS connection. +* `hint` is a String representing the hint value to be used for the DTLS connection. -### Coap.socket() +When `dynamic_resources` is TRUE new resources can be created via PUT reqeusts, if the resource with the given URI does not already exist. +The new resource is created with default properties (no mediatype, no Max-Age and no ETAG is enabled) and with default value received in the PUT request. On the new resource by default all operations (GET, PUT, POST and DELETE) are enabled. -Returns with the socket assigned to the given address and port during Coap.init() (= assigned to the CoAp module). +## Methods: -### Coap.add_resource(uri, *, media_type=-1, max_age=-1, value=0, etag=False) +#### Coap.add_resource(uri, *, media_type=-1, max_age=-1, value=0, etag=False) -Creates a resource object and adds it to the CoAp module to operate as a server. +Creates a resource object and adds it to the CoAp Server. * `uri` is the full path of the resource. * `media_type` is the media type (CoAp option: Content-Format) of the resource. If not given, no defined media type is associated with the resource. @@ -110,28 +93,28 @@ Creates a resource object and adds it to the CoAp module to operate as a server. * `etag` is a Boolean argument that enables/disables entity tag calculation (CoAp option: ETag). By default it is turned off. -> Media-type argument is one of the standard defined values that is available via CoAp module's constants. +{{% hint style="info" %}} +Media-type argument is one of the standard defined values that is available via CoAp module's constants. +{{% /hint %}} -> Entity tag calculation is a simple counter increment between value 1-65535 with overflow, it doesn't include the value 0. It is incremented each time and the value of the resource is changed. +{{% hint style="info" %}} +Entity tag calculation is a simple counter increment between value 1-65535 with overflow, it doesn't include the value 0. It is incremented each time and the value of the resource is changed. +{{% /hint %}} -### Coap.remove_resource(uri) +#### Coap.remove_resource(uri) Removes the resource defined by the `uri` argument. * `uri` is the full path of the resource to be removed. -### Coap.get_resource(uri) +#### Coap.get_resource(uri) Returns with the resource defined by `uri` argument. * `uri` is the full path of the resource to be returned. -### Coap.read() - -Must be called when a packet is received on the socket assigned to the CoAp module. This function passes on the incoming request, whilst also composing and sending out the response if needed. - -### Coap.register_response_handler(callback) +#### Coap.register_response_handler(callback) Registers a callback function which will be called when a remote CoAp Server responses to the local CoAp client's request. @@ -142,25 +125,66 @@ Registers a callback function which will be called when a remote CoAp Server res * `token` is the token field from the received message * `payload` is the payload of the received message -### Coap.send_request(uri_host, method, *, uri_port=5683, uri_path, content_format, payload, token, include_options=true) +#### Coap.register_new_resource_handler(callback) + +Registers a callback function which will be called when a new resource has been created via PUT operation. + +* `callback` is the callback to be registered. The callback must have 1 argument: + * `resource` is the new resource which has been created + +#### Coap.new_client_session(address, *, port=5683, psk=None, identity=None) + +Creates a new CoAp Client Session which can be used to communicate with an external CoAp Server. + +* `address` is the IPv4 Address of the CoAp Server to join. +* `port` is the port of the CoAp Server to join. If not set, the default CoAp UDP port 5683 is used if no PSK is given otherwise the default CoAp DTLS port 5684 is used. +* `psk` is a String representing the Pre-Shared Key to be used for the DTLS connection. +* `identity` is a String representing the identity value to be used for the DTLS connection. + + +#### Coap.remove_client_session(destination, port=5683, protocol=UDP) -Creates and sends a request to a CoAp server. +Removes the specified CoAp Client Session. + +* `destination` is the IPv4 Address of the CoAp Server where this CoAp Client Session has joined. +* `port` is the port of the CoAp Server where this CoAp Client Session has joined. If not set, the default CoAp UDP port 5683 is used. +* `protocol` is the protocol to be used to communicate with the CoAp Server where this CoAp Client Session has joined. This value can be: `Coap.PROTOCOL_UDP`,`Coap.PROTOCOL_DTLS`, if not set the protocol UDP is used. + +#### Coap.get_client_sessions() + +Returns with all the registered CoAp Client Sessions. + +## Class Client Session + +The CoAp Client Session class represents a Client Session in the scope of the CoAp module which can be used to communicate with an external CoAp Server. A new CoAp Client Session can only be created with the `Coap.new_client_session` function. + +#### CoapClientSession.send_request(method, *, uri_path, content_format, payload, token, include_options=true) + +Creates and sends a request to the external CoAp Server defined by the `destination` and `port` parameters when this CoAp Client Session was created. -* `uri_host` is the IP address of the server, included in the message as an "URI-HOST" option * `method` is the method to be sent to the server, can be: `Coap.REQUEST_GET`, `Coap.REQUEST_PUT`, `Coap.REQUEST_POST`, `Coap.REQUEST_DELETE` -* `uri_port` is the port of the server, included in the message as an "URI-PORT" option. By default it is 5683 * `uri_path` is the full path of the resource in the server, included in the message as an "URI-PATH" option. If nothing is given the request will not have URI-PATH option. * `content_format` is the Content-Format option of the request, can be: `Coap.MEDIATYPE_TEXT_PLAIN`, `Coap.MEDIATYPE_APP_LINK_FORMAT`, `Coap.MEDIATYPE_APP_XML`, `Coap.MEDIATYPE_APP_OCTET_STREAM`, `Coap.MEDIATYPE_APP_RDF_XML`, `Coap.MEDIATYPE_APP_EXI`, `Coap.MEDIATYPE_APP_JSON`, `Coap.MEDIATYPE_APP_CBOR`. If nothing is given the request will not have Content-Format option. * `payload` is the payload of the request. If nothing is given the request will not have payload. * `token` is the token field of the request. If nothing is given the request will not have token field. * `include_options` decides whether put any options (including the ones above) into the message or not. It can be used to send special requests to servers accepting CoAp formed requests without options, e.g. to a Dish Telemetry server. By default, the options are included. -## Class resource +#### CoapClientSession.get_details() + +Returns with a list of elements showing internal information about this CoAP Client Session: -The resource class represents a resource in the scope of the CoAp module when acting as a server. A new resource can only be created with the `Coap.add_resource` function. +* `Item 0:` is the IPv4 Address of the CoAp Server where this CoAp Client Session has joined. +* `Item 1:` is the port of the CoAp Server where this CoAp Client Session has joined. +## Class CoapResource -### resource.add_attribute(name, value) +The CoapResource class represents a resource in the scope of the CoAp module when acting as a server. + +#### Class methods + +The following methods are defined in the scope of the `resource` class. + +#### CoapResource.add_attribute(name, value) Adds a new attribute to the resource. Attributes are used to explain the resource during service discovery. @@ -177,30 +201,53 @@ coap-client -m get coap:///.well-known/core {{% /hint %}} -### resource.value(value) +#### CoapResource.get_details() + +Returns with the main details of this resource in a form of list: +* `Item 0:` is the URI of the resource. +* `Item 1:` is the mediatype (content format) configured for this resource. -1 means no mediatype is configured. +* `Item 2:` is the Max-Age configured for this resource. -1 means no Max-Age is configured. +* `Item 3:` is a boolean showing whether ETAG is enabled on this resource. +* `Item 4:` is the current ETAG value. + +#### CoapResource.set_details(*, mediatype, max_age, etag) + +Configures the main details of this resource: +* `mediatype` is the mediatype (content format) os the resource. Value -1 means no mediatype is defined for this resource. +* `max_age` is max-age value to be used on the resource. Value -1 means no Max-Age is defined for this resource. +* `etag` is a Boolean which enables or disables ETAG on the resource. + +This function leaves untouched any property of the resource not given as a parameter. + + +#### CoapResource.value(value) Updates or fetches the value of the resource. * `value` is the new value to update the current value with. If the method is called without a parameter, the current value is returned. -### resource.callback(operation, enable) +#### CoapResource.callback(operation, enable) To enable or disable a specific operation (GET, PUT, POST, DELETE) on the resource. * `operation` is the operation to enable/disable, can be ORED of the followings: `Coap.REQUEST_GET`, `Coap.REQUEST_PUT`, `Coap.REQUEST_POST`, `Coap.REQUEST_DELETE` * `enable` is Boolean parameter that enables/disables the operations specified by `operation` -> During a GET request, only the first occurrence of an ETAG or Accept option is passed on and interpreted; others of the same type are dropped (if any). - -> During a PUT request, only the first occurrence of an If-Match option is passed on and interpreted; others of the same type are dropped (if any). - - +{{% hint style="info" %}} +During a GET request, only the first occurrence of an ETAG or Accept option is interpreted; others of the same type are dropped (if any). +{{% /hint %}} -> Due to limitations of the underlying ESP-IDF/libcoap library, new resources cannot be added via PUT or POST requests. +{{% hint style="info" %}} +During a PUT request, only the first occurrence of an If-Match and If-None-Match option is interpreted; others of the same type are dropped (if any). +{{% /hint %}} +{{% hint style="danger" %}} +Due to limitations of the underlying ESP-IDF/libcoap library, new resources cannot be added via POST request. +{{% /hint %}} ## Constants * Define the media type: `Coap.MEDIATYPE_TEXT_PLAIN`, `Coap.MEDIATYPE_APP_LINK_FORMAT`, `Coap.MEDIATYPE_APP_XML`, `Coap.MEDIATYPE_APP_OCTET_STREAM`, `Coap.MEDIATYPE_APP_RDF_XML`, `Coap.MEDIATYPE_APP_EXI`, `Coap.MEDIATYPE_APP_JSON`, `Coap.MEDIATYPE_APP_CBOR` * Define the operation: `Coap.REQUEST_GET`, `Coap.REQUEST_PUT`, `Coap.REQUEST_POST`, `Coap.REQUEST_DELETE` +* Define the protocol: `Coap.PROTOCOL_UDP`,`Coap.PROTOCOL_DTLS` \ No newline at end of file diff --git a/content/firmwareapi/pycom/network/espnow.md b/content/firmwareapi/pycom/network/espnow.md new file mode 100644 index 00000000..1e2afea6 --- /dev/null +++ b/content/firmwareapi/pycom/network/espnow.md @@ -0,0 +1,154 @@ +--- +title: "ESPNOW" +aliases: + - firmwareapi/pycom/network/espnow.html + - firmwareapi/pycom/network/espnow.md + - chapter/firmwareapi/pycom/network/espnow +--- +This module implements interface to ESP-NOW protocol: https://www.espressif.com/en/products/software/esp-now/overview + +## Usage Example + +```python + +from network import WLAN +from network import ESPNOW +import binascii +import time + +# The callback to be registered when a message has been sent to a Peer +def espnow_tx(result): + # "result" is the parameter in form of 2 element long tuple + # "peer" is the Peer which the message has been sent to + # "sent" is a boolean showing whether the message could be sent + peer, sent = result + mac = peer.addr() + if(sent == False): + print("Sending message to %s failed!" % binascii.hexlify(mac)) + else: + print("Message sent to: %s" % (binascii.hexlify(mac))) + +# The callback to be registered when a message has been received +def espnow_rx(result): + # "result" is the parameter in form of 3 element long tuple + # "mac" is the MAC address of the sender + # "peer" is the Peer which the message has been received from. If message has been received from a not registered Peer this parameter is None + # "msg" is the payload from the received message + mac, peer, msg = result + if(peer is not None): + print("Message received from %s with content: %s" % (binascii.hexlify(mac), msg)) + peer.send("Sending back an answer") + +# The ESPNOW module needs that WLAN is initialized +w = WLAN() +# Initialize the ESPNOW module +ESPNOW.init() + +# Register the callback which will be called on TX +ESPNOW.on_send(espnow_tx) +# Register the callback which will be called on RX +ESPNOW.on_recv(espnow_rx) + +# Add a dedicated Peer with MAC address: 11:22:33:44:55:66 +p = ESPNOW.add_peer("112233445566") +# Send a message dedicated to the Peer +p.send("My Message") +# Sending 1 message to all Peers which are registered +ESPNOW.send(None, "Hello all Peers!") + +``` + +## Initialization + +#### ESPNOW.init() + +Initialize the ESP-NOW module. The module needs that WLAN has already been initialized. + +{{% hint style="danger" %}} +When using ESP-NOW module, WLAN must not be reconfigured or deinitialized. +{{% /hint %}} + +## Methods: + +#### ESPNOW.deinit() + +Deinitialize the ESP-NOW module. + +#### ESPNOW.pmk(pmk) + +Configures the Primary Master Key which is used to encrypt the Local Master Key. + +* `pmk` is the Primary Master Key to be configured. + +The PMK specified by `pmk` is accepted in format of string with length 16. + +#### ESPNOW.send(addr, msg) + +Sends a message `msg` to a remote device with MAC address `addr`. + +* `addr` is the MAC address of the remote device. If `None` is passed then the message is sent to all registered Peers. +* `msg` is the message to send. + +#### ESPNOW.on_recv(cb) + +Registers the `cb` callback which will be called when a new message has been received. + +* `cb` is the callback function to be called, it expects 1 parameter which is a tuple with 3 elements: + * Element 0: is the MAC address of the sender. + * Element 1: is the Peer which the message has been received from. If message has been received from a not registered Peer this parameter is None. + * Element 2: is the payload from the received message. + +#### ESPNOW.on_send(cb) + +Registers the `cb` callback which will be called when a new message has been sent. + +* `cb` is the callback function to be called, it expects 1 parameter which is a tuple with 2 elements: + * Element 0: is the Peer which the message has been sent to. + * Element 1: is a boolean showing whether the message could be sent. + +#### ESPNOW.peer_count() + +Returns with the number of registered Peers in a form of tuple with 2 elements: +* Element 0: is the number of all the registered Peers. +* Element 1: is the number of encrypted Peers. + +#### ESPNOW.version() + +Returns with the curently used version of the ESP-NOW protocol. + +#### ESPNOW.add_peer(addr, lmk=None) + +Creates a new Peer object and registers it into ESP-NOW module. +* `addr` is the MAC address of the Peer to be created. MAC address is accepted in either String format or as a Byte array. +* `lmk` is the Local Master Key to be used when communicating with the Peer. By default it is None which means the communication will not be encrypted. + +The LMK specified by `lmk` is accepted in format of string with length 16. + +This function returns with an `ESPNOW_Peer` object. + +#### ESPNOW.del_peer(Peer) + +Destroys the Peer object with type `ESPNOW_Peer`. + +## Class ESPNOW_Peer + +The ESPNOW_Peer class represents a Peer in the scope of the ESP-NOW module. A new resource can only be created with the `ESPNOW.add_peer` function. + +#### Class methods + +#### ESPNOW_Peer.addr(addr=None) + +Returns with the MAC address of the Peer. + +#### ESPNOW_Peer.lmk(lmk=None) + +Configures or returns the Local Master Key of the Peer. +* `lmk` is the new LMK to be set to this Peer. + +If `lmk` is not used then this functions returns the LMK of the Peer. +The LMK specified by `lmk` is accepted in format of string with length 16. + +#### ESPNOW_Peer.send(msg) + +Sends a message to the Peer. +* `msg` is the message to send. diff --git a/content/tutorials/networkprotocols/coap.md b/content/tutorials/networkprotocols/coap.md new file mode 100644 index 00000000..72130c49 --- /dev/null +++ b/content/tutorials/networkprotocols/coap.md @@ -0,0 +1,119 @@ +--- +title: "" +aliases: + - tutorials/all/coap.html + - tutorials/all/coap.md +--- + +Detailed information about this class can be found in [coap](/firmwareapi/pycom/network/coap). + +### Example of CoAp Server + +The following example sets up a Coap Server. + +```python + +from network import WLAN +from network import Coap + +# Callback to be called when a new resource has been added via PUT +def new_resource_callback(new_resource): + details = new_resource.get_details() + print("New resource has been created!") + print("URI: {}".format(details[0])) + print("Mediatype: {}".format(details[1])) + print("Max Age: {}".format(details[2])) + print("ETAG: {}".format(details[3])) + print("ETAG value: {}".format(details[4])) + print("Value: {}".format(new_resource.value())) + # Configure the properties of the new resource + # Mediatype, max_age and etag depends on the use-case, for this example set them randomly + new_resource.set_details(mediatype=Coap.MEDIATYPE_TEXT_PLAIN, max_age=100, etag=True) + +# Connect to the network +wlan = WLAN(mode=WLAN.STA) +wlan.connect('your-ssid', auth=(WLAN.WPA2, 'your-key')) + +# Initialize Coap module as CoAp Server, enable service discovery and new resources to be added via PUT +Coap.init(str(wlan.ifconfig()[0]), service_discovery=True, dynamic_resources=True) +# Register callback which will be called when new resource is added via PUT +Coap.register_new_resource_handler(new_resource_callback) + +# Add an example resource with default value and plain text content format +r = Coap.add_resource("resource1", media_type=Coap.MEDIATYPE_TEXT_PLAIN, value="default_value") +# Add an attribute to the resource +r.add_attribute("title", "resource1") +# Add an attribute to the resource +r.add_attribute("ct", str(Coap.MEDIATYPE_TEXT_PLAIN)) +# Configure the possible operations on the resource +r.callback(Coap.REQUEST_GET | Coap.REQUEST_POST | Coap.REQUEST_PUT, True) + +# Add another example resource without default value, XML content format and E-Tag enabled +r = Coap.add_resource("resource2", media_type=Coap.MEDIATYPE_APP_XML, etag=True) +# Configure the possible operations on the resource +r.callback(Coap.REQUEST_GET | Coap.REQUEST_POST | Coap.REQUEST_PUT | Coap.REQUEST_DELETE, True) + +``` + +### Example of CoAp Client +This example shows how to use the CoAp Client session. + + +```python +from network import WLAN +from network import Coap +import _thread + +# Address of the CoAp Server which this Client Session wants to connect +COAP_SERVER_ADDRESS = "192.168.0.234" +# Port of the CoAp Server which this Client Session wants to connect +COAP_SERVER_PORT = 5683 + +# Callback handling the responses to the requests sent to a Coap Server +def response_callback(code, id_param, type_param, token, payload): + print("Code: {}".format(code)) + # The ID can be used to pair the requests with the responses + print("ID: {}".format(id_param)) + print("Type: {}".format(type_param)) + print("Token: {}".format(token)) + print("Payload: {}".format(payload)) + +# Connect to the network +wlan = WLAN(mode=WLAN.STA) +wlan.connect('your-ssid', auth=(WLAN.WPA2, 'your-key')) + +# Initialize Coap module +Coap.init(str(wlan.ifconfig()[0]), service_discovery=True) + +# Register the response handler for the requests the module initiates as a Coap Client +Coap.register_response_handler(response_callback) + +# Create a new Client Session +session = Coap.new_client_session(COAP_SERVER_ADDRESS, COAP_SERVER_PORT) + +# Send a GET Request to the CoAp Server asking for all resources via URI ".well-known/core" +id = session.send_request(Coap.REQUEST_GET, uri_path=".well-known/core", payload="payload", token="token1", include_options=True) +# The id can be used to match the request with the response in the response_callback +print(id) +# Send a GET Request to the CoAp Server asking for resource with URI "time" +id = session.send_request(Coap.REQUEST_GET, uri_path="time", payload="payload", token="token2", include_options=True) +print(id) +# Send a PUT request to the CoAp Server updating the resource with URI "time" to new value "ABCD" +id = session.send_request(Coap.REQUEST_PUT, uri_path="time",content_format=Coap.MEDIATYPE_TEXT_PLAIN, payload="ABCD", token="token3", include_options=True) +print(id) + +``` +### Example snippet for sending message without options +This example snippet shows how to send messages to servers which accept CoAp messages without any options. +In this case the URI should be placed in the "payload". + +```python + +# Token can be 8 bytes maximum +TOKEN = 12345678 +# Payload must include the full URI (Host, Port, Path etc.) +FULL_URI_PATH = "abcd://server:1234/mypath.data" +# Disable normal CoAp options via include_options=False +id = session.send_request(Coap.REQUEST_GET, payload=FULL_URI_PATH, token=TOKEN, include_options=False) + +``` \ No newline at end of file diff --git a/content/tutorials/networkprotocols/espnow.md b/content/tutorials/networkprotocols/espnow.md new file mode 100644 index 00000000..ed88808d --- /dev/null +++ b/content/tutorials/networkprotocols/espnow.md @@ -0,0 +1,207 @@ +--- +title: "" +aliases: + - tutorials/all/ESP-NOW.html + - tutorials/all/espnow.md +--- + +Detailed information about this class can be found in [espnow](/firmwareapi/pycom/network/espnow). + +### Sending and receiving messages to/from 2 remote devices: Peer 1 and Peer 2 + +The following example sets up a device which communicates with 2 remote Peers. Message exchange with Peer 1 is encrypted while with Peer 2 it is not. + +```python + +from network import WLAN +from network import ESPNOW +import binascii +import time + +# Modify this variable to change the PMK to be used +# Note: This must match with the value of ESPNOW_PMK from the next example. +ESPNOW_PMK = "0123456789abcdef" +# Modify this variable to change the MAC address of the remote Peer 1 +# Note: This must match with the MAC address of the Peer 1 from the next example. +ESPNOW_PEER_1_MAC = "112233445566" +# Modify this variable to change the LMK to be used when communicating with remote Peer 1 +# Note: This must match with the value of ESPNOW_PEER_LMK from the next example. +ESPNOW_PEER_1_LMK = "0123456789123456" +# Modify this variable to change the MAC address of the remote Peer 2 +# Note: This must match with the MAC address of the Peer 2 from the next example. +ESPNOW_PEER_2_MAC = "AABBCCDDEEFF" + + +# The callback to be registered when a message has been sent to a Peer +def espnow_tx(result): + # "result" is the parameter in form of 2 element long tuple + # "peer" is the Peer which the message has been sent to + # "sent" is a boolean showing whether the message could be sent + peer, sent = result + mac = peer.addr() + if(sent == False): + print("Sending message to %s failed!" % binascii.hexlify(mac)) + else: + print("Message sent to: %s" % (binascii.hexlify(mac))) + +# The callback to be registered when a message has been received +def espnow_rx(result): + # "result" is the parameter in form of 3 element long tuple + # "mac" is the MAC address of the sender + # "peer" is the Peer which the message has been received from. If message has been received from a not registered Peer this parameter is None + # "msg" is the payload from the received message + mac, peer, msg = result + # Accept and handle messages only from the registered Peers + if(peer is not None): + # Do something with the received message + print("Message received from %s with content: %s" % (binascii.hexlify(mac), msg)) + +# The ESPNOW module needs that WLAN is initialized +w = WLAN() +# Initialize the ESPNOW module +ESPNOW.init() +print("ESP-NOW version: %s" % ESPNOW.version()) + +# Register the callback which will be called on TX +ESPNOW.on_send(espnow_tx) +# Register the callback which will be called on RX +ESPNOW.on_recv(espnow_rx) +# Configure the Primary Master Key which is used to encrypt the Local Master Key +ESPNOW.pmk(ESPNOW_PMK) + +# Add Peer 1 +p1 = ESPNOW.add_peer(ESPNOW_PEER_1_MAC) +# Add Peer 2 +p2 = ESPNOW.add_peer(ESPNOW_PEER_2_MAC) +# Set the Local Master Key of Peer p1 +p1.lmk(ESPNOW_PEER_1_LMK) +# Do not set LMK for Peer 2, traffic is not encrypted + +# Get the number of registered Peers and how many is encrypted +count = ESPNOW.peer_count() +print("Number of Peers: %s, encrypted: %s" % (count[0], count[1])) + +# Sending some messages to the Peers individually +i = 0 +while i < 10: + # The Peer 1 will only receive this message if the same PMK and LMK is configured for the Peer object created for this device on the other device also. Check the next examples. + p1.send("%s" % (i)) + # Message to Peer 2 is not encrypted, on the Peer 2 device encryption for this current device's Peer object should not be configured. Check the next examples. + p2.send("%s" % (i)) + i = i + 1 + time.sleep(1) + +# Sending 1 message to all Peers which are registered +ESPNOW.send(None, "Hello all Peers!") + +# Remove Peer 1 +ESPNOW.del_peer(p1) + +# Deinit the module +ESPNOW.deinit() + +``` + +### Example code for Peer 1 +This example code implements Peer 1 device from the previous example. + +```python +from network import WLAN +from network import ESPNOW +import binascii +import time + +# Modify this variable to change the PMK to be used +# Note: This must match with the value of ESPNOW_PMK from the first example. +ESPNOW_PMK = "0123456789abcdef" +# Modify this variable to change the MAC address of the remote Peer. +# Note: This must match with the MAC address of the Peer from the first example. +ESPNOW_PEER_MAC = "665544332211" +# Modify this variable to change the LMK to be used when communicating with the remote Peer +# Note: This must match with the value of ESPNOW_PEER_1_LMK from the first example. +ESPNOW_PEER_LMK = "0123456789123456" + +def tx(result): + peer, sent = result + mac = peer.addr() + print("Sending to: %s - %r" % (binascii.hexlify(mac),sent)) + +def rx(result): + print("Message received:") + mac, peer, msg = result + # Accept messages only from the registered Peer + if(peer is not None): + print("Received: %s - %s" % (binascii.hexlify(mac),msg)) + # Send back a response + peer.send("Message received: %s" % msg) + +w = WLAN() +ESPNOW.init() +ESPNOW.on_send(tx) +ESPNOW.on_recv(rx) +ESPNOW.pmk(ESPNOW_PMK) + +# Add the Peer +p = ESPNOW.add_peer(ESPNOW_PEER_MAC) +# Configure the LMK to be used during communication. +p.lmk(ESPNOW_PEER_LMK) + +``` + +### Example code for Peer 2 +This example code implements Peer 2 device from the first example. + +```python +from network import WLAN +from network import ESPNOW +import binascii +import time + +# Modify this variable to change the MAC address of the remote Peer. +# Note: This must match with the MAC address of the Peer from the first example. +ESPNOW_PEER_MAC = "665544332211" + +def tx(result): + peer, sent = result + mac = peer.addr() + print("Sending to: %s - %r" % (binascii.hexlify(mac),sent)) + +def rx(result): + print("Message received:") + mac, peer, msg = result + # Accept messages only from the registered Peer + if(peer is not None): + print("Received: %s - %s" % (binascii.hexlify(mac),msg)) + # Send back a response + peer.send("Message received: %s" % msg) + +w = WLAN() +# Init ESP-NOW, no PMK is configured. +ESPNOW.init() +ESPNOW.on_send(tx) +ESPNOW.on_recv(rx) + +# Add the Peer. No LMK will be configured for it. +p = ESPNOW.add_peer(ESPNOW_PEER_MAC) + +``` + +### Example snippet for RX callback +This example snippet shows how it may be handled when the device receives a message from a previously not registered Peer. + +```python + +# The callback to be registered when a message has been received +def espnow_rx(result): + # "result" is the parameter in form of 3 element long tuple + # "mac" is the MAC address of the sender + # "peer" is the Peer which the message has been received from. If message has been received from a not registered Peer this parameter is None + # "msg" is the payload from the received message + mac, peer, msg = result + if(peer is None): + print("Message received from an unknown Peer, registering...") + peer = ESPNOW.add_peer(mac) + print("Message received from %s with content: %s" % (binascii.hexlify(mac), msg)) + peer.send("Sending back an answer...") + +``` \ No newline at end of file