{"id":625,"date":"2015-08-01T20:55:47","date_gmt":"2015-08-01T19:55:47","guid":{"rendered":"http:\/\/axotron.se\/blog\/?p=625"},"modified":"2015-08-02T06:52:47","modified_gmt":"2015-08-02T05:52:47","slug":"frdm-based-zigbee-humidity-sensor-node-prototype","status":"publish","type":"post","link":"https:\/\/axotron.se\/blog\/frdm-based-zigbee-humidity-sensor-node-prototype\/","title":{"rendered":"FRDM-based ZigBee Humidity Sensor Node Prototype"},"content":{"rendered":"<p>I have previously written about <a href=\"http:\/\/axotron.se\/blog\/interfacing-an-xbee-module-to-a-frdm-board\/\" target=\"_blank\">how to interface an XBee ZigBee module to a FRDM-KL25Z board<\/a> and also <a href=\"http:\/\/axotron.se\/blog\/interfacing-to-an-xbee-module-from-python\/\" target=\"_blank\">how to talk to an XBee ZigBee module from a Python program on a PC<\/a>. In this article I build on these foundations and show how to make a somewhat more complex system where a Python program running on a PC sends commands to &#8211; and receives replies from &#8211; a sensor node. The sensor node has an SHT21 humidity (and temperature) sensor which is connected via I2C to the FRDM board.<\/p>\n<p>Here I use the API mode of the XBee module on the sensor node, instead of AT mode that I used in the <a href=\"http:\/\/axotron.se\/blog\/interfacing-an-xbee-module-to-a-frdm-board\/\" target=\"_blank\">previous blog post<\/a>. There are a few XBee libraries for e.g. Arduino and mbed available. It might be possible to use one of those, but I looked into some of them briefly and it seemed like more work to understand and adapt one of those than to write the code necessary to take care of the somewhat limited communication with the XBee module required for this system. On the Python side, I continue to use the XBee library I used before.<\/p>\n<h4>Preparing the XBee Modules<\/h4>\n<p>The XBee ZigBee modules need to be properly configured before they can be used in this system. The one connected to the PC shall be programmed with the ZigBee API-mode Coordinator firmware and the one used in the sensor node shall have the API-mode Router firmware. A 64-bit PAN ID must be selected and both modules should be configured with this.<\/p>\n<h4>Humidity Sensor SHT21 and I2C<\/h4>\n<p>The combined humidity and temperature sensor I selected for this project is called <a href=\"http:\/\/www.sensirion.com\/fileadmin\/user_upload\/customers\/sensirion\/Dokumente\/Humidity\/Sensirion_Humidity_SHT21_Datasheet_V4.pdf\" target=\"_blank\">SHT21<\/a> and is made by <a href=\"http:\/\/www.sensirion.com\/en\/products\/humidity-temperature\/humidity-temperature-sensor-sht2x\/\" target=\"_blank\">Sensirion<\/a>. This is a reasonably priced nice little sensor (3&#215;3 mm<sup>2<\/sup>) with I2C interface and a tolerance on the relative humidity of at most 3% (2% typical) between 20% and 80% RH while the temperature is accurate to 0.4 \u00b0C (0.3 \u00b0C typical) between 5 and 60 \u00b0C.<\/p>\n<p>A minor problem is that it comes in a 6-pad DFN-package (dual flat, no leads) with a pitch of 1 mm. I connected to the chip by soldering very thin wires (a single strand from a relatively thin multi-stranded wire) to the four pads that are used. I then soldered these wires to a small piece of prototype board and added a decoupling capacitor and I2C pull-up resistors (I happened to use 2.2 k\u03a9 and 4.7 k\u03a9 because I had them easily available in suitable form factors). The top side of the package needs to be away from the board to expose the opening of the sensor to the environment. This could potentially lead to short circuits between the board and the bare thin wires, but I put a piece of insulating tape under the sensor to prevent this. See photo below.<\/p>\n<figure id=\"attachment_626\" aria-describedby=\"caption-attachment-626\" style=\"width: 604px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/axotron.se\/blog\/wp-content\/uploads\/2015\/07\/sht21.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-626\" src=\"http:\/\/axotron.se\/blog\/wp-content\/uploads\/2015\/07\/sht21-1024x481.jpg\" alt=\"SHT21 sensor on a prototype board with auxiliary components.\" width=\"604\" height=\"284\" srcset=\"https:\/\/axotron.se\/blog\/wp-content\/uploads\/2015\/07\/sht21-1024x481.jpg 1024w, https:\/\/axotron.se\/blog\/wp-content\/uploads\/2015\/07\/sht21-300x141.jpg 300w, https:\/\/axotron.se\/blog\/wp-content\/uploads\/2015\/07\/sht21.jpg 1500w\" sizes=\"auto, (max-width: 604px) 100vw, 604px\" \/><\/a><figcaption id=\"caption-attachment-626\" class=\"wp-caption-text\">SHT21 sensor (central black square) on a prototype board with a few auxiliary components.<\/figcaption><\/figure>\n<p>I ended up using the pins PTC11 (J1 pin 15, SDA1) and PTC10 (J1 pin 13, SCL1) as the I2C bus on the FRDM-KL25Z. I first tried A4 (J10 pin 10, SDA1) and A5 (J10 pin 12, SCL1), but it turned out that there is a square wave on A5 that obviously ruins the I2C communication. Maybe this could be turned off, but I moved to other pins instead of investigating what caused the square ware.<\/p>\n<p>The SHT21 has a bit of a quirky requirement on the I2C communication, namely that a delay of 20 \u00b5s is recommended between the ACK bit after some command and before issuing the stop condition. This turned out to be straightforward to implement using the mbed I2C library routines, see the file sht21.cpp.<\/p>\n<p>I did not implement a test of the checksum that the SHT21 provides. That can be added later and the easiest is probably to use code from the <a href=\"http:\/\/www.sensirion.com\/nc\/en\/products\/humidity-temperature\/download-center\/?cid=8585&amp;did=102&amp;sechash=03a6edbf\" target=\"_blank\">SHT21 sample code application note<\/a> for this.<\/p>\n<p>A photo of the complete sensor node prototype is shown below.<\/p>\n<figure id=\"attachment_627\" aria-describedby=\"caption-attachment-627\" style=\"width: 604px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/axotron.se\/blog\/wp-content\/uploads\/2015\/07\/frdm_zb_sht21.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-627\" src=\"http:\/\/axotron.se\/blog\/wp-content\/uploads\/2015\/07\/frdm_zb_sht21-864x1024.jpg\" alt=\"ZigBee sensor node based on a FRDM board, an XBee module and a SHT21 humidity sensor.\" width=\"604\" height=\"716\" srcset=\"https:\/\/axotron.se\/blog\/wp-content\/uploads\/2015\/07\/frdm_zb_sht21-864x1024.jpg 864w, https:\/\/axotron.se\/blog\/wp-content\/uploads\/2015\/07\/frdm_zb_sht21-253x300.jpg 253w, https:\/\/axotron.se\/blog\/wp-content\/uploads\/2015\/07\/frdm_zb_sht21.jpg 1266w\" sizes=\"auto, (max-width: 604px) 100vw, 604px\" \/><\/a><figcaption id=\"caption-attachment-627\" class=\"wp-caption-text\">ZigBee sensor node prototype based on a FRDM board, an XBee module and an SHT21 humidity sensor.<\/figcaption><\/figure>\n<h4>Communication Protocol<\/h4>\n<p>To enable the PC to control the sensor node, some form of protocol needs to be devised on top of ZigBee that allows the PC to send requests to the sensor and also allows the sensor to respond.<\/p>\n<p>To send data between the two XBee modules, the &#8220;Transmit Request&#8221; API command is used. This allows messages containing about 80 bytes of payload data to be sent. At the receiving end the messages show up as &#8220;RX Packet&#8221; frames.<\/p>\n<p>I defined the request data packet to just contain two payload bytes, namely an ASCII acronym for the command to be executed. Currently I have two commands defined:<\/p>\n<ul>\n<li>&#8220;ME&#8221; &#8211; perform the measurement and return the data<\/li>\n<li>&#8220;ID&#8221; &#8211; return information about the sensor node<\/li>\n<\/ul>\n<p>The payload of the replies to these commands are defined as follows:<\/p>\n<ul>\n<li>#&lt;command&gt;#&lt;command specific data&gt;<\/li>\n<\/ul>\n<p>For the ME command, the &lt;command specific data&gt; format is:<\/p>\n<ul>\n<li>4 bytes &#8211; the characters &#8220;HTR &#8221; (which stands for Humidity, Temperature and Received Signal Strength Indicator (RSSI)<\/li>\n<li>4 bytes &#8211; a float with the humidity in %RH<\/li>\n<li>4 bytes &#8211; a float with the temperature in \u00b0C<\/li>\n<li>1 byte &#8211; the RSSI in negative dBm<\/li>\n<li>1 byte &#8211; error counter<\/li>\n<\/ul>\n<p>So a reply to the ME command might look like:<\/p>\n<pre>#ME#HTR &lt;a float with the value 47.18&gt;&lt;a float with the value 24.11&gt;&lt;a byte with the value 57&gt;&lt;a byte with the value 0&gt;<\/pre>\n<p>This would mean that the relative humidity is 47.18%, the temperature is 24.11 \u00b0C, the RSSI is -57 dBm and that the error counter is at zero.<\/p>\n<p>For the ID command, the &lt;command specific data&gt; format is:<\/p>\n<ul>\n<li>4 bytes &#8211; ASCII characters\u00a0 containing the node type code<\/li>\n<li>4 bytes &#8211; ASCII characters\u00a0 containing the node revision<\/li>\n<li>? bytes &#8211; An ASCII string with a free text description<\/li>\n<\/ul>\n<p>So this reply is purely in ASCII characters and a reply might look like this:<\/p>\n<pre>#ID#HT\u00a0 0001Humidity and temperature sensor<\/pre>\n<p>This protocol can easily be expanded to more commands and different kinds of replies from different kinds of sensors.<\/p>\n<h4>Sample Terminal Output<\/h4>\n<p>Examples of text printed in the terminal windows (Tera Term for the sensor node and IPython for the Python program) are shown below.<\/p>\n<figure id=\"attachment_647\" aria-describedby=\"caption-attachment-647\" style=\"width: 677px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/axotron.se\/blog\/wp-content\/uploads\/2015\/08\/ipython_api.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-647\" src=\"http:\/\/axotron.se\/blog\/wp-content\/uploads\/2015\/08\/ipython_api.png\" alt=\"Text printed from the Python program.\" width=\"677\" height=\"426\" srcset=\"https:\/\/axotron.se\/blog\/wp-content\/uploads\/2015\/08\/ipython_api.png 677w, https:\/\/axotron.se\/blog\/wp-content\/uploads\/2015\/08\/ipython_api-300x189.png 300w\" sizes=\"auto, (max-width: 677px) 100vw, 677px\" \/><\/a><figcaption id=\"caption-attachment-647\" class=\"wp-caption-text\">Text printed from the Python program.<\/figcaption><\/figure>\n<figure id=\"attachment_648\" aria-describedby=\"caption-attachment-648\" style=\"width: 547px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/axotron.se\/blog\/wp-content\/uploads\/2015\/08\/tera_term_mbed.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-648\" src=\"http:\/\/axotron.se\/blog\/wp-content\/uploads\/2015\/08\/tera_term_mbed.png\" alt=\"Text printed from the sensor node to the USB serial port.\" width=\"547\" height=\"362\" srcset=\"https:\/\/axotron.se\/blog\/wp-content\/uploads\/2015\/08\/tera_term_mbed.png 547w, https:\/\/axotron.se\/blog\/wp-content\/uploads\/2015\/08\/tera_term_mbed-300x199.png 300w\" sizes=\"auto, (max-width: 547px) 100vw, 547px\" \/><\/a><figcaption id=\"caption-attachment-648\" class=\"wp-caption-text\">Text printed from the sensor node to the USB serial port.<\/figcaption><\/figure>\n<h4>Python Code<\/h4>\n<p>The Python code running on the PC starts out by selecting a COM port to use, sets up communication with the XBee module and sends an ID command to the sensor node before it enters into the main loop. The main loop regularly sends ME commands to the sensor. It also prints dots to show that it is running and checks if the q key has been hit, in which case it exits the program.<\/p>\n<p>There is also a function that is called in the background by the xbee library whenever a frame has been received from the XBee. This function takes the proper action based on what kind of frame was received. The information in replies to the ID and ME command are printed out. Also, if there is anything anomalous in the status replies to transmissions, a warning detailing the issue is printed.<\/p>\n<p>The 64-bit address of the sensor node is hard coded into the program (this might be OK for a quick demo or a one-off system, but should be solved in a more flexible manner in the future). The 16-bit address of the sensor node is not known at the beginning, but it is revealed in the first frame it sends back to the PC and so the Python program can store that address and use it in subsequent transmissions to decrease the burden on the ZigBee network.<\/p>\n<p>The Python code can be found below.<\/p>\n<span class=\"collapseomatic \" id=\"id69fefc0b026ed\"  tabindex=\"0\" title=\"Expand the Python code xbee_rx.py\"    >Expand the Python code xbee_rx.py<\/span><div id=\"target-id69fefc0b026ed\" class=\"collapseomatic_content \">\n[python]\n#! \/usr\/bin\/python<\/p>\n<p>&quot;&quot;&quot;<br \/>\nxbee_rx.py<\/p>\n<p>Rev 0.1, 2015-07-31<br \/>\nPer Magnusson, Axotron, axotron.se\/blog<\/p>\n<p>This code is public domain and comes with absolutely no warranty. Enjoy!<\/p>\n<p>Regularly send measurement requests to a remote ZigBee sensor node and display<br \/>\nthe data that comes back.<br \/>\n&quot;&quot;&quot;<\/p>\n<p>from xbee import ZigBee<br \/>\nimport serial<br \/>\nimport serial.tools.list_ports<br \/>\nimport sys<br \/>\nimport time<br \/>\nimport msvcrt<br \/>\nimport struct<\/p>\n<p># Remote node address<br \/>\nlong_addr = &quot;\\x00\\x13\\xA2\\x00\\x40\\xB4\\x51\\x40&quot; # Hard coded for now<br \/>\nshort_addr = &#8216;\\xff\\xfe&#8217; # Use 0xfffe as 16-bit address until it becomes known<\/p>\n<p># Callback function that runs when frames are received<br \/>\ndef receive_data(data):<br \/>\n\u00a0\u00a0\u00a0 global short_addr<br \/>\n\u00a0\u00a0\u00a0 if data[&#8216;id&#8217;] == &#8216;rx_explicit&#8217;:<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if short_addr == &#8216;\\xff\\xfe&#8217; and data[&#8216;source_addr_long&#8217;] == long_addr:<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 short_addr = data[&#8216;source_addr&#8217;]\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print &#8216;Discovered remote 16-bit address: 0x{:02x}{:02x}&#8217;.format(ord(short_addr[0]),<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 ord(short_addr[1]))<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # Transmission from a node<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if data[&#8216;rf_data&#8217;][:4] == &#8216;#ID#&#8217;:<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # Frame with node ID<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print &#8216;\\nNode identifiaction frame&#8217;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print &#8216;Node type: &quot;&#8217; + data[&#8216;rf_data&#8217;][4:8] + &#8216;&quot;&#8217;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print &#8216;Node revision: &quot;&#8217; + data[&#8216;rf_data&#8217;][8:12] + &#8216;&quot;&#8217;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print &#8216;Node description: &quot;&#8217; + data[&#8216;rf_data&#8217;][12:] + &#8216;&quot;&#8217;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 elif data[&#8216;rf_data&#8217;][:4] == &#8216;#ME#&#8217;:<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # Reply to measure command<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print &#8216;Measurement data frame&#8217;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if data[&#8216;rf_data&#8217;][4:8] == &quot;HTR &quot;:<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 humidity = struct.unpack(&#8216;f&#8217;, data[&#8216;rf_data&#8217;][8:12])[0]\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 temperature = struct.unpack(&#8216;f&#8217;, data[&#8216;rf_data&#8217;][12:16])[0]\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 rssi = ord(data[&#8216;rf_data&#8217;][16])<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 err_cnt = ord(data[&#8216;rf_data&#8217;][17])<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print &quot;Humidity: {0:.2f}%, Temperature: {1:.2f} C, RSSI: -{2:d} dBm&quot;.format(humidity,<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 temperature,<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 rssi)<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 else:<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print &#8216;Measurement from unknown node type &quot;{}&quot;&#8217;.format(data[&#8216;rf_data&#8217;][4:8])<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print data<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 else:<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print &#8216;Unknown RX frame type &quot;{}&quot;&#8217;.format(data[&#8216;rf_data&#8217;][:4])<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print data<br \/>\n\u00a0\u00a0\u00a0 elif data[&#8216;id&#8217;] == &#8216;tx_status&#8217;:<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # Status from a transmission<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print &#8216;\\nTX status frame, fid={:d}&#8217;.format(ord(data[&#8216;frame_id&#8217;]))<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if ord(data[&#8216;retries&#8217;]) != 0:<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print &#8216;TX Warning: {:d} retries were done&#8217;.format(ord(data[&#8216;retries&#8217;]))<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if ord(data[&#8216;deliver_status&#8217;]) != 0:<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print &#8216;TX Warning: delivery failed 0x{:x}&#8217;.format(ord(data[&#8216;deliver_status&#8217;]))<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if ord(data[&#8216;discover_status&#8217;]) != 0:<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print &#8216;TX Warning: discovery overhead 0x{:x}&#8217;.format(ord(data[&#8216;discover_status&#8217;]))<br \/>\n\u00a0\u00a0\u00a0 else:<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print &quot;\\nUnknown frame type&quot;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print data<\/p>\n<p># Look for a COM port that might have an XBee connected<br \/>\nportfound = False<br \/>\nports = list(serial.tools.list_ports.comports())<br \/>\nfor p in ports:<br \/>\n\u00a0\u00a0\u00a0 # The SparkFun XBee Explorer USB board uses an FTDI chip as USB interface<br \/>\n\u00a0\u00a0\u00a0 print p<br \/>\n\u00a0\u00a0\u00a0 if &quot;FTDIBUS&quot; in p[2]:<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print &quot;Found possible XBee on &quot; + p[0]\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if not portfound:<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 portname = p[0]\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 portfound = True<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print &quot;Using &quot; + portname + &quot; as XBee COM port.&quot;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 else:<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print &quot;Ignoring this port, using the first one that was found.&quot;<\/p>\n<p>if portfound:<br \/>\n\u00a0\u00a0\u00a0 ser = serial.Serial(portname, 9600)<br \/>\nelse:<br \/>\n\u00a0\u00a0\u00a0 sys.exit(&quot;No serial port seems to have an XBee connected.&quot;)<\/p>\n<p>xbee = ZigBee(ser, callback = receive_data)<br \/>\ncnt = 0<br \/>\ncnt_me = 100<br \/>\nid = 2<\/p>\n<p># Request information about remote node<br \/>\nxbee.send(&quot;tx&quot;, data=&quot;ID&quot;, dest_addr_long=long_addr, dest_addr=short_addr)<br \/>\nprint &quot;Press q to quit&quot;<\/p>\n<p># Main loop<br \/>\nwhile True:<br \/>\n\u00a0\u00a0\u00a0 try:<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if msvcrt.kbhit():<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # Ctrl-C exits the whole shell in IPython on Windows,<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # so make a press on the q-key exit the main loop.<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if msvcrt.getch() == &#8216;q&#8217;:<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print &quot;Exiting loop&quot;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 break<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 time.sleep(0.1)<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 cnt = cnt+1<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 cnt_me = cnt_me+1<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if cnt &gt;= 10:<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # Show that the program is still running<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 sys.stdout.write(&#8216;.&#8217;)<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 sys.stdout.flush()<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 cnt = 0<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if cnt_me &gt;= 100:<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # Send request for measurement data to remote node<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 xbee.send(&quot;tx&quot;, data=&quot;ME&quot;, dest_addr_long=long_addr, dest_addr=short_addr, frame_id=chr(id))<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 id = id+1<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if id &gt; 255:<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 id = 2<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 cnt_me = 0<\/p>\n<p>\u00a0\u00a0\u00a0 except KeyboardInterrupt:<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 print &quot;Ctrl-C was pressed&quot;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 break<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0<br \/>\nxbee.halt()<br \/>\nser.close()<br \/>\n[\/python]\n<\/div>\n<h4>Mbed Code<\/h4>\n<p>The program running on the FRDM-KL25Z in the sensor node is divided into several source code files. It works like this:<\/p>\n<p>An interrupt routine receives the bytes coming from the XBee serial port and when it finds a valid frame, it stores it in an available frame buffer and marks that frame buffer as containing a frame to be processed.<\/p>\n<p>The main program:<\/p>\n<ul>\n<li>sets everything up and then enters the main loop that:<\/li>\n<li>regularly performs the measurements<\/li>\n<li>looks at the frame buffers to see if there is any that contains an unprocessed frame<\/li>\n<li>when it finds a new frame, it processes it (sends the reply to the command, handles the reply to an AT command or sends an ATDB command to get the RSSI value)<\/li>\n<li>prints status information to the PC serial port (for debug purposes)<\/li>\n<\/ul>\n<p>Currently the error counter functionality is not implemented.<\/p>\n<p>The program is divided into several source code files. These files together with the Python program can all be downloaded as a zip archive:<\/p>\n<p><a href=\"http:\/\/axotron.se\/blog\/wp-content\/uploads\/2015\/08\/xbee_api_150731.zip\">XBee sensor source code, 2015-07-31<\/a><\/p>\n<p>The individual files are described further below.<\/p>\n<h5>main.cpp<\/h5>\n<p>This file contains the main part of the program that<\/p>\n<ul>\n<li>sets up communication with the XBee module<\/li>\n<li>sets up the SHT21 sensor<\/li>\n<li>a main loop as described above<\/li>\n<li>code to update the variables with the most recent measurement results<\/li>\n<li>code to parse AT command replies from the XBee module (used to figure out RSSI), handle_at_response()<\/li>\n<li>code to send out the measurement data, send_humidity_temperature()<\/li>\n<li>code to reply to the ID command, send_id()<\/li>\n<li>code to parse the incoming RX frames, handle_rx()<\/li>\n<\/ul>\n<span class=\"collapseomatic \" id=\"id69fefc0b0275a\"  tabindex=\"0\" title=\"Expand main.cpp\"    >Expand main.cpp<\/span><div id=\"target-id69fefc0b0275a\" class=\"collapseomatic_content \">\n[cpp]\n\/*<br \/>\nmain.cpp<\/p>\n<p>Rev 0.1, 2015-07-31<br \/>\nPer Magnusson, Axotron, axotron.se\/blog<\/p>\n<p>This code is public domain and comes with absolutely no warranty. Enjoy!<\/p>\n<p>Sensor node based on an XBee ZigBee module, a FRDM-KL25Z development board<br \/>\nand an SHT21 humidity\/temperature sensor.<\/p>\n<p>This file contains the main program that:<br \/>\n&#8211; sets everything up<br \/>\n&#8211; regularly performs the measurements<br \/>\n&#8211; processes ZigBee frames that the interrupt routine has stored in frame buffers<br \/>\n&#8211; responds to commands from other ZigBee nodes<br \/>\n&#8211; prints status information to the PC serial port<br \/>\n*\/<\/p>\n<p>#include &quot;mbed.h&quot;<br \/>\n#include &quot;serial_buffer.h&quot;<br \/>\n#include &quot;sht21.h&quot;<br \/>\n#include &quot;zigbee.h&quot;<br \/>\n#include &quot;xbee_api.h&quot;<\/p>\n<p>Serial pc(USBTX, USBRX);<br \/>\nSerial xbee(PTE0, PTE1);<br \/>\nDigitalOut redled(LED1);<br \/>\nDigitalOut greenled(LED2);<br \/>\nDigitalOut blueled(LED3);<\/p>\n<p>uint8_t rssi; \/\/ Most recent RSSI value<br \/>\nuint8_t rssi_updated; \/\/ Whether the RSSI value was updated since last time it was read<br \/>\nfloat humidity = -1.0;<br \/>\nfloat temperature = -1000.0;<br \/>\nuint8_t id = 1; \/\/ ID used for TX packets<\/p>\n<p>\/\/ Update the temperature and humidity variables<br \/>\nvoid update_measurements() {<br \/>\n\u00a0\u00a0\u00a0 humidity = sht21_read_humidity();<br \/>\n\u00a0\u00a0\u00a0 temperature = sht21_read_temperature();\u00a0\u00a0 \u00a0<br \/>\n}<\/p>\n<p>\/\/ Process a frame that was a response to an AT command<br \/>\nvoid handle_at_response(const uint8_t *buffer) {<br \/>\n\u00a0\u00a0\u00a0 uint32_t length;<br \/>\n\u00a0\u00a0\u00a0 uint8_t frame_type;<br \/>\n\u00a0\u00a0\u00a0 uint8_t status;<br \/>\n\u00a0\u00a0\u00a0 uint32_t at;<br \/>\n\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 length = zb_get_buffer_length(buffer);<br \/>\n\u00a0\u00a0\u00a0 frame_type = buffer[ZB_OFFS_FRAME_TYPE];<br \/>\n\u00a0\u00a0\u00a0 if (frame_type != ZB_FRAME_TYPE_AT_RESPONSE) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Not the expected frame type for this handler!<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;Incorrect frame type 0x%02x for at-response handler\\n&quot;, (uint32_t)frame_type);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return;<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0 status = buffer[ZB_OFFS_AT_RESP_STATUS];<br \/>\n\u00a0\u00a0\u00a0 if (status != 0) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Status is not OK<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;Status is not OK (0x%02x) in at-response\\n&quot;, (uint32_t)status);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return;<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 at = zb_get_at(buffer);<br \/>\n\u00a0\u00a0\u00a0 if (at == ZB_ATDB) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Response to ATDB command<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (length != 6) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Unexpected length of ATDB response<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;Unexpected length (%u) of at-response\\n&quot;, length);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 rssi = buffer[ZB_OFFS_AT_RESP_DATA0];<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 rssi_updated = 1;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;RSSI = -%u dBm\\n&quot;, (uint32_t)rssi);<br \/>\n\u00a0\u00a0\u00a0 } else {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;No handler for response to AT%c%c\\n&quot;,<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 buffer[ZB_OFFS_AT_RESP_AT0], buffer[ZB_OFFS_AT_RESP_AT1]);<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n}<\/p>\n<p>\/\/ Send a TX packet with humidity, temperature and RSSI information<br \/>\nvoid send_humidity_temperature(const uint8_t *dest64, const uint8_t *dest16) {<br \/>\n\u00a0\u00a0\u00a0 humidity_data_t data;<br \/>\n\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 \/\/ Make sure measurements are OK<br \/>\n\u00a0\u00a0\u00a0 if(humidity &lt; 0) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 update_measurements();<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 data.cmd_reply[0] = &#8216;#&#8217;; \/\/ Shows what command we are responding to<br \/>\n\u00a0\u00a0\u00a0 data.cmd_reply[1] = &#8216;M&#8217;;<br \/>\n\u00a0\u00a0\u00a0 data.cmd_reply[2] = &#8216;E&#8217;;<br \/>\n\u00a0\u00a0\u00a0 data.cmd_reply[3] = &#8216;#&#8217;;<br \/>\n\u00a0\u00a0\u00a0 data.sensor_data_type[0] = &#8216;H&#8217;; \/\/ Humidity<br \/>\n\u00a0\u00a0\u00a0 data.sensor_data_type[1] = &#8216;T&#8217;; \/\/ Temperature<br \/>\n\u00a0\u00a0\u00a0 data.sensor_data_type[2] = &#8216;R&#8217;; \/\/ RSSI<br \/>\n\u00a0\u00a0\u00a0 data.sensor_data_type[3] = &#8216; &#8216;;<br \/>\n\u00a0\u00a0\u00a0 data.humidity = humidity;<br \/>\n\u00a0\u00a0\u00a0 data.temperature = temperature;<br \/>\n\u00a0\u00a0\u00a0 data.rssi = rssi;<br \/>\n\u00a0\u00a0\u00a0 data.err_cnt = 0;<br \/>\n\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 id++; \/\/ Increase frame ID<br \/>\n\u00a0\u00a0\u00a0 if(id &lt; 2) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 id = 2; \/\/ Never let it be less than 2<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0 \/\/ Send the data<br \/>\n\u00a0\u00a0\u00a0 zb_send_tx_req(id, dest64, dest16, (uint8_t *)&amp;data, sizeof(data));<br \/>\n}<\/p>\n<p>\/\/ Send a TX packet identifying this sensor node type<br \/>\nvoid send_id(const uint8_t *dest64, const uint8_t *dest16) {<br \/>\n\u00a0\u00a0\u00a0 \/\/ Data format:<br \/>\n\u00a0\u00a0\u00a0 \/\/ 4 bytes &#8211; The command this is a response to, surrounded by # signs<br \/>\n\u00a0\u00a0\u00a0 \/\/ 4 bytes &#8211; The node type code<br \/>\n\u00a0\u00a0\u00a0 \/\/ 4 bytes &#8211; The node revision<br \/>\n\u00a0\u00a0\u00a0 \/\/ ? bytes &#8211; A free text description<br \/>\n\u00a0\u00a0\u00a0 char reply[] = &quot;#ID#&quot; &quot;HT\u00a0 &quot; &quot;0001&quot; &quot;Humidity and temperature sensor&quot;;<br \/>\n\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 id++; \/\/ Increase frame ID<br \/>\n\u00a0\u00a0\u00a0 if(id &lt; 2) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 id = 2; \/\/ Never let it be less than 2<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0 \/\/ Send the data<br \/>\n\u00a0\u00a0\u00a0 \/\/ Do not include the trailing nul in the string<br \/>\n\u00a0\u00a0\u00a0 zb_send_tx_req(id, dest64, dest16, (uint8_t *)&amp;reply, sizeof(reply)-1);<br \/>\n}<\/p>\n<p>\/\/ Process a frame containing an RX packet<br \/>\nvoid handle_rx(const uint8_t *buffer) {<br \/>\n\u00a0\u00a0\u00a0 uint8_t frame_type;<br \/>\n\u00a0\u00a0\u00a0 uint32_t cmd;<br \/>\n\u00a0\u00a0\u00a0 uint32_t length, data_len;<br \/>\n\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 pc.printf(&quot;RX handler\\n&quot;);<br \/>\n\u00a0\u00a0\u00a0 length = zb_get_buffer_length(buffer);<br \/>\n\u00a0\u00a0\u00a0 frame_type = buffer[ZB_OFFS_FRAME_TYPE];<br \/>\n\u00a0\u00a0\u00a0 if (frame_type != ZB_FRAME_TYPE_RX_RECEIVED) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Not the expected frame type for this handler!<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;Incorrect frame type 0x%02x for RX handler\\n&quot;, (uint32_t)frame_type);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return;<br \/>\n\u00a0\u00a0\u00a0 }<\/p>\n<p>\u00a0\u00a0\u00a0 \/\/ Ignore receive options field<br \/>\n\u00a0\u00a0\u00a0 data_len = length &#8211; ZB_OFFS_RX_DATA_0 + 3;<br \/>\n\u00a0\u00a0\u00a0 if (data_len &lt; 2) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;Too little data in RX packet\\n&quot;);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return;<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0 cmd = zb_get16(buffer, ZB_OFFS_RX_DATA_0);<br \/>\n\u00a0\u00a0\u00a0 if (cmd == ZB_CMD_ME) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ ME Measure command<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Send a response packet with the measurement data.<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 blueled = 0;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;Sending reply to ME\\n&quot;);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 send_humidity_temperature(buffer+ZB_OFFS_RX_SRC64_0, buffer+ZB_OFFS_RX_SRC16_0);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 blueled = 1;<br \/>\n\u00a0\u00a0\u00a0 } else if (cmd == ZB_CMD_ID) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ ID, identify command<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Send a response<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 blueled = 0;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;Sending reply to ID\\n&quot;);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 send_id(buffer+ZB_OFFS_RX_SRC64_0, buffer+ZB_OFFS_RX_SRC16_0);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 blueled = 1;<br \/>\n\u00a0\u00a0\u00a0 } else {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 redled = 0;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;No handler for RX command %c%c\\n&quot;,<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 buffer[ZB_OFFS_RX_DATA_0], buffer[ZB_OFFS_RX_DATA_0+1]);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 redled = 1;<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n}<\/p>\n<p>int main() {<br \/>\n\u00a0\u00a0\u00a0 uint8_t frame_type;<br \/>\n\u00a0\u00a0\u00a0 Timer rssi_timer, humidity_timer, data_timer;<br \/>\n\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 redled = 1;\u00a0\u00a0\u00a0 \/\/ led off<br \/>\n\u00a0\u00a0\u00a0 blueled = 1;\u00a0\u00a0 \/\/ led off<br \/>\n\u00a0\u00a0\u00a0 greenled = 0;\u00a0 \/\/ led on<br \/>\n\u00a0\u00a0\u00a0 wait(1);<br \/>\n\u00a0\u00a0\u00a0 pc.printf(&quot;\\n\\nZigBee sensor node test program\\n&quot;);<br \/>\n\u00a0\u00a0\u00a0 xbee.baud(9600);<br \/>\n\u00a0\u00a0\u00a0 xbee.attach(&amp;rx_interrupt, Serial::RxIrq);<\/p>\n<p>\u00a0\u00a0\u00a0 greenled = 1; \/\/ led off<\/p>\n<p>\u00a0\u00a0\u00a0 pc.printf(&quot;Setting up I2C\\n&quot;);<br \/>\n\u00a0\u00a0\u00a0 while (!sht21_setup()) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;SHT21 setup failed\\n&quot;);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 redled = 0;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 wait(1);<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0 redled = 1;<br \/>\n\u00a0\u00a0\u00a0 pc.printf(&quot;SHT21 setup successful\\n&quot;);<br \/>\n\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 humidity_timer.start();<br \/>\n\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 \/\/ Main loop, look for frames coming from the XBee module<br \/>\n\u00a0\u00a0\u00a0 while (1) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Check if there is any complete frame in any buffer<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 for (int ii=0; ii&lt;n_rx_buffers; ii++) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (buffer_used[ii] == 2) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Found a complete frame, process it \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 frame_type = rx_buffer[ii][ZB_OFFS_FRAME_TYPE]; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 switch (frame_type) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 case ZB_FRAME_TYPE_AT_RESPONSE: \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Response from AT command \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 handle_at_response(rx_buffer[ii]); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 break; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 case ZB_FRAME_TYPE_TX_RESPONSE: \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Response from TX command \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;Response from TX\\n&quot;); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 zb_send_simple_at(&quot;DB&quot;, 1); \/\/ Update RSSI \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 break; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 case ZB_FRAME_TYPE_RX_RECEIVED: \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Incoming RX frame \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 handle_rx(rx_buffer[ii]); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 break; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ The frame has been processed, mark it as empty \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 buffer_used[ii] = 0; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Timer controlled activities \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if(humidity_timer.read() &gt; 10.0) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Read humidity and temperature<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 greenled = 0;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 update_measurements();<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;Relative humidity = %.2f%% &quot;, humidity);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;, Temperature = %.2f C\\n&quot;, temperature);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 humidity_timer.reset();<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 greenled = 1;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n}<br \/>\n[\/cpp]\n<\/div>\n<h5>xbee_api.h<\/h5>\n<p>This header file declares some global variables from main.cpp and typedefs a struct used for the data format in replies to the ME command.<\/p>\n<span class=\"collapseomatic \" id=\"id69fefc0b02788\"  tabindex=\"0\" title=\"Expand xbee_api.h\"    >Expand xbee_api.h<\/span><div id=\"target-id69fefc0b02788\" class=\"collapseomatic_content \">\n[cpp]\n\/*<br \/>\nxbee_api.h<\/p>\n<p>Rev 0.1, 2015-07-31<br \/>\nPer Magnusson, Axotron, axotron.se\/blog<\/p>\n<p>This code is public domain and comes with absolutely no warranty. Enjoy!<\/p>\n<p>Declarations for the main part of the program.<br \/>\n*\/<\/p>\n<p>#include &quot;mbed.h&quot;<\/p>\n<p>extern Serial pc;<br \/>\nextern Serial xbee;<br \/>\nextern DigitalOut redled;<br \/>\nextern DigitalOut greenled;<br \/>\nextern DigitalOut blueled; <\/p>\n<p>typedef struct humidity_data_t {<br \/>\n\u00a0\u00a0\u00a0 char cmd_reply[4];<br \/>\n\u00a0\u00a0\u00a0 char sensor_data_type[4];<br \/>\n\u00a0\u00a0\u00a0 float humidity;<br \/>\n\u00a0\u00a0\u00a0 float temperature;<br \/>\n\u00a0\u00a0\u00a0 uint8_t rssi;<br \/>\n\u00a0\u00a0\u00a0 uint8_t err_cnt;<br \/>\n} humidity_data_t;<br \/>\n[\/cpp]\n<\/div>\n<h5>serial_buffer.cpp<\/h5>\n<p>This file contains the interrupt routine rx_interrupt() which is called when data arrives from the XBee serial port. The data is parsed and if it is found to be a valid ZigBee frame, it is stored in a free frame buffer. The buffer is then marked as containing a valid frame to be processed (by the main loop).<\/p>\n<span class=\"collapseomatic \" id=\"id69fefc0b027ad\"  tabindex=\"0\" title=\"Expand serial_buffer.cpp\"    >Expand serial_buffer.cpp<\/span><div id=\"target-id69fefc0b027ad\" class=\"collapseomatic_content \">\n[cpp]\n\/*<br \/>\nserial_buffer.cpp<\/p>\n<p>Rev 0.1, 2015-07-31<br \/>\nPer Magnusson, Axotron, axotron.se\/blog<\/p>\n<p>This code is public domain and comes with absolutely no warranty. Enjoy!<\/p>\n<p>Frame buffers for data from the XBee module.<\/p>\n<p>rx_interrupt() is called when data arrives from the XBee serial port. The data is parsed<br \/>\nand if it is found to be a valid ZigBee frame, it is stored in a free frame buffer.<br \/>\nThe buffer is then marked as containing a valid frame to be processed (by the main loop).<br \/>\n*\/<\/p>\n<p>#include &quot;mbed.h&quot;<br \/>\n#include &quot;serial_buffer.h&quot;<br \/>\n#include &quot;xbee_api.h&quot;<br \/>\n#include &quot;zigbee.h&quot;<\/p>\n<p>\/\/ Frame buffers for XBee RX data &#8211; used by interrupt routines<\/p>\n<p>uint8_t rx_buffer[n_rx_buffers][buffer_size+1]; \/\/ The received frames are stored here<br \/>\n\/\/ buffer_used[]:<br \/>\n\/\/ 0 &#8211; not used<br \/>\n\/\/ 1 &#8211; being filled up<br \/>\n\/\/ 2 &#8211; filled with a frame, waiting to be processed by main loop<br \/>\n\/\/ (variables with static storage are initilized to all zeros)<br \/>\nint buffer_used[n_rx_buffers];<br \/>\nvolatile int rx_ptr=0;<\/p>\n<p>\/\/ Interrupt routine to read frame data from the serial port into frame buffers.<br \/>\nvoid rx_interrupt() {<br \/>\n\u00a0\u00a0\u00a0 \/\/ State:<br \/>\n\u00a0\u00a0\u00a0 \/\/ 0 = waiting for start delimiter of new frame,<br \/>\n\u00a0\u00a0\u00a0 \/\/ 1 = inside frame, storing it in a buffer<br \/>\n\u00a0\u00a0\u00a0 \/\/ 2 = inside frame, dropping it<br \/>\n\u00a0\u00a0\u00a0 static int state = 0;<br \/>\n\u00a0\u00a0\u00a0 static int current_buffer;<br \/>\n\u00a0\u00a0\u00a0 static uint32_t length;<br \/>\n\u00a0\u00a0\u00a0 uint8_t cur_byte;<br \/>\n\u00a0\u00a0\u00a0 static uint8_t sum; \/\/ For checksum calculation<br \/>\n\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 while (xbee.readable()) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 cur_byte = ((int)xbee.getc())&amp;0xff;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (state == 0) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Waiting for a start delimiter<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (cur_byte != ZB_START_DELIM) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Not a start delimiter. This is a bit unexpected.<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Just eat it and wait for the next byte.<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } else {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Got a start delimiter.<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Find a buffer to store it in.<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 current_buffer = -1;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 for (int ii=0; ii &lt; n_rx_buffers; ii++) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (!buffer_used[ii]) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Found an empty buffer<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 current_buffer = ii;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 state = 1;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 buffer_used[ii] = 1;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 rx_ptr = 0;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 rx_buffer[current_buffer][rx_ptr++] = cur_byte;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 break;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (current_buffer &lt; 0) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ There was no free buffer! \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Ignore this packet and wait for a new one. \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;Error: Discarding frame, no free buffers.\\n&quot;); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 state = 2; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } else if (state == 1) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Copying a frame into a buffer \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (rx_ptr == 1) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ MSB of length \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 rx_buffer[current_buffer][rx_ptr++] = cur_byte; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } else if (rx_ptr == 2) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ LSB of length \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 rx_buffer[current_buffer][rx_ptr++] = cur_byte; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 length = zb_get_buffer_length(rx_buffer[current_buffer]); \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 sum = 0; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (length + 4 &gt; buffer_size) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Ooops! The frame is bigger than the buffer, this is very unexpected.<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ We need to discard this frame.<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;The frame is bigger than the buffer (%u), discarding it.\\n&quot;, length);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 buffer_used[current_buffer] = 0;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 state = 2;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } else if (length &gt; 0) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ This is a byte inside the frame<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 sum += cur_byte;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 rx_buffer[current_buffer][rx_ptr++] = cur_byte;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 length&#8211;;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } else {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ The checksum byte<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 sum += cur_byte;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 rx_buffer[current_buffer][rx_ptr++] = cur_byte;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 state = 0;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (sum != 0xff) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Checksum error, discard frame<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;Frame checksum error\\n&quot;);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 buffer_used[current_buffer] = 0;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } else {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Checksum OK, mark buffer as ready to be processed<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 buffer_used[current_buffer] = 2;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 state = 0;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } else {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Discarding incoming frame<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (rx_ptr==1) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ MSB of length<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 length = ((uint32_t)cur_byte)&lt;&lt;8; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } else if (rx_ptr==2) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ LSB of length \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 length += cur_byte; \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (length &gt; 102) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Length is unrealistic, we do not want to wait for<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ so many bytes before starting to look for a new frame.<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Start looking for start of a new frame immediately.<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 length = 0;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } else if (length &gt; 0) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ A byte inside the frame<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 length&#8211;;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 } else {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Finally the checksum, change state<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 state = 0;<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n}<br \/>\n[\/cpp]\n<\/div>\n<h5>serial_buffer.h<\/h5>\n<p>Header file for serial_buffer.cpp<\/p>\n<span class=\"collapseomatic \" id=\"id69fefc0b027d3\"  tabindex=\"0\" title=\"Expand serial_buffer.h\"    >Expand serial_buffer.h<\/span><div id=\"target-id69fefc0b027d3\" class=\"collapseomatic_content \">\n[cpp]\n\/*<br \/>\nserial_buffer.h<\/p>\n<p>Rev 0.1, 2015-07-31<br \/>\nPer Magnusson, Axotron, axotron.se\/blog<\/p>\n<p>This code is public domain and comes with absolutely no warranty. Enjoy!<\/p>\n<p>Frame buffers for data from the XBee module.<\/p>\n<p>rx_interrupt() is called when data arrives from the XBee serial port. The data is parsed<br \/>\nand if it is found to be a valid ZigBee frame, it is stored in a free frame buffer.<br \/>\nThe buffer is then marked as containing a valid frame to be processed (by the main loop).<br \/>\n*\/<\/p>\n<p>void rx_interrupt();<\/p>\n<p>\/\/ ZigBee frame buffers<br \/>\nconst int buffer_size = 256;<br \/>\nconst int n_rx_buffers = 4;<br \/>\n\/\/ The received frames are stored here<br \/>\nextern uint8_t rx_buffer[n_rx_buffers][buffer_size+1];<br \/>\n\/\/ buffer_used[]:<br \/>\n\/\/ 0 &#8211; not used<br \/>\n\/\/ 1 &#8211; being filled up<br \/>\n\/\/ 2 &#8211; filled with a frame, waiting to be processed by main loop<br \/>\nextern int buffer_used[n_rx_buffers];<br \/>\n[\/cpp]\n<\/div>\n<h5>sht21.cpp<\/h5>\n<p>Functions to talk to an SHT21 humidity and temperature sensor via I2C.<\/p>\n<span class=\"collapseomatic \" id=\"id69fefc0b027f5\"  tabindex=\"0\" title=\"Expand sht21.cpp\"    >Expand sht21.cpp<\/span><div id=\"target-id69fefc0b027f5\" class=\"collapseomatic_content \">\n[cpp]\n\/*<br \/>\nsht21.cpp<\/p>\n<p>Rev 0.1, 2015-07-31<br \/>\nPer Magnusson, Axotron, axotron.se\/blog<\/p>\n<p>This code is public domain and comes with absolutely no warranty. Enjoy!<\/p>\n<p>Functions to talk to an SHT21 humidity and temperature sensor via I2C.<br \/>\nSome of the code is adapted from https:\/\/developer.mbed.org\/handbook\/I2C<br \/>\n*\/<\/p>\n<p>#include &quot;mbed.h&quot;<br \/>\n#include &quot;xbee_api.h&quot;<\/p>\n<p>I2C i2c(PTC11, PTC10);<br \/>\n\u00a0<br \/>\nstatic const int sht21_addr = 0x80;<br \/>\nstatic const uint8_t i2c_cmd_trig_t = 0xf3;<br \/>\nstatic const uint8_t i2c_cmd_trig_h = 0xf5;<br \/>\nstatic const uint8_t i2c_cmd_write_reg = 0xe6;<br \/>\nstatic const uint8_t i2c_cmd_read_reg = 0xe7;<br \/>\nstatic const uint8_t i2c_cmd_reset = 0xfe;<\/p>\n<p>static uint8_t sht21_default_reg = 0;<\/p>\n<p>\/\/ Initialize the SHT21 sensor. Returns true on success.<br \/>\nint sht21_setup() {<br \/>\n\u00a0\u00a0\u00a0 char data[2];<br \/>\n\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 i2c.frequency(200000); \/\/ SHT21 supports up to 400 kHz<br \/>\n\u00a0\u00a0\u00a0 \/\/ Soft reset<br \/>\n\u00a0\u00a0\u00a0 data[0] = i2c_cmd_reset;<br \/>\n\u00a0\u00a0\u00a0 if(i2c.write(sht21_addr, data, 1)) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;I2C Error: Did not receive ACK on soft reset.\\n&quot;);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return 0;<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0 wait_ms(16); \/\/ Soft reset takes less than 15 ms<br \/>\n\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 \/\/ Read user register to figure out the default bits<br \/>\n\u00a0\u00a0\u00a0 data[0] = i2c_cmd_read_reg;<br \/>\n\u00a0\u00a0\u00a0 if(i2c.write(sht21_addr, data, 1)) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;I2C Error: Did not receive ACK on read reg 1.\\n&quot;);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return 0;<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0 if(i2c.read(sht21_addr, data, 1)) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;I2C Error: Did not receive ACK on read reg 2.\\n&quot;);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return 0;<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0 \/\/ Mask out the default bit pattern for reserved bits 3, 4, 5<br \/>\n\u00a0\u00a0\u00a0 sht21_default_reg = data[0] &amp; 0x34;<br \/>\n\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 \/\/ Set it to maximum resolution<br \/>\n\u00a0\u00a0\u00a0 data[0] = i2c_cmd_write_reg;<br \/>\n\u00a0\u00a0\u00a0 data[1] = sht21_default_reg | 0x02; \/\/ max resolution, disable heater, disable OTP reload<br \/>\n\u00a0\u00a0\u00a0 if(i2c.write(sht21_addr, data, 1)) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;I2C Error: Did not receive ACK on write reg.\\n&quot;);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return 0;<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0 return 1;<br \/>\n}\u00a0\u00a0 \u00a0<\/p>\n<p>\u00a0\u00a0 \u00a0<br \/>\n\/\/ Read the humidity from the SHT21.<br \/>\n\/\/ The humidity is returned as a float (%RH).<br \/>\n\/\/ Returns a large negative value if an error occured.<br \/>\nfloat sht21_read_humidity() {<br \/>\n\u00a0\u00a0\u00a0 char data[3];<br \/>\n\u00a0\u00a0\u00a0 int32_t humidity;<br \/>\n\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 \/\/ Read user register to figure out default bits<br \/>\n\u00a0\u00a0\u00a0 data[0] = i2c_cmd_trig_h;<br \/>\n\u00a0\u00a0\u00a0 if(i2c.write(sht21_addr, data, 1, true)) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;I2C Error: Did not receive ACK on humidity trigger.\\n&quot;);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 wait_us(20);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 i2c.stop();<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return -(1&lt;&lt;24);<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0 \/\/ Need to send the stop condition a little later<br \/>\n\u00a0\u00a0\u00a0 wait_us(20);<br \/>\n\u00a0\u00a0\u00a0 i2c.stop();<br \/>\n\u00a0\u00a0\u00a0 wait_ms(30); \/\/ Wait until we are sure the conversion is done (29 ms maximum)<br \/>\n\u00a0\u00a0\u00a0 if(i2c.read(sht21_addr, data, 3)) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;I2C Error: Did not receive ACK on read humidity.\\n&quot;);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return -(1&lt;&lt;24);<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0 \/\/ Ignore checksum testing<br \/>\n\u00a0\u00a0\u00a0 humidity = ((((int32_t)data[0])&amp;0xff)&lt;&lt;8) + (data[1]&amp;0xfc); \/\/ Combine bytes and clear status bits<br \/>\n\u00a0\u00a0\u00a0 return 125.0*humidity\/65536.0 &#8211; 6.0;<br \/>\n}<\/p>\n<p>\/\/ Read the temperature from the SHT21.<br \/>\n\/\/ The temperature is returned as a float (degrees C).<br \/>\n\/\/ Returns a large negative value if an error occured.<br \/>\nfloat sht21_read_temperature() {<br \/>\n\u00a0\u00a0\u00a0 char data[3];<br \/>\n\u00a0\u00a0\u00a0 int32_t temperature;<br \/>\n\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 \/\/ Read user register to figure out default bits<br \/>\n\u00a0\u00a0\u00a0 data[0] = i2c_cmd_trig_t;<br \/>\n\u00a0\u00a0\u00a0 if(i2c.write(sht21_addr, data, 1, true)) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;I2C Error: Did not receive ACK on temperature trigger.\\n&quot;);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 wait_us(20);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 i2c.stop();<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return -(1&lt;&lt;24);<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0 \/\/ Need to send the stop condition a little later<br \/>\n\u00a0\u00a0\u00a0 wait_us(20);<br \/>\n\u00a0\u00a0\u00a0 i2c.stop();<br \/>\n\u00a0\u00a0\u00a0 wait_ms(90); \/\/ Wait until we are sure the conversion is done (85 ms maximum)<br \/>\n\u00a0\u00a0\u00a0 if(i2c.read(sht21_addr, data, 3)) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;I2C Error: Did not receive ACK on read temperature.\\n&quot;);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 return -(1&lt;&lt;24);<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0 \/\/ Ignore checksum testing<br \/>\n\u00a0\u00a0\u00a0 temperature = ((((int32_t)data[0])&amp;0xff)&lt;&lt;8) + (data[1]&amp;0xfc); \/\/ Combine bytes and clear status bits<br \/>\n\u00a0\u00a0\u00a0 return 175.72*temperature\/65536.0 &#8211; 46.85;<br \/>\n}<br \/>\n[\/cpp]\n<\/div>\n<h5>sht21.h<\/h5>\n<p>Header file for sht21.cpp<\/p>\n<span class=\"collapseomatic \" id=\"id69fefc0b02815\"  tabindex=\"0\" title=\"Expand sht21.h\"    >Expand sht21.h<\/span><div id=\"target-id69fefc0b02815\" class=\"collapseomatic_content \">\n[cpp]\n\/*<br \/>\nsht21.h<\/p>\n<p>Rev 0.1, 2015-07-31<br \/>\nPer Magnusson, Axotron, axotron.se\/blog<\/p>\n<p>Header file for routines for SHT21 I2C humidity and temperature sensor.<br \/>\n*\/<\/p>\n<p>int sht21_setup();<br \/>\nfloat sht21_read_humidity();<br \/>\nfloat sht21_read_temperature();<br \/>\n[\/cpp]\n<\/div>\n<h5>zigbee.cpp<\/h5>\n<p>Functions to deal with the XBee ZigBee module. This includes functions to create and send various kinds of API frames as well as to get information out of received frames.<\/p>\n<span class=\"collapseomatic \" id=\"id69fefc0b02835\"  tabindex=\"0\" title=\"Expand zigbee.cpp\"    >Expand zigbee.cpp<\/span><div id=\"target-id69fefc0b02835\" class=\"collapseomatic_content \">\n[cpp]\n\/*<br \/>\nzigbee.cpp<\/p>\n<p>Rev 0.1, 2015-07-31<br \/>\nPer Magnusson, Axotron, axotron.se\/blog<\/p>\n<p>Functions to deal with the XBee ZigBee module.<br \/>\n*\/<\/p>\n<p>#include &quot;xbee_api.h&quot;<br \/>\n#include &quot;zigbee.h&quot;<\/p>\n<p>\/\/ Calculate and set the checksum of the ZB buffer pointed to by buffer with the length len.<br \/>\n\/\/ len is the number of bytes in the buffer, including start delimiter and length.<br \/>\nvoid zb_set_checksum(uint8_t *buffer, int len) {<br \/>\n\u00a0\u00a0\u00a0 uint8_t sum = 0;<br \/>\n\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 for(int ii = ZB_OFFS_LEN_LSB+1; ii &lt; len; ii++) { \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 sum += buffer[ii]; \u00a0\u00a0\u00a0 } \u00a0\u00a0\u00a0 buffer[len] = 0xff &#8211; sum; } \u00a0\u00a0 \u00a0 \/\/ Set the length field of the ZigBee buffer. \/\/ len is the number of bytes in the buffer, including start delimiter and length, \/\/ but excluding the checksum void zb_set_len(uint8_t *buffer, uint32_t len) { \u00a0\u00a0\u00a0 uint32_t len_val; \u00a0\u00a0 \u00a0 \u00a0\u00a0\u00a0 len_val = len &#8211; 3; \u00a0\u00a0\u00a0 buffer[ZB_OFFS_LEN_MSB] = len_val &gt;&gt; 8;<br \/>\n\u00a0\u00a0\u00a0 buffer[ZB_OFFS_LEN_LSB] = len_val &amp; 0xff;<br \/>\n}<\/p>\n<p>\/\/ Send a buffer containing a ZigBee frame to the XBee module<br \/>\nvoid zb_send_buffer(const uint8_t *buffer) {<br \/>\n\u00a0\u00a0\u00a0 uint32_t len;<\/p>\n<p>\u00a0\u00a0\u00a0 len = zb_get_buffer_length(buffer) + 4;<br \/>\n\u00a0\u00a0\u00a0 pc.printf(&quot;Sending frame: &quot;);\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 for(int ii=0; ii &lt; len; ii++) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 pc.printf(&quot;%02x &quot;, (uint32_t)buffer[ii]);<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 xbee.putc(buffer[ii]);<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0 pc.printf(&quot;\\n&quot;);\u00a0\u00a0 \u00a0<br \/>\n}<\/p>\n<p>\/\/ Send an AT command without parameter<br \/>\nvoid zb_send_simple_at(const char *at, char frame_id) {<br \/>\n\u00a0\u00a0\u00a0 uint8_t buffer[8];<br \/>\n\u00a0\u00a0\u00a0 uint32_t bufp = 0;<br \/>\n\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 buffer[0] = ZB_START_DELIM;<br \/>\n\u00a0\u00a0\u00a0 buffer[ZB_OFFS_FRAME_TYPE] = ZB_FRAME_TYPE_AT;<br \/>\n\u00a0\u00a0\u00a0 buffer[ZB_OFFS_FRAME_ID] = frame_id;<br \/>\n\u00a0\u00a0\u00a0 buffer[ZB_OFFS_AT_CMD_AT0] = at[0];<br \/>\n\u00a0\u00a0\u00a0 buffer[ZB_OFFS_AT_CMD_AT1] = at[1];<br \/>\n\u00a0\u00a0\u00a0 bufp = ZB_OFFS_AT_CMD_AT1+1;<br \/>\n\u00a0\u00a0\u00a0 zb_set_checksum(buffer, bufp);<br \/>\n\u00a0\u00a0\u00a0 zb_set_len(buffer, bufp);<br \/>\n\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 zb_send_buffer(buffer);\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0<br \/>\n}<\/p>\n<p>\/\/ Send an AT command with a single byte as parameter<br \/>\nvoid zb_send_at_byte_param(const char *at, uint8_t param) {<br \/>\n\u00a0\u00a0\u00a0 uint8_t buffer[9];<br \/>\n\u00a0\u00a0\u00a0 uint32_t bufp = 0;<br \/>\n\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 buffer[0] = ZB_START_DELIM;<br \/>\n\u00a0\u00a0\u00a0 buffer[ZB_OFFS_FRAME_TYPE] = ZB_FRAME_TYPE_AT;<br \/>\n\u00a0\u00a0\u00a0 buffer[ZB_OFFS_FRAME_ID] = 0;<br \/>\n\u00a0\u00a0\u00a0 buffer[ZB_OFFS_AT_CMD_AT0] = at[0];<br \/>\n\u00a0\u00a0\u00a0 buffer[ZB_OFFS_AT_CMD_AT1] = at[1];<br \/>\n\u00a0\u00a0\u00a0 bufp = ZB_OFFS_AT_CMD_AT1+1;<br \/>\n\u00a0\u00a0\u00a0 buffer[bufp++] = param;<br \/>\n\u00a0\u00a0\u00a0 zb_set_checksum(buffer, bufp);<br \/>\n\u00a0\u00a0\u00a0 zb_set_len(buffer, bufp);<br \/>\n\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 zb_send_buffer(buffer);\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0<br \/>\n}<\/p>\n<p>\/\/ Send a transmit request frame<br \/>\nvoid zb_send_tx_req(uint8_t id, const uint8_t *dest64, const uint8_t *dest16, const uint8_t *data, int data_len) {<br \/>\n\u00a0\u00a0\u00a0 uint8_t buffer[18+82]; \/\/ How much payload can there be?<br \/>\n\u00a0\u00a0\u00a0 int bufp = 0;<br \/>\n\u00a0\u00a0 \u00a0<br \/>\n\u00a0\u00a0\u00a0 buffer[0] = ZB_START_DELIM;<br \/>\n\u00a0\u00a0\u00a0 buffer[ZB_OFFS_FRAME_TYPE] = ZB_FRAME_TYPE_TX_REQ;<br \/>\n\u00a0\u00a0\u00a0 buffer[ZB_OFFS_FRAME_ID] = id;<br \/>\n\u00a0\u00a0\u00a0 for(int ii=0; ii&lt;8; ii++) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 buffer[ZB_OFFS_TX_DEST64_0+ii] = dest64[ii];<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0 buffer[ZB_OFFS_TX_DEST16_0] = dest16[0];<br \/>\n\u00a0\u00a0\u00a0 buffer[ZB_OFFS_TX_DEST16_1] = dest16[1];<br \/>\n\u00a0\u00a0\u00a0 buffer[ZB_OFFS_TX_RADIUS] = 0;<br \/>\n\u00a0\u00a0\u00a0 buffer[ZB_OFFS_TX_OPTIONS] = 0;<br \/>\n\u00a0\u00a0\u00a0 bufp = ZB_OFFS_TX_DATA_0;<br \/>\n\u00a0\u00a0\u00a0 for(int ii=0; ii&lt;data_len; ii++) {<br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 buffer[bufp++] = data[ii];<br \/>\n\u00a0\u00a0\u00a0 }<br \/>\n\u00a0\u00a0\u00a0 zb_set_checksum(buffer, bufp);<br \/>\n\u00a0\u00a0\u00a0 zb_set_len(buffer, bufp);<br \/>\n\u00a0\u00a0\u00a0 zb_send_buffer(buffer);\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0<br \/>\n}<\/p>\n<p>\/\/ Return two bytes (MSB and LSB) from a uint8_t array as an unsigned int<br \/>\nuint32_t zb_get16(const uint8_t *buffer, int start) {<br \/>\n\u00a0\u00a0\u00a0 return (buffer[start]&lt;&lt;8) + buffer[start+1];<br \/>\n}<\/p>\n<p>\/\/ Return the length field from a frame<br \/>\nint zb_get_buffer_length(const uint8_t *buffer) {<br \/>\n\u00a0\u00a0\u00a0 return zb_get16(buffer, ZB_OFFS_LEN_MSB);<br \/>\n}<\/p>\n<p>\/\/ Return the AT command field from an AT command response frame<br \/>\nuint32_t zb_get_at(const uint8_t *buffer) {<br \/>\n\u00a0\u00a0\u00a0 return zb_get16(buffer, ZB_OFFS_AT_RESP_AT0);<br \/>\n}<br \/>\n[\/cpp]\n<\/div>\n<h5>zigbee.h<\/h5>\n<p>Header file with declarations and constants for ZigBee communication.<\/p>\n<span class=\"collapseomatic \" id=\"id69fefc0b0285c\"  tabindex=\"0\" title=\"Expand zigbee.h\"    >Expand zigbee.h<\/span><div id=\"target-id69fefc0b0285c\" class=\"collapseomatic_content \">\n[cpp]\n\/*<br \/>\nzigbee.h<\/p>\n<p>Rev 0.1, 2015-07-31<br \/>\nPer Magnusson, Axotron, axotron.se\/blog<\/p>\n<p>Header file with declarations and constants for ZigBee communication.<br \/>\n*\/<\/p>\n<p>#define ZB_BUFSIZE 150<br \/>\n\/\/ ZigBee defines<br \/>\n\/\/ General frame offsets<br \/>\n#define ZB_START_DELIM 0x7e<br \/>\n#define ZB_OFFS_LEN_MSB 1<br \/>\n#define ZB_OFFS_LEN_LSB 2<br \/>\n#define ZB_OFFS_FRAME_TYPE 3<br \/>\n#define ZB_OFFS_FRAME_ID 4<br \/>\n\/\/ AT command frame offsets<br \/>\n#define ZB_OFFS_AT_CMD_AT0 5<br \/>\n#define ZB_OFFS_AT_CMD_AT1 6<br \/>\n#define ZB_OFFS_AT_CMD_DATA0 7<br \/>\n\/\/ AT response frame offsets<br \/>\n#define ZB_OFFS_AT_RESP_AT0 5<br \/>\n#define ZB_OFFS_AT_RESP_AT1 6<br \/>\n#define ZB_OFFS_AT_RESP_STATUS 7<br \/>\n#define ZB_OFFS_AT_RESP_DATA0 8<\/p>\n<p>\/\/ TX request frame offsets<br \/>\n#define ZB_OFFS_TX_DEST64_0 5<br \/>\n#define ZB_OFFS_TX_DEST64_1 6<br \/>\n#define ZB_OFFS_TX_DEST64_2 7<br \/>\n#define ZB_OFFS_TX_DEST64_3 8<br \/>\n#define ZB_OFFS_TX_DEST64_4 9<br \/>\n#define ZB_OFFS_TX_DEST64_5 10<br \/>\n#define ZB_OFFS_TX_DEST64_6 11<br \/>\n#define ZB_OFFS_TX_DEST64_7 12<br \/>\n#define ZB_OFFS_TX_DEST16_0 13<br \/>\n#define ZB_OFFS_TX_DEST16_1 14<br \/>\n#define ZB_OFFS_TX_RADIUS 15<br \/>\n#define ZB_OFFS_TX_OPTIONS 16<br \/>\n#define ZB_OFFS_TX_DATA_0 17<\/p>\n<p>\/\/ RX packet frame offsets<br \/>\n#define ZB_OFFS_RX_SRC64_0 4<br \/>\n#define ZB_OFFS_RX_SRC64_1 5<br \/>\n#define ZB_OFFS_RX_SRC64_2 6<br \/>\n#define ZB_OFFS_RX_SRC64_3 7<br \/>\n#define ZB_OFFS_RX_SRC64_4 8<br \/>\n#define ZB_OFFS_RX_SRC64_5 9<br \/>\n#define ZB_OFFS_RX_SRC64_6 10<br \/>\n#define ZB_OFFS_RX_SRC64_7 11<br \/>\n#define ZB_OFFS_RX_SRC16_0 12<br \/>\n#define ZB_OFFS_RX_SRC16_1 13<br \/>\n#define ZB_OFFS_RX_OPTIONS 14<br \/>\n#define ZB_OFFS_RX_DATA_0 15<\/p>\n<p>\/\/ Frame types<br \/>\n#define ZB_FRAME_TYPE_AT 0x08<br \/>\n#define ZB_FRAME_TYPE_TX_REQ 0x10<br \/>\n#define ZB_FRAME_TYPE_AT_RESPONSE 0x88<br \/>\n#define ZB_FRAME_TYPE_TX_RESPONSE 0x8b<br \/>\n#define ZB_FRAME_TYPE_RX_RECEIVED 0x90<\/p>\n<p>\/\/ AT commands as numbers<br \/>\n#define ZB_ATDB ((((uint32_t)&#8217;D&#8217;)&amp;lt;&amp;lt;8) + &#8216;B&#8217;)<\/p>\n<p>\/\/ Commands to sensor nodes<br \/>\n#define ZB_CMD_ME ((((uint32_t)&#8217;M&#8217;)&amp;lt;&amp;lt;8) + &#8216;E&#8217;)<br \/>\n#define ZB_CMD_ID ((((uint32_t)&#8217;I&#8217;)&amp;lt;&amp;lt;8) + &#8216;D&#8217;)<\/p>\n<p>void zb_send_simple_at(const char *at, char frame_id=0);<br \/>\nvoid zb_send_at_byte_param(const char *at, uint8_t param);<br \/>\nvoid zb_send_tx_req(uint8_t id, const uint8_t *dest64, const uint8_t *dest16, const uint8_t *data, int data_len);<br \/>\nuint32_t zb_get16(const uint8_t *buffer, int start);<br \/>\nint zb_get_buffer_length(const uint8_t *buffer);<br \/>\nuint32_t zb_get_at(const uint8_t *buffer);<br \/>\n[\/cpp]\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>I have previously written about how to interface an XBee ZigBee module to a FRDM-KL25Z board and also how to talk to an XBee ZigBee module from a Python program on a PC. In this article I build on these foundations and show how to make a somewhat more complex system where a Python program &hellip; <a href=\"https:\/\/axotron.se\/blog\/frdm-based-zigbee-humidity-sensor-node-prototype\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">FRDM-based ZigBee Humidity Sensor Node Prototype<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-625","post","type-post","status-publish","format-standard","hentry","category-electronics"],"_links":{"self":[{"href":"https:\/\/axotron.se\/blog\/wp-json\/wp\/v2\/posts\/625","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/axotron.se\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/axotron.se\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/axotron.se\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/axotron.se\/blog\/wp-json\/wp\/v2\/comments?post=625"}],"version-history":[{"count":13,"href":"https:\/\/axotron.se\/blog\/wp-json\/wp\/v2\/posts\/625\/revisions"}],"predecessor-version":[{"id":649,"href":"https:\/\/axotron.se\/blog\/wp-json\/wp\/v2\/posts\/625\/revisions\/649"}],"wp:attachment":[{"href":"https:\/\/axotron.se\/blog\/wp-json\/wp\/v2\/media?parent=625"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/axotron.se\/blog\/wp-json\/wp\/v2\/categories?post=625"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/axotron.se\/blog\/wp-json\/wp\/v2\/tags?post=625"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}