{"id":4804,"date":"2025-02-28T08:00:46","date_gmt":"2025-02-28T16:00:46","guid":{"rendered":"https:\/\/www.cloudacm.com\/?p=4804"},"modified":"2025-03-27T17:51:21","modified_gmt":"2025-03-28T00:51:21","slug":"esp8266-and-esp32-nrf24l01-native-support","status":"publish","type":"post","link":"https:\/\/www.cloudacm.com\/?p=4804","title":{"rendered":"Live RF Plotting using the ESP8266 or ESP32 with NRF24L01 Native Support"},"content":{"rendered":"<p>This post will cover directly interfacing the NRF24L01 module with either the ESP32 or ESP8266 Wemos modules.\u00a0 In earlier posts I had used the Arduino Mini module as a shim.\u00a0 This allows the setup to be compact with fewer points of failure.\u00a0 This post will also introduce a plotting alternative that wasn&#8217;t covered in earlier posts.<\/p>\n<p><iframe loading=\"lazy\" title=\"Wemos NRF24L01 MQTT Processing Demo\" src=\"https:\/\/player.vimeo.com\/video\/1060493036?dnt=1&amp;app_id=122963\" width=\"640\" height=\"299\" frameborder=\"0\" allow=\"autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media\"><\/iframe><\/p>\n<p>Here is the wiring diagram for the ESP8266 Wemos Module and its SPI connection to the NRF24L01.<\/p>\n<p><a href=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/Wemos_nRF24L01_Native_bb.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-4805\" src=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/Wemos_nRF24L01_Native_bb-300x200.png\" alt=\"\" width=\"500\" height=\"333\" srcset=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/Wemos_nRF24L01_Native_bb-300x200.png 300w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/Wemos_nRF24L01_Native_bb-768x512.png 768w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/Wemos_nRF24L01_Native_bb-405x270.png 405w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/Wemos_nRF24L01_Native_bb.png 900w\" sizes=\"auto, (max-width: 500px) 100vw, 500px\" \/><\/a><\/p>\n<p>Here is the Arduino code used to send readings from the NRF24L01 module to a MQTT broker.<\/p>\n<pre>#include \"SPI.h\"\r\n#include \"NRFLite.h\"\r\n#include &lt;ESP8266WiFi.h&gt; \/\/ For ESP8266\r\n\/\/#include &lt;WiFi.h&gt; \/\/ For ESP32, uncomment this line and comment out the above line\r\n#include &lt;PubSubClient.h&gt;\r\n\r\nconst static uint8_t RADIO_ID = 0;\r\nconst static uint8_t PIN_RADIO_CE = 2;\r\nconst static uint8_t PIN_RADIO_CSN = 15;\r\nconst static uint8_t ChannelSamples = 16;\r\nconst static uint8_t ChannelSweep = 2;\r\nconst int ledPin = LED_BUILTIN;\r\n\r\nconst char* ssid = &lt;Wireless SSID&gt;; \/\/ WiFi network name\r\nconst char* password = &lt;Wireless Password&gt;; \/\/ WiFi network password\r\nconst char* mqttServer = \"&lt;MQTT Broker&gt;\"; \/\/ MQTT Broker address\r\nconst int mqttPort = 1883; \/\/ MQTT port (default 1883 for non-TLS connections)\r\n\r\nWiFiClient espClient;\r\nPubSubClient client(espClient);\r\n\r\nNRFLite _radio;\r\nbool shouldScan = false; \/\/ Flag to control scanning\r\nint scanCount = 0; \/\/ Count scans\r\n\r\n\/\/ MQTT Functions\r\nvoid callback(char* topic, byte* message, unsigned int length) {\r\nString messageTemp;\r\nfor (int i = 0; i &lt; length; i++) {\r\nmessageTemp += (char)message[i];\r\n}\r\nif (String(topic) == \"Wemos_NRF24L01\/read\") {\r\nif(messageTemp == \"scan\"){\r\nshouldScan = true; \/\/ Set flag to start scanning\r\nscanCount = 0; \/\/ Reset scan count\r\n}\r\n}\r\n}\r\n\r\nvoid setup() {\r\npinMode(ledPin, OUTPUT);\r\ndigitalWrite(ledPin, LOW);\r\ndelay(10);\r\nWiFi.begin(ssid, password); \/\/ Connect to WiFi\r\n\r\nwhile (WiFi.status() != WL_CONNECTED) {\r\ndelay(100);\r\n}\r\n\r\nclient.setServer(mqttServer, mqttPort); \/\/ Connect to MQTT Broker\r\nclient.setCallback(callback); \/\/ Set the MQTT callback function\r\nwhile (!client.connected()) {\r\nif (client.connect(\"Wemos_NRF24L01\")) {\r\nclient.subscribe(\"Wemos_NRF24L01\/#\");\r\n} else {\r\ndelay(1000);\r\n}\r\n}\r\n\r\nif (!_radio.init(RADIO_ID, PIN_RADIO_CE, PIN_RADIO_CSN)) {\r\nwhile (1);\r\n}\r\n}\r\n\r\nvoid loop() {\r\nclient.loop(); \/\/ Maintain MQTT connection\r\n\r\nif (!client.connected()) {\r\nwhile (!client.connected()) {\r\nif (client.connect(\"Wemos_NRF24L01\")) {\r\n} else {\r\ndelay(100);\r\n}\r\n}\r\n}\r\n\r\nif (shouldScan &amp;&amp; scanCount &lt; 32) {\r\nString resultString = \"\";\r\nuint16_t RFLevel = 0;\r\n\r\nfor (uint8_t channel = 0; channel &lt;= NRFLite::MAX_NRF_CHANNEL; channel += ChannelSweep) {\r\nuint8_t signalStrength = _radio.scanChannel(channel, ChannelSamples);\r\nresultString += String(signalStrength);\r\nif (channel &lt; NRFLite::MAX_NRF_CHANNEL) {\r\nresultString += \",\";\r\n}\r\n#if defined(ESP8266) || defined(ESP32)\r\nyield();\r\n#endif\r\n}\r\n\r\nclient.publish(\"Wemos_NRF24L01\/data\", resultString.c_str()); \/\/ Publish data to MQTT topic\r\nscanCount++; \/\/ Increment scan count\r\n}\r\n\r\nif (scanCount &gt;= 32) {\r\nshouldScan = false; \/\/ Reset flag after 32 scans\r\ndigitalWrite(ledPin, HIGH);\r\n}\r\n}<\/pre>\n<p>Here is the wiring diagram for the ESP32 Cam Module and its SPI connection to the NRF24L01 module.\u00a0 The SPI data pin connections cannot be shared, so the onboard camera module will not be available.<\/p>\n<p><a href=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/ESP32-Cam_nRF24L01_Native_bb.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-4809\" src=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/ESP32-Cam_nRF24L01_Native_bb-300x167.png\" alt=\"\" width=\"500\" height=\"278\" srcset=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/ESP32-Cam_nRF24L01_Native_bb-300x167.png 300w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/ESP32-Cam_nRF24L01_Native_bb-1024x569.png 1024w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/ESP32-Cam_nRF24L01_Native_bb-768x427.png 768w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/ESP32-Cam_nRF24L01_Native_bb-486x270.png 486w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/ESP32-Cam_nRF24L01_Native_bb-1038x576.png 1038w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/ESP32-Cam_nRF24L01_Native_bb.png 1066w\" sizes=\"auto, (max-width: 500px) 100vw, 500px\" \/><\/a><\/p>\n<p>Here is the Arduino code used to send readings from the NRF24L01 module to a MQTT broker.<\/p>\n<pre>#include \"SPI.h\"\r\n#include \"NRFLite.h\"\r\n\/\/#include &lt;ESP8266WiFi.h&gt; \/\/ For ESP8266\r\n#include &lt;WiFi.h&gt; \/\/ For ESP32, uncomment this line and comment out the above line\r\n#include &lt;PubSubClient.h&gt;\r\n\r\nconst static uint8_t RADIO_ID = 0;\r\nconst static uint8_t PIN_RADIO_CE = 2;\r\nconst static uint8_t PIN_RADIO_CSN = 4;\r\nconst static uint8_t ChannelSamples = 16;\r\nconst static uint8_t ChannelSweep = 2;\r\n\r\nconst char* ssid = &lt;Wireless SSID&gt;; \/\/ WiFi network name \r\nconst char* password = &lt;Wireless Password&gt;; \/\/ WiFi network password \r\nconst char* mqttServer = \"&lt;MQTT Broker&gt;\"; \/\/ MQTT Broker address\r\nconst int mqttPort = 1883; \/\/ MQTT port (default 1883 for non-TLS connections)\r\n\r\nWiFiClient espClient;\r\nPubSubClient client(espClient);\r\n\r\nNRFLite _radio;\r\nbool shouldScan = false; \/\/ Flag to control scanning\r\nint scanCount = 0; \/\/ Count scans\r\n\r\n\/\/ MQTT Functions\r\nvoid callback(char* topic, byte* message, unsigned int length) { \r\nString messageTemp; \r\nfor (int i = 0; i &lt; length; i++) {\r\nmessageTemp += (char)message[i];\r\n} \r\nif (String(topic) == \"ESP32Cam_NRF24L01\/read\") {\r\nif(messageTemp == \"scan\"){\r\nshouldScan = true; \/\/ Set flag to start scanning\r\nscanCount = 0; \/\/ Reset scan count\r\n}\r\n} \r\n}\r\n\r\n\r\nvoid setup() {\r\nWiFi.begin(ssid, password); \/\/ Connect to WiFi\r\n\r\nwhile (WiFi.status() != WL_CONNECTED) {\r\ndelay(100);\r\n}\r\n\r\nclient.setServer(mqttServer, mqttPort); \/\/ Connect to MQTT Broker\r\nclient.setCallback(callback); \/\/ Set the MQTT callback function\r\nwhile (!client.connected()) {\r\nif (client.connect(\"ESP32Cam_NRF24L01\")) {\r\nclient.subscribe(\"ESP32Cam_NRF24L01\/#\");\r\n} else {\r\ndelay(1000);\r\n}\r\n}\r\n\r\n\/\/ Initialize SPI on pins chosen so as not to conflict with camera\/SD:\r\n\/\/ Parameters: SPI.begin(SCK, MISO, MOSI, dummy SS)\r\nSPI.begin(14, 12, 13, 2);\r\nif (!_radio.init(RADIO_ID, PIN_RADIO_CE, PIN_RADIO_CSN)) {\r\nwhile (1);\r\n}\r\n}\r\n\r\nvoid loop() {\r\nclient.loop(); \/\/ Maintain MQTT connection\r\n\r\nif (!client.connected()) {\r\nwhile (!client.connected()) {\r\nif (client.connect(\"ESP32Cam_NRF24L01\")) {\r\n} else {\r\ndelay(100);\r\n}\r\n}\r\n}\r\n\r\nif (shouldScan &amp;&amp; scanCount &lt; 32) {\r\nString resultString = \"\";\r\nuint16_t RFLevel = 0;\r\n\r\nfor (uint8_t channel = 0; channel &lt;= NRFLite::MAX_NRF_CHANNEL; channel += ChannelSweep) {\r\nuint8_t signalStrength = _radio.scanChannel(channel, ChannelSamples);\r\nresultString += String(signalStrength);\r\nif (channel &lt; NRFLite::MAX_NRF_CHANNEL) {\r\nresultString += \",\";\r\n}\r\n#if defined(ESP8266) || defined(ESP32)\r\nyield();\r\n#endif\r\n}\r\n\r\nclient.publish(\"ESP32Cam_NRF24L01\/data\", resultString.c_str()); \/\/ Publish data to MQTT topic\r\nscanCount++; \/\/ Increment scan count\r\n}\r\n\r\nif (scanCount &gt;= 32) {\r\nshouldScan = false; \/\/ Reset flag after 32 scans\r\n}\r\n}<\/pre>\n<p>Here is the Processing IDE code used to display readings from the MQTT broker.\u00a0 It can be set to subscribe to either the Wemos or ESP32 Cam module feeds, change the comments as needed.<\/p>\n<pre>\/\/ Import the MQTT client library\r\nimport org.eclipse.paho.client.mqttv3.MqttClient;\r\nimport org.eclipse.paho.client.mqttv3.MqttMessage;\r\nimport org.eclipse.paho.client.mqttv3.MqttConnectOptions;\r\nimport org.eclipse.paho.client.mqttv3.MqttCallback;\r\nimport org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;\r\n\r\n\/\/ Global variables for drawing\r\nint cols = 63; \/\/ Number of columns for both sketches\r\nint rows = 32; \/\/ Number of rows for the first sketch\r\nfloat[][] z = new float[rows][cols]; \/\/ Array for the first sketch\r\nint[] values = new int[cols]; \/\/ Array to store the data for the second sketch\r\nint[] maxValues = new int[cols]; \/\/ Array to store maximum values for each column in the second sketch\r\nint currentNumber = 0;\r\n\r\n\/\/ Global MQTT client and data buffer\r\nMqttClient client;\r\nString mqttData = null;\r\n\r\nvoid setup() {\r\nsize(1320, 580); \/\/ Combined sketch size\r\nbackground(0); \/\/ Set background to black\r\n\r\n\/\/ Set up the MQTT client\r\ntry {\r\n\/\/ Connect to the broker using the specified URI and unique client ID\r\nclient = new MqttClient(\"tcp:\/\/&lt;MQTT Broker&gt;:1883\", \"MqttClient2\");\r\nMqttConnectOptions options = new MqttConnectOptions();\r\noptions.setCleanSession(true);\r\n\r\n\/\/ Set up the callback to receive messages\r\nclient.setCallback(new MqttCallback() {\r\npublic void connectionLost(Throwable cause) {\r\nprintln(\"MQTT connection lost: \" + cause.getMessage());\r\n}\r\n\r\npublic void messageArrived(String topic, MqttMessage message) throws Exception {\r\n\/\/ When a message is received, store it for processing in draw()\r\nmqttData = message.toString();\r\n}\r\n\r\npublic void deliveryComplete(IMqttDeliveryToken token) {\r\n\/\/ Not used (we are only subscribing)\r\n}\r\n});\r\n\r\n\/\/ Connect to the broker and subscribe to the topic\r\nclient.connect(options);\r\n\/\/ client.subscribe(\"Wemos_NRF24L01\/data\"); \/\/ Uncomment to subscribe to the Wemos Module\r\nclient.subscribe(\"ESP32Cam_NRF24L01\/data\"); \/\/ Replace with your actual topic if needed\r\nprintln(\"Connected to MQTT broker and subscribed to topic ESP32Cam_NRF24L01\/data\");\r\n} \r\ncatch(Exception e) {\r\ne.printStackTrace();\r\n}\r\n\r\n\/\/ Initialize the waterfall array and maximum values\r\ninitializeArray();\r\nfor (int i = 0; i &lt; maxValues.length; i++) {\r\nmaxValues[i] = 0;\r\n}\r\n}\r\n\r\nvoid draw() {\r\nbackground(0); \/\/ Clear the background\r\n\r\n\/\/ Draw the two sketches side by side\r\npushMatrix();\r\ntranslate(0, 0);\r\ndrawBordersAndText1();\r\nColorScaledraw1();\r\npopMatrix();\r\n\r\npushMatrix();\r\ntranslate(660, 0);\r\ndrawBordersAndText2();\r\nColorScaledraw2();\r\npopMatrix();\r\n\r\ndrawRectangles();\r\ndrawFooter();\r\n\r\n\/\/ If MQTT data has been received, process it\r\nif (mqttData != null) {\r\nString incomingData = mqttData.trim();\r\nprocessData1(incomingData); \/\/ Update the waterfall display\r\nprocessData2(incomingData); \/\/ Update the barchart display\r\nmqttData = null; \/\/ Clear the data after processing\r\n}\r\n}\r\n\r\nvoid ColorScaledraw1() {\r\nif (currentNumber == 1) {\r\nViridisColorWaterfall();\r\n} else if (currentNumber == 2) {\r\nThermalColorWaterfall();\r\n} else if (currentNumber == 3) {\r\nPlasmaColorWaterfall();\r\n} else if (currentNumber == 4) {\r\nInfernoColorWaterfall();\r\n} else {\r\nGrayScaleWaterfall();\r\n} \r\n}\r\n\r\nvoid ColorScaledraw2() { \r\nif (currentNumber == 1) {\r\nViridisColorBarchart();\r\ndrawViridisMaxLine();\r\n} else if (currentNumber == 2) {\r\nThermalColorBarchart();\r\ndrawThermalMaxLine();\r\n} else if (currentNumber == 3) {\r\nPlasmaColorBarchart();\r\ndrawPlasmaMaxLine();\r\n} else if (currentNumber == 4) {\r\nInfernoColorBarchart();\r\ndrawInfernoMaxLine();\r\n} else {\r\nGrayScaleBarchart();\r\ndrawGrayScaleMaxLine();\r\n} \r\n}\r\n\r\nvoid keyPressed() {\r\nif (key == '1') {\r\ncurrentNumber = 1;\r\n} else if (key == '2') {\r\ncurrentNumber = 2;\r\n} else if (key == '3') {\r\ncurrentNumber = 3;\r\n} else if (key == '4') {\r\ncurrentNumber = 4;\r\n} else if (key == 'x') {\r\nfor (int i = 0; i &lt; maxValues.length; i++) {\r\nmaxValues[i] = 0; \/\/ Reset maximum values for the barchart\r\n}\r\n} else {\r\ncurrentNumber = 0;\r\n} \r\n}\r\n\r\nvoid initializeArray() {\r\nfor (int y = 0; y &lt; rows; y++) {\r\nfor (int x = 0; x &lt; cols; x++) {\r\nz[y][x] = 0; \/\/ Initialize the waterfall array with zeros\r\n}\r\n}\r\n}\r\n\r\nvoid drawFooter() {\r\nfill(255);\r\ntextSize(12);\r\nString middleText = \"Enter number for Color Scale: (1) Viridis, (2) Thermo, (3) Plasma, (4) Inferno\";\r\nfloat middleTextWidth = textWidth(middleText);\r\nfloat middleTextX = (width - middleTextWidth) \/ 2;\r\ntext(middleText, middleTextX, height - 60);\r\n\r\nString bottomText = \"Press x to reset max reading line, Gray scale (default) by pressing any other key\";\r\nfloat bottomTextWidth = textWidth(bottomText);\r\nfloat bottomTextX = (width - bottomTextWidth) \/ 2;\r\ntext(bottomText, bottomTextX, height - 40);\r\n\r\ntextSize(8);\r\nString signatureText = \"Developed by Patrick Gilfeather - CloudACM.com - Jan 2025\";\r\nfloat signatureTextWidth = textWidth(signatureText);\r\nfloat signatureTextX = (width - signatureTextWidth) \/ 2;\r\ntext(signatureText, signatureTextX, height - 25);\r\n}\r\n\r\nvoid drawRectangles() {\r\nfill(0); \r\nnoStroke(); \r\nrect(658, 100, 12, 390);\r\nrect(660, 500, 655, 70);\r\n}\r\n\r\nvoid drawGrid() {\r\nstroke(128, 128, 128, 32);\r\nfor (int i = 0; i &lt;= cols; i++) {\r\nfloat x = map(i, 0, 10, 10, 650);\r\nline(x, 100, x, 480);\r\n}\r\nfor (int i = 0; i &lt;= rows; i++) {\r\nfloat y = map(i, 0, 10, 100, 480);\r\nline(10, y, 650, y);\r\n}\r\n}\r\n\r\nvoid shiftDataDown() {\r\nfor (int y = rows - 1; y &gt; 0; y--) {\r\narrayCopy(z[y-1], z[y]);\r\n}\r\n}\r\n\r\nvoid drawBordersAndText1() {\r\nfill(255);\r\ntextSize(20);\r\nString topText = \"ESP32Cam nRF24L01 Waterfall\";\r\nfloat topTextWidth = textWidth(topText);\r\ntext(topText, (660 - topTextWidth) \/ 2, 50);\r\ntextSize(12);\r\n}\r\n\r\nvoid drawBordersAndText2() {\r\nfill(255);\r\ntextSize(20);\r\nString topText = \"ESP32Cam nRF24L01 Barchart\";\r\nfloat topTextWidth = textWidth(topText);\r\ntext(topText, (660 - topTextWidth) \/ 2, 50);\r\n}\r\n\r\nvoid processData1(String data) {\r\n\/\/ Expect a comma-separated string of 63 numbers ending with a comma\r\nif (data.matches(\"^(\\\\d+,){63}$\")) {\r\nString[] values = split(data, ',');\r\nshiftDataDown();\r\nfor (int i = 0; i &lt; cols; i++) {\r\ntry {\r\nz[0][i] = Float.parseFloat(values[i]);\r\n} catch (NumberFormatException e) {\r\nprintln(\"Error converting value: \" + values[i]);\r\n}\r\n}\r\n}\r\n}\r\n\r\nvoid processData2(String data) {\r\n\/\/ Process the comma-separated string for the barchart\r\nString[] stringValues = data.split(\",\");\r\nfor (int i = 0; i &lt; stringValues.length &amp;&amp; i &lt; values.length; i++) {\r\nvalues[i] = int(stringValues[i].trim());\r\nmaxValues[i] = max(maxValues[i], values[i]);\r\n}\r\n}\r\n\r\nvoid GrayScaleWaterfall() {\r\nfloat xSpacing = (660 - 20) \/ float(cols);\r\nfloat ySpacing = (580 - 200) \/ float(rows);\r\nfor (int y = 0; y &lt; rows; y++) {\r\nfor (int x = 0; x &lt; cols; x++) {\r\nfill(map(z[y][x], 0, 16, 0, 255));\r\nrect(10 + x * xSpacing, 100 + y * ySpacing, xSpacing, ySpacing);\r\n}\r\n}\r\n}\r\n\r\nvoid ViridisColorWaterfall() {\r\nfloat xSpacing = (660 - 20) \/ float(cols);\r\nfloat ySpacing = (580 - 200) \/ float(rows);\r\nfor (int y = 0; y &lt; rows; y++) {\r\nfor (int x = 0; x &lt; cols; x++) {\r\nfloat value = z[y][x];\r\nfloat c = map(value, 0, 16, 0, 255);\r\nif (c &lt;= 64) {\r\nfloat r = map(c, 0, 64, 32, 128); \r\nfloat g = 0; \r\nfloat b = map(c, 0, 64, 32, 128); \r\nfill(r, g, b);\r\n} else if (c &lt;= 128) {\r\nfloat r = map(c, 64, 128, 96, 0); \r\nfloat g = map(c, 64, 128, 0, 255); \r\nfloat b = map(c, 64, 128, 96, 0); \r\nfill(r, g, b);\r\n} else {\r\nfloat r = map(c, 128, 255, 0, 255); \r\nfloat g = 255; \r\nfloat b = 0; \r\nfill(r, g, b);\r\n}\r\nrect(10 + x * xSpacing, 100 + y * ySpacing, xSpacing, ySpacing);\r\n}\r\n}\r\n}\r\n\r\nvoid ThermalColorWaterfall() {\r\nfloat xSpacing = (660 - 20) \/ float(cols);\r\nfloat ySpacing = (580 - 200) \/ float(rows);\r\nfor (int y = 0; y &lt; rows; y++) {\r\nfor (int x = 0; x &lt; cols; x++) {\r\nfloat value = z[y][x];\r\nfloat c = map(value, 0, 16, 0, 255);\r\nif (c &lt;= 64) {\r\nfloat r = 0;\r\nfloat g = 0;\r\nfloat b = map(c, 0, 64, 32, 255);\r\nfill(r, g, b);\r\n} else if (c &lt;= 128) {\r\nfloat r = map(c, 64, 128, 0, 255);\r\nfloat g = 0;\r\nfloat b = map(c, 64, 128, 255, 0);\r\nfill(r, g, b);\r\n} else {\r\nfloat r = 255;\r\nfloat g = map(c, 128, 255, 0, 255);\r\nfloat b = 0;\r\nfill(r, g, b);\r\n}\r\nrect(10 + x * xSpacing, 100 + y * ySpacing, xSpacing, ySpacing);\r\n}\r\n}\r\n}\r\n\r\nvoid PlasmaColorWaterfall() {\r\nfloat xSpacing = (660 - 20) \/ float(cols);\r\nfloat ySpacing = (580 - 200) \/ float(rows);\r\nfor (int y = 0; y &lt; rows; y++) {\r\nfor (int x = 0; x &lt; cols; x++) {\r\nfloat value = z[y][x];\r\nfloat c = map(value, 0, 16, 0, 255);\r\nif (c &lt;= 64) {\r\nfloat r = map(c, 0, 64, 16, 156);\r\nfloat g = map(c, 0, 64, 0, 23);\r\nfloat b = map(c, 0, 64, 128, 158);\r\nfill(r, g, b);\r\n} else if (c &lt;= 128) {\r\nfloat r = map(c, 64, 128, 156, 241);\r\nfloat g = map(c, 64, 128, 23, 131);\r\nfloat b = map(c, 64, 128, 158, 76);\r\nfill(r, g, b);\r\n} else {\r\nfloat r = 241;\r\nfloat g = map(c, 128, 255, 131, 255);\r\nfloat b = map(c, 128, 255, 76, 0);\r\nfill(r, g, b);\r\n}\r\nrect(10 + x * xSpacing, 100 + y * ySpacing, xSpacing, ySpacing);\r\n}\r\n}\r\n}\r\n\r\nvoid InfernoColorWaterfall() {\r\nfloat xSpacing = (660 - 20) \/ float(cols);\r\nfloat ySpacing = (580 - 200) \/ float(rows);\r\nfor (int y = 0; y &lt; rows; y++) {\r\nfor (int x = 0; x &lt; cols; x++) {\r\nfloat value = z[y][x];\r\nfloat c = map(value, 0, 16, 0, 255);\r\nif (c &lt;= 64) {\r\nfloat r = map(c, 0, 64, 16, 255);\r\nfloat g = 0;\r\nfloat b = 0;\r\nfill(r, g, b);\r\n} else if (c &lt;= 128) {\r\nfloat r = 255;\r\nfloat g = map(c, 64, 128, 0, 255);\r\nfloat b = 0;\r\nfill(r, g, b);\r\n} else {\r\nfloat r = 255;\r\nfloat g = 255;\r\nfloat b = map(c, 128, 255, 0, 255);\r\nfill(r, g, b);\r\n}\r\nrect(10 + x * xSpacing, 100 + y * ySpacing, xSpacing, ySpacing);\r\n}\r\n}\r\n}\r\n\r\nvoid GrayScaleBarchart() {\r\nnoStroke();\r\nfloat barWidth = 640 \/ float(values.length);\r\nfor (int i = 0; i &lt; values.length; i++) {\r\nint value = values[i];\r\nfloat barHeight = map(value, 0, 32, 0, 380);\r\nfill(map(value, 0, 32, 0, 255));\r\nrect(10 + i * barWidth, 580 - 100 - barHeight, barWidth, barHeight);\r\n}\r\n}\r\n\r\nvoid drawGrayScaleMaxLine() {\r\nstroke(128, 128, 128, 128);\r\nfill(128, 128, 128, 32);\r\nstrokeWeight(2);\r\nfloat barWidth = 640 \/ float(maxValues.length);\r\nbeginShape();\r\nfloat lastX = 0;\r\nfloat y = 580 - 100 - map(0, 0, 32, 0, 380);\r\nvertex(lastX, y);\r\nfor (int i = 0; i &lt; maxValues.length; i++) {\r\nfloat x = i * barWidth + barWidth + 10;\r\ny = 580 - 100 - map(maxValues[i], 0, 32, 0, 380);\r\nif (i &gt;= 0) {\r\nvertex(lastX, y);\r\n}\r\nvertex(x, y);\r\nlastX = x;\r\n}\r\nendShape();\r\ndrawGrid();\r\n}\r\n\r\nvoid ViridisColorBarchart() {\r\nnoStroke();\r\nfloat barWidth = 640 \/ float(values.length);\r\nfor (int i = 0; i &lt; values.length; i++) {\r\nint value = values[i];\r\nfloat barHeight = map(value, 0, 32, 0, (height - 200));\r\nif (value &lt;= 3) {\r\nfloat redValue = map(value, 0, 3, 0, 128);\r\nfloat greenValue = 0;\r\nfloat blueValue = map(value, 0, 3, 0, 128);\r\nfill(redValue, greenValue, blueValue);\r\n} else if (value &gt; 3 &amp;&amp; value &lt;= 6) {\r\nfloat redValue = map(value, 3, 6, 128, 0);\r\nfloat greenValue = map(value, 3, 6, 0, 128);\r\nfloat blueValue = map(value, 3, 6, 128, 0);\r\nfill(redValue, greenValue, blueValue);\r\n} else {\r\nfloat redValue = map(value, 6, 16, 0, 255);\r\nfloat greenValue = map(value, 6, 16, 128, 255);\r\nfloat blueValue = 0;\r\nfill(redValue, greenValue, blueValue);\r\n}\r\nrect(i * barWidth + 10, height - barHeight - 100, barWidth, barHeight);\r\n}\r\n}\r\n\r\nvoid drawViridisMaxLine() {\r\nstroke(0, 255, 0, 128);\r\nfill(0, 128, 0, 32);\r\nstrokeWeight(2);\r\nfloat barWidth = 640 \/ float(maxValues.length);\r\nbeginShape();\r\nfloat lastX = 0;\r\nfloat y = 580 - 100 - map(0, 0, 32, 0, 380);\r\nvertex(lastX, y);\r\nfor (int i = 0; i &lt; maxValues.length; i++) {\r\nfloat x = i * barWidth + barWidth + 10;\r\ny = 580 - 100 - map(maxValues[i], 0, 32, 0, 380);\r\nif (i &gt;= 0) {\r\nvertex(lastX, y);\r\n}\r\nvertex(x, y);\r\nlastX = x;\r\n}\r\nendShape();\r\ndrawGrid();\r\n}\r\n\r\nvoid ThermalColorBarchart() {\r\nnoStroke();\r\nfloat barWidth = 640 \/ float(values.length);\r\nfor (int i = 0; i &lt; values.length; i++) {\r\nint value = values[i];\r\nfloat barHeight = map(value, 0, 32, 0, (height - 200));\r\nif (value &lt;= 4) {\r\nfloat redValue = 0;\r\nfloat greenValue = 0;\r\nfloat blueValue = map(value, 0, 4, 0, 255);\r\nfill(redValue, greenValue, blueValue);\r\n} else if (value &gt; 4 &amp;&amp; value &lt;= 8) {\r\nfloat redValue = map(value, 4, 8, 0, 255);\r\nfloat greenValue = 0;\r\nfloat blueValue = map(value, 4, 8, 255, 0);\r\nfill(redValue, greenValue, blueValue);\r\n} else {\r\nfloat redValue = 255;\r\nfloat greenValue = map(value, 8, 16, 0, 255);\r\nfloat blueValue = 0;\r\nfill(redValue, greenValue, blueValue);\r\n}\r\nrect(i * barWidth + 10, height - barHeight - 100, barWidth, barHeight);\r\n}\r\n}\r\n\r\nvoid drawThermalMaxLine() {\r\nstroke(0, 96, 255, 255);\r\nfill(0, 0, 196, 32);\r\nstrokeWeight(2);\r\nfloat barWidth = 640 \/ float(maxValues.length);\r\nbeginShape();\r\nfloat lastX = 0;\r\nfloat y = 580 - 100 - map(0, 0, 32, 0, 380);\r\nvertex(lastX, y);\r\nfor (int i = 0; i &lt; maxValues.length; i++) {\r\nfloat x = i * barWidth + barWidth + 10;\r\ny = height - map(maxValues[i], 0, 32, 0, (height - 200)) - 100;\r\nif (i &gt;= 0) {\r\nvertex(lastX, y);\r\n}\r\nvertex(x, y);\r\nlastX = x;\r\n}\r\nendShape();\r\ndrawGrid();\r\n}\r\n\r\nvoid PlasmaColorBarchart() {\r\nnoStroke();\r\nfloat barWidth = 640 \/ float(values.length);\r\nfor (int i = 0; i &lt; values.length; i++) {\r\nint value = values[i];\r\nfloat barHeight = map(value, 0, 32, 0, (height - 200));\r\nif (value &lt;= 4) {\r\nfloat redValue = map(value, 0, 4, 13, 156);\r\nfloat greenValue = map(value, 0, 4, 8, 23);\r\nfloat blueValue = map(value, 0, 4, 135, 158);\r\nfill(redValue, greenValue, blueValue);\r\n} else if (value &gt; 4 &amp;&amp; value &lt;= 8) {\r\nfloat redValue = map(value, 4, 8, 156, 241);\r\nfloat greenValue = map(value, 4, 8, 23, 131);\r\nfloat blueValue = map(value, 4, 8, 158, 76);\r\nfill(redValue, greenValue, blueValue);\r\n} else {\r\nfloat redValue = 241;\r\nfloat greenValue = map(value, 8, 16, 131, 255);\r\nfloat blueValue = map(value, 8, 16, 76, 0);\r\nfill(redValue, greenValue, blueValue);\r\n}\r\nrect(i * barWidth + 10, height - barHeight - 100, barWidth, barHeight);\r\n}\r\n}\r\n\r\nvoid drawPlasmaMaxLine() {\r\nstroke(196, 0, 196, 196);\r\nfill(64, 0, 255, 32);\r\nstrokeWeight(2);\r\nfloat barWidth = 640 \/ float(maxValues.length);\r\nbeginShape();\r\nfloat lastX = 0;\r\nfloat y = 580 - 100 - map(0, 0, 32, 0, 380);\r\nvertex(lastX, y);\r\nfor (int i = 0; i &lt; maxValues.length; i++) {\r\nfloat x = i * barWidth + barWidth + 10;\r\ny = height - map(maxValues[i], 0, 32, 0, (height - 200)) - 100;\r\nif (i &gt;= 0) {\r\nvertex(lastX, y);\r\n}\r\nvertex(x, y);\r\nlastX = x;\r\n}\r\nendShape();\r\ndrawGrid();\r\n}\r\n\r\nvoid InfernoColorBarchart() {\r\nnoStroke();\r\nfloat barWidth = 640 \/ float(values.length);\r\nfor (int i = 0; i &lt; values.length; i++) {\r\nint value = values[i];\r\nfloat barHeight = map(value, 0, 32, 0, (height - 200));\r\nif (value &lt;= 4) {\r\nfloat redValue = map(value, 0, 4, 0, 255);\r\nfloat greenValue = 0;\r\nfloat blueValue = 0;\r\nfill(redValue, greenValue, blueValue);\r\n} else if (value &gt; 4 &amp;&amp; value &lt;= 8) {\r\nfloat redValue = 255;\r\nfloat greenValue = map(value, 4, 8, 0, 255);\r\nfloat blueValue = 0;\r\nfill(redValue, greenValue, blueValue);\r\n} else {\r\nfloat redValue = 255;\r\nfloat greenValue = 255;\r\nfloat blueValue = map(value, 8, 16, 0, 255);\r\nfill(redValue, greenValue, blueValue);\r\n}\r\nrect(i * barWidth + 10, height - barHeight - 100, barWidth, barHeight);\r\n}\r\n}\r\n\r\nvoid drawInfernoMaxLine() {\r\nstroke(255, 128, 0, 196);\r\nfill(255, 0, 0, 32);\r\nstrokeWeight(2);\r\nfloat barWidth = 640 \/ float(maxValues.length);\r\nbeginShape();\r\nfloat lastX = 0;\r\nfloat y = 580 - 100 - map(0, 0, 32, 0, 380);\r\nvertex(lastX, y);\r\nfor (int i = 0; i &lt; maxValues.length; i++) {\r\nfloat x = i * barWidth + barWidth + 10;\r\ny = height - map(maxValues[i], 0, 32, 0, (height - 200)) - 100;\r\nif (i &gt;= 0) {\r\nvertex(lastX, y);\r\n}\r\nvertex(x, y);\r\nlastX = x;\r\n}\r\nendShape();\r\ndrawGrid();\r\n}<\/pre>\n<p>Publishing a read command to the MQTT broker will prompt either ESP based modules to start a scan.\u00a0 Here is a bash script that uses the mosquitto publish command.<\/p>\n<pre>#!\/bin\/bash\r\n# cloudacm.com\r\n\r\nmosquitto_pub -h &lt;MQTT Broker&gt; -t Wemos_NRF24L01\/read -m \"scan\"\r\nmosquitto_pub -h &lt;MQTT Broker&gt; -t ESP32Cam_NRF24L01\/read -m \"scan\"\r\n\r\nexit<\/pre>\n<p>I created some Easter Eggs that can be called by changing the publish script.<\/p>\n<pre>#!\/bin\/bash\r\n# cloudacm.com\r\n\r\nmosquitto_pub -h &lt;MQTT Broker&gt; -t ESP32Cam_NRF24L01\/Easter -m \"Turin\"\r\nmosquitto_pub -h &lt;MQTT Broker&gt; -t ESP32Cam_NRF24L01\/Easter -m \"Lisa\"\r\n\r\nexit<\/pre>\n<p>Adding these function calls in the MQTT Function section of the Arduino code followed by the functions should do the trick.<\/p>\n<pre>\/\/ MQTT Functions\r\nvoid callback(char* topic, byte* message, unsigned int length) { \r\nString messageTemp; \r\nfor (int i = 0; i &lt; length; i++) {\r\nmessageTemp += (char)message[i];\r\n} \r\nif (String(topic) == \"ESP32Cam_NRF24L01\/read\") {\r\nif(messageTemp == \"scan\"){\r\nshouldScan = true; \/\/ Set flag to start scanning\r\nscanCount = 0; \/\/ Reset scan count\r\n}\r\n}\r\n<strong>if (String(topic) == \"ESP32Cam_NRF24L01\/Easter\") {<\/strong>\r\n<strong>if(messageTemp == \"Turin\"){<\/strong>\r\n<strong>Turin(); \/\/ <\/strong>\r\n<strong>}<\/strong>\r\n<strong>}<\/strong>\r\n<strong>if (String(topic) == \"ESP32Cam_NRF24L01\/Easter\") {<\/strong>\r\n<strong>if(messageTemp == \"Lisa\"){<\/strong>\r\n<strong>Lisa(); \/\/ <\/strong>\r\n<strong>}<\/strong>\r\n<strong>}<\/strong> \r\n}<\/pre>\n<p>Here are the functions.<\/p>\n<pre>void Turin() {\r\n\/\/ Define an array of messages to publish\r\nconst char* eggString[] = {\r\n\"11,9,8,6,6,6,5,5,5,4,2,3,3,3,3,2,3,3,6,10,9,5,1,3,5,6,4,2,4,3,4,4,6,3,2,4,3,1,1,1,1,3,3,0,3,3,2,2,1,1,2,2,2,2,4,5,8,8,12,11,8,7,13,\",\r\n\"13,8,8,6,6,4,4,4,4,2,2,1,2,2,3,2,3,1,1,2,4,8,9,9,11,11,11,8,7,10,9,4,5,3,3,5,3,2,3,3,4,3,3,1,3,2,3,3,1,1,1,2,3,4,4,5,5,9,12,12,10,9,7,\",\r\n\"14,9,7,6,6,5,6,5,5,4,3,2,2,3,3,2,4,4,3,3,1,2,1,4,6,8,11,9,9,9,11,12,12,11,11,11,10,10,10,9,8,6,5,3,9,3,2,2,1,1,2,2,4,4,5,5,7,7,9,12,12,9,9,\",\r\n\"14,12,10,11,9,5,7,4,4,4,2,2,3,3,2,3,5,6,6,3,2,3,1,4,7,8,12,9,8,8,11,9,5,5,4,3,2,2,2,6,8,7,9,6,3,2,2,2,2,2,3,4,5,6,7,6,10,9,11,12,14,14,14,\",\r\n\"15,11,7,7,6,5,6,3,3,3,2,1,3,1,3,1,5,6,5,4,2,2,1,4,7,8,13,10,12,11,13,9,6,7,5,6,2,4,3,7,10,3,4,1,1,2,2,3,3,3,4,5,5,8,8,7,8,10,12,14,15,15,15,\",\r\n\"14,12,9,9,6,4,3,3,4,4,2,1,2,2,1,1,6,5,6,5,3,1,1,4,6,9,11,12,12,11,10,8,5,8,7,8,3,4,4,4,10,4,2,1,1,2,3,3,4,4,3,5,4,5,9,7,10,10,11,12,14,15,15,\",\r\n\"10,6,5,4,3,2,2,1,1,0,0,0,3,2,0,0,5,5,5,4,2,1,1,5,5,6,7,8,10,9,10,5,4,6,7,8,1,1,2,3,4,3,2,1,2,3,3,5,4,3,3,5,4,5,7,7,9,9,9,13,14,15,15,\",\r\n\"4,2,2,3,2,1,3,0,1,0,0,1,2,1,0,0,5,6,9,7,4,0,1,7,8,6,7,5,6,8,7,4,3,5,6,7,2,0,3,3,4,3,2,1,2,1,2,2,3,3,3,4,6,5,6,5,8,9,10,14,15,15,15,\",\r\n\"4,2,1,0,0,0,1,0,0,0,0,0,0,0,0,1,2,5,6,5,3,1,3,6,6,6,6,5,8,9,8,5,8,6,6,7,2,1,3,3,3,2,2,1,2,2,3,3,4,3,2,3,3,6,5,4,7,7,10,13,15,15,15,\",\r\n\"4,2,1,0,0,0,2,0,2,1,0,0,1,1,1,0,1,4,4,6,3,1,1,4,7,9,10,10,7,8,7,9,9,5,6,6,3,0,3,4,3,6,2,1,2,2,1,2,4,3,1,3,1,3,5,4,4,6,8,10,11,15,15,\",\r\n\"3,2,1,1,0,0,1,0,2,0,0,1,0,0,1,0,1,3,2,5,3,1,3,4,5,8,11,11,10,10,12,11,8,5,4,7,3,1,3,4,3,8,2,1,1,2,2,3,2,2,1,2,1,3,2,1,2,3,4,6,7,10,13,\",\r\n\"3,2,1,1,1,1,1,1,1,0,0,1,0,1,1,1,1,3,3,7,2,1,1,4,6,5,8,8,5,8,8,7,7,6,7,6,2,1,3,4,8,4,4,2,2,2,3,2,4,3,2,3,3,3,1,2,1,3,3,4,4,7,12,\",\r\n\"5,2,1,0,1,0,0,1,0,0,0,1,0,0,0,0,0,3,5,5,3,2,1,4,5,4,3,3,5,11,11,5,6,8,7,5,2,1,2,6,7,4,4,1,1,4,1,2,3,3,3,5,3,2,0,1,1,2,2,4,4,5,11,\",\r\n\"3,3,1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,4,5,4,3,1,2,4,6,6,5,6,7,10,11,7,9,11,8,7,2,1,2,4,5,7,5,2,1,3,2,3,4,2,2,3,5,3,2,2,2,2,3,5,5,6,9,\",\r\n\"1,2,1,1,0,0,0,0,0,0,0,0,1,0,1,0,0,3,5,6,1,1,1,5,6,6,7,6,5,8,8,7,8,7,6,5,2,3,2,4,6,7,4,3,3,3,4,3,3,2,3,3,2,4,2,3,3,3,2,4,5,7,10,\",\r\n\"0,1,1,1,1,0,0,0,0,0,0,0,1,0,0,0,2,4,8,6,3,3,4,7,8,7,6,5,3,8,8,5,6,7,7,7,5,2,2,3,4,4,4,3,2,2,2,3,2,2,2,3,4,4,3,4,2,1,2,4,5,7,13,\",\r\n\"1,1,1,0,1,0,1,0,0,0,0,1,1,0,0,0,1,4,5,6,4,2,5,8,7,7,7,6,4,8,6,6,5,8,9,8,5,3,1,3,4,3,3,2,1,2,4,3,3,2,2,3,5,3,2,2,2,1,4,9,8,9,14,\",\r\n\"1,0,0,1,0,0,1,0,0,0,1,0,0,0,1,0,0,3,5,6,1,2,2,3,3,4,4,4,5,9,7,5,4,8,7,8,6,3,2,3,4,4,3,0,2,2,3,2,3,2,2,3,3,3,2,2,4,3,6,6,5,7,10,\",\r\n\"0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,3,4,7,2,0,1,2,3,4,5,3,4,8,6,3,4,4,5,5,4,2,2,4,4,4,1,1,2,3,2,3,3,2,3,2,3,1,1,2,2,2,2,4,4,4,6,\",\r\n\"1,1,0,0,0,0,0,0,0,0,0,0,0,2,1,0,2,3,4,7,4,3,1,2,3,4,7,5,5,6,6,3,5,6,5,3,2,2,3,5,4,3,1,0,1,2,3,3,1,2,3,2,3,3,3,3,2,3,2,3,2,4,5,\",\r\n\"1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,1,2,4,5,3,3,3,4,4,3,4,4,5,8,7,4,4,5,5,3,2,3,3,4,5,2,2,1,1,2,4,3,3,2,3,2,3,2,1,3,3,2,1,2,2,4,6,\",\r\n\"0,0,0,0,0,0,1,0,1,0,1,0,0,1,0,0,1,1,5,5,4,3,5,8,8,9,10,11,7,7,10,7,7,7,6,6,5,4,3,4,5,2,2,1,0,2,4,2,1,2,1,2,3,3,3,3,3,5,2,2,1,4,8,\",\r\n\"0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,2,4,7,5,10,11,5,8,9,9,12,6,7,9,8,9,9,8,6,4,2,4,5,3,3,2,1,1,2,4,3,2,3,1,1,2,2,3,2,3,3,2,4,8,9,12,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,2,4,7,5,7,6,5,5,5,6,12,11,5,7,5,5,6,6,6,5,3,5,9,6,2,2,2,0,2,2,2,2,2,1,2,2,1,2,2,4,8,9,8,3,9,14,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,4,5,5,3,4,9,5,4,6,13,8,8,7,5,5,6,4,4,6,7,6,9,4,2,1,2,1,1,2,2,2,1,1,1,2,7,8,8,8,1,2,3,4,9,14,\",\r\n\"3,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,2,5,5,6,6,4,6,4,5,5,12,11,8,7,8,6,5,4,4,12,5,9,4,3,3,4,5,5,3,5,4,3,4,4,5,4,2,1,2,1,2,2,2,4,8,11,\",\r\n\"5,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,1,3,5,5,8,6,6,5,5,6,5,10,9,7,5,5,5,5,4,5,9,7,8,3,2,2,6,3,2,4,2,2,1,1,1,2,3,4,2,2,2,2,4,2,4,7,11,\",\r\n\"2,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,0,1,2,7,7,13,8,6,5,4,4,4,7,6,5,5,4,6,5,7,10,6,5,4,8,8,5,0,1,1,1,3,1,1,0,1,2,6,2,2,2,2,4,4,4,5,9,\",\r\n\"0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,2,2,8,10,8,9,10,7,6,6,6,6,7,8,3,5,8,9,7,8,8,8,6,2,2,1,1,1,1,2,2,3,3,2,2,3,5,5,2,3,4,3,5,5,7,10,\",\r\n\"1,1,1,0,2,0,0,0,1,0,0,0,0,0,2,1,0,1,2,2,4,7,6,8,9,10,10,7,9,8,9,6,5,10,8,8,7,4,2,1,0,2,2,1,2,2,2,2,2,4,3,2,4,3,5,3,3,3,4,4,5,7,11,\",\r\n\"1,1,1,4,1,2,1,0,1,3,1,3,3,1,0,0,0,1,2,1,1,4,7,10,9,9,9,11,12,11,12,9,8,8,6,8,5,4,1,1,1,3,2,1,2,2,3,3,4,4,2,2,2,3,3,2,4,3,4,6,6,12,12,\",\r\n\"0,1,1,2,1,2,3,2,3,2,0,2,4,5,7,8,8,6,7,8,9,10,6,3,3,4,6,8,8,8,10,10,8,7,7,7,3,2,2,1,2,1,2,2,2,3,1,4,4,3,3,4,4,5,2,2,3,3,4,5,6,6,12,\"\r\n};\r\n\r\nconst int numStrings = sizeof(eggString) \/ sizeof(eggString[0]);\r\n\r\n\/\/ Publish each message with a delay\r\nfor (int i = 0; i &lt; numStrings; i++) {\r\nclient.publish(\"ESP32Cam_NRF24L01\/data\", eggString[i]);\r\ndelay(500);\r\n}\r\n}\r\n\r\nvoid Lisa() {\r\n\/\/ Define an array of messages to publish\r\nconst char* lisaStrings[] = {\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,2,3,2,3,3,4,6,7,7,8,8,7,4,2,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,4,1,0,0,0,1,2,4,6,8,8,9,9,9,9,9,9,8,5,3,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,4,4,0,0,0,0,2,7,9,9,9,8,9,9,9,9,9,8,7,5,4,4,4,2,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,3,1,0,0,0,3,7,8,9,9,9,9,8,9,8,8,8,6,4,3,3,4,6,6,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,3,2,3,1,0,0,2,4,6,8,9,9,9,8,8,8,8,8,7,4,2,1,3,4,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,2,2,2,1,0,0,2,4,7,8,9,9,8,8,8,8,8,7,5,2,2,1,0,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,1,1,0,1,0,0,0,2,6,8,8,8,8,8,8,8,8,7,5,3,2,2,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,1,0,1,0,0,0,0,4,7,8,8,8,7,7,8,7,7,5,2,2,2,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,1,0,0,1,0,0,0,0,0,3,5,7,7,7,6,7,7,7,5,2,1,2,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,1,1,0,0,0,0,1,4,6,6,5,5,5,6,4,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,4,3,4,3,3,2,0,0,0,0,0,2,5,4,3,3,4,4,2,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,4,4,4,4,4,1,1,0,0,0,0,2,3,1,1,1,2,2,1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,3,3,4,5,4,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,2,2,3,3,2,1,1,0,0,0,4,5,3,2,1,1,0,0,0,1,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,3,2,1,0,0,0,4,8,7,6,5,3,1,1,0,0,0,0,0,0,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,1,1,1,2,1,0,0,0,4,7,7,5,4,5,5,3,1,0,0,0,0,0,0,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,2,2,1,1,0,0,0,1,5,6,6,3,3,5,6,4,2,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,2,2,2,1,0,0,0,2,6,7,6,1,5,7,6,4,2,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,2,2,1,1,0,0,0,4,7,8,8,6,6,8,7,5,3,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,2,2,2,2,2,1,0,5,8,8,8,7,7,8,8,7,4,0,0,0,0,0,1,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,3,5,4,3,5,1,0,6,7,7,8,6,7,8,8,8,5,1,0,0,0,0,0,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,5,7,7,7,6,1,0,4,6,4,7,5,5,7,4,5,6,2,0,0,0,0,1,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,8,8,8,7,2,0,3,4,4,7,6,2,4,2,1,2,1,0,0,0,0,1,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,7,8,8,8,8,8,3,0,5,7,8,8,8,7,7,7,6,4,1,0,0,0,0,2,3,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,5,0,5,8,9,8,9,9,7,7,6,5,1,0,0,0,0,3,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,8,8,8,8,8,8,8,3,3,7,9,9,9,9,8,6,4,2,0,0,0,0,0,7,7,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,6,3,7,9,9,9,9,8,6,3,1,0,0,0,0,4,8,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,8,8,8,7,7,8,8,8,3,5,8,9,9,8,7,4,1,0,0,0,0,0,6,8,9,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,8,8,7,7,8,7,8,6,1,3,5,5,4,3,0,0,0,0,0,0,5,8,8,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,7,7,7,7,7,7,7,5,1,1,2,0,0,0,0,0,0,0,4,7,8,7,7,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,7,7,7,7,7,7,7,7,6,2,1,0,0,0,0,0,2,5,7,7,7,7,7,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\",\r\n\"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,7,7,7,7,6,7,6,7,7,7,7,6,5,4,4,5,5,7,7,7,8,8,7,8,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\"\r\n};\r\n\r\nconst int numStrings = sizeof(lisaStrings) \/ sizeof(lisaStrings[0]);\r\n\r\n\/\/ Publish each message with a delay\r\nfor (int i = 0; i &lt; numStrings; i++) {\r\nclient.publish(\"ESP32Cam_NRF24L01\/data\", lisaStrings[i]);\r\ndelay(500);\r\n}\r\n}<\/pre>\n<p>If you want to create your own images, this python script processes an input image that is formatted as an ASCII PGM (Portable Gray Map, see <a href=\"https:\/\/people.sc.fsu.edu\/~jburkardt\/data\/pgma\/pgma.html\">https:\/\/people.sc.fsu.edu\/~jburkardt\/data\/pgma\/pgma.html<\/a> for background information) format that has a resolution of 63 x 32.<\/p>\n<pre>#!\/usr\/bin\/env python3\r\nimport sys\r\nimport time\r\nimport os\r\n\r\ndef step1_scaling(input_file=\"input.pgm\", output_file=\"output.step1\"):\r\n\"\"\"\r\nReads input.pgm, skips the first 4 lines, and for each remaining line,\r\nconverts the value to an integer, divides it by 16 (using integer division),\r\nand writes the result to output.step1.\r\n\"\"\"\r\ntry:\r\nwith open(input_file, 'r') as infile:\r\nlines = infile.readlines()[4:] # skip first 4 header lines\r\nexcept FileNotFoundError:\r\nsys.exit(1)\r\n\r\nwith open(output_file, 'w') as outfile:\r\nfor line in lines:\r\nstripped_line = line.strip()\r\nif not stripped_line:\r\ncontinue # Skip empty lines\r\ntry:\r\nnumber = int(stripped_line)\r\nresult = number \/\/ 16\r\noutfile.write(f\"{result}\\n\")\r\nexcept ValueError:\r\nprint(f\"Skipping invalid line: {stripped_line}\")\r\n\r\ndef step2_processing(input_file=\"output.step1\", output_file=\"output.step2\"):\r\n\"\"\"\r\nProcesses output.step1 by reading all numbers and joining them into a single\r\ncomma-separated string. It then breaks this list into lines, with each line\r\ncontaining 63 fields.\r\n\"\"\"\r\ntry:\r\nwith open(input_file, 'r') as infile:\r\n# Read non-empty, stripped lines as fields\r\nfields = [line.strip() for line in infile if line.strip()]\r\nexcept FileNotFoundError:\r\nsys.exit(1)\r\n\r\nwith open(output_file, 'w') as outfile:\r\n# Process fields in chunks of 63 items\r\nfor i in range(0, len(fields), 63):\r\nchunk = fields[i:i+63]\r\nline = \",\".join(chunk)\r\noutfile.write(line + \"\\n\")\r\n\r\ndef step3_sorting(input_file=\"output.step2\", output_file=\"output.step3\"):\r\n\"\"\"\r\nReverses the order of the lines in output.step2 and writes them to output.step3.\r\n\"\"\"\r\nif not os.path.isfile(input_file):\r\nsys.exit(1)\r\nwith open(input_file, 'r') as infile:\r\nlines = infile.readlines()\r\nwith open(output_file, 'w') as outfile:\r\nfor line in reversed(lines):\r\noutfile.write(line)\r\n\r\ndef process_line(line):\r\n\"\"\"\r\nFormats a given line by wrapping it inside an eggString assignment.\r\n\"\"\"\r\nline = line.rstrip(\"\\n\")\r\nreturn '\"' + line + ',\",'\r\n\r\ndef step4_formatting(input_file=\"output.step3\", output_file=\"output.step4\"):\r\n\"\"\"\r\nReads each line from output.step3, formats it using process_line(),\r\nand writes the result along with MQTT publishing commands to output.step4.\r\n\"\"\"\r\ntry:\r\nwith open(input_file, 'r') as infile, open(output_file, 'w') as outfile:\r\nfor line in infile:\r\nmodified_line = process_line(line)\r\noutfile.write(modified_line + \"\\n\")\r\nexcept FileNotFoundError:\r\nsys.exit(1)\r\n\r\ndef main():\r\nstep1_scaling()\r\nstep2_processing()\r\nstep3_sorting()\r\nstep4_formatting()\r\n\r\nif __name__ == \"__main__\":\r\nmain()<\/pre>\n<p>Plotting the results in Processing may not be preferred, so this python script will publish the plot to a MQTT broker.\u00a0 From there, Node-Red can subscribe to the topic and display the image.\u00a0 Here is Python code to create a plot in the &#8220;inferno&#8221; color scale.<\/p>\n<pre>import paho.mqtt.client as mqtt\r\nimport matplotlib.pyplot as plt\r\nimport numpy as np\r\nimport io\r\nimport base64\r\n\r\n# Create a global array to hold 32 rows of data.\r\ndata_array = [None] * 32\r\n\r\n# Global counter to track which row to update next.\r\ncurrent_row = 0\r\n\r\ndef update_plot():\r\n\"\"\"\r\nUpdate the plot if all 32 rows have been received.\r\nThe plot is generated as a heatmap and published to the MQTT topic 'sensor\/plot'.\r\n\"\"\"\r\nif not all(row is not None for row in data_array):\r\nprint(\"Waiting for complete data array...\")\r\nreturn\r\n\r\n# Convert the list of rows to a NumPy array.\r\narr = np.array(data_array)\r\n\r\n# Create a heatmap of the array.\r\nplt.figure(figsize=(8, 6))\r\nplt.style.use('dark_background')\r\n# plt.imshow(arr, cmap='viridis', aspect='auto')\r\nplt.imshow(arr, cmap='inferno', aspect='auto', origin='lower')\r\nplt.title(\"Sensor Data Array\")\r\nplt.colorbar()\r\n\r\n# Save the plot to an in-memory bytes buffer.\r\nbuf = io.BytesIO()\r\nplt.savefig(buf, format='png')\r\nplt.close() # Free up memory.\r\nbuf.seek(0)\r\n\r\n# Encode the image to a Base64 string.\r\nimg_base64 = base64.b64encode(buf.read()).decode('utf-8')\r\n\r\n# Publish the encoded image to the 'sensor\/plot' topic.\r\nmqtt_client.publish(\"sensor\/plot\", img_base64)\r\nprint(\"Plot updated and published.\")\r\n\r\ndef on_connect(client, userdata, flags, rc):\r\n\"\"\"\r\nCallback for when the MQTT client connects.\r\nSubscribes to the 'sensor\/data' topic.\r\n\"\"\"\r\nprint(\"Connected with result code \" + str(rc))\r\nclient.subscribe(\"sensor\/data\")\r\n\r\ndef on_message(client, userdata, msg):\r\n\"\"\"\r\nCallback for when an MQTT message is received.\r\nParses the comma-separated data, updates the global data array, and calls update_plot().\r\n\"\"\"\r\nglobal data_array, current_row\r\ntry:\r\n# Decode the payload to a string and strip whitespace.\r\npayload = msg.payload.decode('utf-8').strip()\r\nprint(\"Received message:\", payload)\r\n\r\n# Split the string by commas.\r\n# This filters out any empty strings (for example, from a trailing comma).\r\nvalues_str = [s for s in payload.split(',') if s]\r\n\r\n# Convert the string values to floats.\r\nrow_values = [float(v) for v in values_str]\r\n\r\n# Use the current_row counter as the row index.\r\nrow_index = current_row\r\ncurrent_row += 1\r\n\r\n# Check if row_index is within bounds.\r\nif row_index &gt;= len(data_array):\r\nprint(\"Row index out of bounds:\", row_index)\r\nreturn\r\n\r\n# Update the data array with the new row.\r\ndata_array[row_index] = row_values\r\nprint(f\"Updated row {row_index}.\")\r\n\r\n# Update the plot.\r\nupdate_plot()\r\n\r\nexcept Exception as e:\r\nprint(\"Error processing message:\", e)\r\n\r\n# Create an MQTT client instance.\r\nmqtt_client = mqtt.Client()\r\nmqtt_client.on_connect = on_connect\r\nmqtt_client.on_message = on_message\r\n\r\n# Connect to your MQTT broker.\r\nmqtt_client.connect(\"&lt;MQTT Broker&gt;\", 1883, 60)\r\n\r\n# Start the MQTT client network loop - but this was a problem so it's commented out\r\n# mqtt_client.loop_forever()<\/pre>\n<p>Here is the Node-Red flow.<\/p>\n<p><a href=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/Node-Red_NRF24L01_Flow.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-4820 size-medium\" src=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/Node-Red_NRF24L01_Flow-300x76.png\" alt=\"\" width=\"300\" height=\"76\" srcset=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/Node-Red_NRF24L01_Flow-300x76.png 300w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/Node-Red_NRF24L01_Flow.png 442w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>These are 2 simple MQTT In nodes followed by a template.\u00a0 Here is the template code.<\/p>\n<pre>&lt;div&gt;\r\n&lt;img src=\"data:image\/png;base64,{{msg.payload}}\" style=\"width:100%;\" \/&gt;\r\n&lt;\/div&gt;<\/pre>\n<p><a href=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/Magma.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-4818 size-medium\" src=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/Magma-300x225.png\" alt=\"\" width=\"300\" height=\"225\" srcset=\"https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/Magma-300x225.png 300w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/Magma-768x576.png 768w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/Magma-360x270.png 360w, https:\/\/www.cloudacm.com\/wp-content\/uploads\/2025\/02\/Magma.png 800w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>Inspiration for this post goes out to Rohf Henkel and J. Coliz, <a href=\"https:\/\/www.riyas.org\/2013\/12\/working-quick-start-guide-for-nrf24l01.html\">https:\/\/www.riyas.org\/2013\/12\/working-quick-start-guide-for-nrf24l01.html<\/a><code><\/code><code><\/code><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post will cover directly interfacing the NRF24L01 module with either the ESP32 or ESP8266 Wemos modules.\u00a0 In earlier posts I had used the Arduino Mini module as a shim.\u00a0 This allows the setup to be compact with fewer points of failure.\u00a0 This post will also introduce a plotting alternative that wasn&#8217;t covered in earlier posts. Here is the wiring diagram for the ESP8266 Wemos Module and its SPI connection to the NRF24L01. Here is the Arduino code used to&#8230;<\/p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https:\/\/www.cloudacm.com\/?p=4804\"> Read More<span class=\"screen-reader-text\">  Read More<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-4804","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts\/4804","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4804"}],"version-history":[{"count":17,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts\/4804\/revisions"}],"predecessor-version":[{"id":4807,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=\/wp\/v2\/posts\/4804\/revisions\/4807"}],"wp:attachment":[{"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4804"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4804"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cloudacm.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4804"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}