15. Interface and Application Programming¶
Hero Shot of the Week¶
Summary¶
I feel like this week was more about debugging than actual interface programming.
But I had my first contact with interactive web interface programming.
I am also getting tired. 15 weeks of hard brain working is getting the toll on me..
Work Process¶
Calendar¶
Date | To be done | What was done |
---|---|---|
Thursday | - 10h local meeting | |
Friday | - FabLab normal work | - FabLab normal work |
Saturday | - cracking my head on code | |
Sunday | - Fete de l'IRIS | - Fete de l'IRIS |
Monday | - continue cracking head on code | |
Tuesday | - FabLab normal work | |
Wednesday | - 12h Local - 13h Regional - 15h Global |
Introduction¶
Bas Pijls
made an introduction.
And some more links from the Global meeting.
Java Script * work in all browsers * D3 * a library for data visualization * JS Tutorial
p5.js
- runs in browser
- quick graphs
- has web editor --> preview
function draw()
is like a loop- VSCode has extensions for it
Recommended book: The Nature of Code
Animation
- variables to move object
Web Serial
- Communication between web browser and your embedded device
- Chrome R/W Tutorial
Some reference sites:
- Nadieh
- Processing
- Rustling
- learn rust
- Quarto
- open-source scientific and technical publishing system
- typer
- Typer is a library for building CLI applications
- Stream
My work¶
Setup VS Code & p5.js¶
Guide I followed. (Bas also showed it but I was too slow to follow)
But even when using VS Code, the online web p5 Editor is useful as it give back error messages.
BlueTooth¶
Bas Managed to connect p5.js with a BLE devices. He shared the code. Now it is my turn to do it.
We need to add the following code to the html setup
The code he shared are the following:
p5.js
BLE
The guides / sites used are:
The compilation is so slooooow...
WHY?!
Lets check verbose compile
And maybe other cause
too.
It works.
Bas also wrote up the guide.
Here is a another method to communicate with GATT and API. Did not test it.
Multiple Devices¶
With BLE¶
At the beginning I was trying to send two values for two different characteristics.
But it seems that I should send one, than wait, than send second value. Read
Now p5 receives both but shows them at the same place...
Well...
I am back at fucking around and wait for compiler if it work...
And there is also the issue that the ESP32 send a value of 0 but it get magically transformed to 48 on p5.js...
Putting a correction value into the ESP code sets p5 out of value.
Trying out ChatGPT if he has a better idea than me.
Fucker.
It managed to do it...
ChatGPT prompt
Rewrite the following code so that the ESP32-S3 can send multiple values via BLE to p5.js
The following is the ESP32-S3 code:
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#define SERVICE_UUID "19B10010-E8F2-537E-4F6C-D104768A1214"
#define CHARACTERISTIC_UUID "a492c083-6ecd-4544-a620-5381017a66e9"
BLECharacteristic *pCharacteristic;
int value = 0;
void setup() {
Serial.begin(115200);
// Initialize BLE
BLEDevice::init("ESP32-BLE"); // Device name shown in browser
BLEServer *pServer = BLEDevice::createServer();
// Create BLE service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create BLE characteristic
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ
);
// Set initial value
pCharacteristic->setValue(String(value).c_str());
// Start the service
pService->start();
// Start advertising
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->start();
Serial.println("BLE device is now advertising");
}
void loop() {
// Simulate value update every second
value++;
pCharacteristic->setValue(String(value).c_str());
delay(1000);
}
The following is the p5.js code:
const serviceUuid = "19B10010-E8F2-537E-4F6C-D104768A1214";
let myCharacteristic;
let myValue = 0;
let myBLE;
function setup() {
// Create a p5ble class
myBLE = new p5ble();
createCanvas(200, 200);
textSize(20);
textAlign(CENTER, CENTER);
// Create a 'Connect' button
const connectButton = createButton("Connect");
connectButton.mousePressed(connectToBle);
}
function connectToBle() {
// Connect to a device by passing the service UUID
myBLE.connect(serviceUuid, gotCharacteristics);
}
// A function that will be called once got characteristics
function gotCharacteristics(error, characteristics) {
if (error) console.log("error: ", error);
console.log("characteristics: ", characteristics);
myCharacteristic = characteristics[0];
// Read the value of the first characteristic
myBLE.read(myCharacteristic, gotValue);
}
// A function that will be called once got values
function gotValue(error, value) {
if (error) console.log("error: ", error);
console.log("value: ", value);
myValue = value;
// After getting a value, call p5ble.read() again to get the value again
myBLE.read(myCharacteristic, gotValue);
}
function draw() {
background(250);
text(myValue, 100, 100);
}}
And here are the codes it gave:
BLE - Multi Data
p5.js - Multi Data
Two days later I have calmed down of my frustration of trying for 3h and than ChatGPT doing it in less than a minute.
With BLE - MESH¶
Lets see what ChatGPT can do than.
I have no clue if this is a good path to go down or not...
Well, it gave something. Now to test if it works too.
Of course.
I am loosing time because of library errors as I am working on my PC and not my laptop.
So the setups are different...
Mmh
Time to document.
ChatGPT prompt + Answer
Write a code with the following condition
- ESP32-S3
- Networking is bluetooth Mesh with painlessMesh.h library
- it need to send multiple data from multiple sensors
- it needs a p5.js code to visualize the data online
should I give you an example code to enhance ?
Yes, please! Sharing your example code will help me tailor the ESP32-S3 and painlessMesh
setup precisely to your needs—including how you're reading sensor data, formatting messages, and your current BLE or Mesh logic.
Once you share it, I’ll:
- Integrate multiple sensor readings.
- Format the data for reliable mesh transmission.
- Add a p5.js sketch to visualize the data online via a BLE bridge or serial proxy.
Could you please upload or paste the example you want to build upon?
ChatGPT prompt 2
/\*
Rui Santos
Complete project details at [https://RandomNerdTutorials.com/esp-mesh-esp32-esp8266-painlessmesh/](https://RandomNerdTutorials.com/esp-mesh-esp32-esp8266-painlessmesh/)
This is a simple example that uses the painlessMesh library: [https://github.com/gmag11/painlessMesh/blob/master/examples/basic/basic.ino](https://github.com/gmag11/painlessMesh/blob/master/examples/basic/basic.ino)
\*/
//BlueTooth MESH
\#include "painlessMesh.h"
//JSON file creation & Reading
\#include \<Arduino\_JSON.h>
//RGB LED
\#include \<FastLED.h>
//LED SETUP
\#define LED\_PIN D0 //or D9
\#define NUM\_LEDS 1
\#define BRIGHTNESS 32
\#define LED\_TYPE WS2811
\#define COLOR\_ORDER GRB
CRGB leds\[NUM\_LEDS];
\#define UPDATES\_PER\_SECOND 100
CRGBPalette16 currentPalette;
TBlendType currentBlending;
extern CRGBPalette16 myRedWhiteBluePalette;
extern const TProgmemPalette16 myRedWhiteBluePalette\_p PROGMEM;
int R;
int G;
int B;
//Button
\#define Button\_PIN1 D6 //or D8
\#define Button\_PIN2 D8
//#define Button\_PIN2 D6 //or D8
//the name of the MESH network
\#define MESH\_PREFIX "whateverYouLike"
//password of the MESH network
\#define MESH\_PASSWORD "somethingSneaky"
// This refers to the the TCP port that you want the mesh server to run on. The default is 5555
\#define MESH\_PORT 5555
//Number for this node
int nodeNumber = 1;
//Node ID to send a specific message
int Node\_ID = 1;
//String to send to other nodes with sensor readings
String final\_message;
//It is recommended to avoid using delay() in the mesh network code.
//To maintain the mesh, some tasks need to be performed in the background.
//Using delay() will stop these tasks from happening and can cause the mesh to lose stability/fall apart.
//The following line creates a new Scheduler called userScheduler
Scheduler userScheduler; // to control your personal task
//Create a painlessMesh object called mesh to handle the mesh network
painlessMesh mesh;
// User stub
void sendMessage() ; // Prototype so PlatformIO doesn't complain
String Message(); // Prototype for sending message
//Create a task called taskSendMessage responsible for calling the sendMessage() function every X (the number) second as long as the program is running.
Task taskSendMessage( TASK\_SECOND \* 5 , TASK\_FOREVER, \&sendMessage );
int M1;
int M2;
String Message() {
JSONVar jsonReadings;
jsonReadings\["node"] = nodeNumber;
jsonReadings\["M1"] = M1;
jsonReadings\["M2"] = M2;
//Message() variable is then converted into a JSON string using the stringify() method and saved on the Message variable
final\_message = JSON.stringify(jsonReadings);
return final\_message;
}
//The sendMessage() function sends a message to all nodes in the message network (broadcast).
void sendMessage() {
String msg = Message();
//board chip ID
//msg += mesh.getNodeId();
//Replaced by nodeNumber
//To broadcast a message, simply use the sendBroadcast() method on the mesh object and pass as argument the message (msg) you want to send.
//mesh.sendBroadcast(msg);
//Send to specific Node
mesh.sendSingle(Node\_ID, msg);
//Every time a new message is sent, the code changes the interval between messages (one to five seconds).
taskSendMessage.setInterval( random( TASK\_SECOND \* 1, TASK\_SECOND \* 5 ));
}
int M1\_I;
int M2\_I;
//several callback functions are created that will be called when specific events happen on the mesh.
//The receivedCallback() function prints the message sender (from) and the content of the message (msg.c\_str()).
void receivedCallback( uint32\_t from, String \&msg ) {
//Serial.printf("Received from %u msg=%s\n", from, msg.c\_str());
//The message comes in JSON format, so, we can access the variables as follows:
JSONVar myObject = JSON.parse(msg.c\_str());
int node = myObject\["node"];
M1\_I = myObject\["M1"];
M2\_I = myObject\["M2"];
//Serial.print(M1\_I);
}
//The newConnectionCallback() function runs whenever a new node joins the network.
//This function simply prints the chip ID of the new node.
//You can modify the function to do any other task.
void newConnectionCallback(uint32\_t nodeId) {
Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);
}
//The changedConnectionCallback() function runs whenever a connection changes on the network (when a node joins or leaves the network).
void changedConnectionCallback() {
Serial.printf("Changed connections\n");
}
//The nodeTimeAdjustedCallback() function runs when the network adjusts the time, so that all nodes are synchronized.
//It prints the offset.
void nodeTimeAdjustedCallback(int32\_t offset) {
Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
}
void setup() {
pinMode(Button\_PIN1, INPUT);
pinMode(Button\_PIN2, INPUT);
Serial.begin(115200);
FastLED.addLeds\<LED\_TYPE, LED\_PIN, COLOR\_ORDER>(leds, NUM\_LEDS).setCorrection( TypicalLEDStrip );
FastLED.setBrightness( BRIGHTNESS );
currentPalette = RainbowColors\_p;
currentBlending = LINEARBLEND;
//mesh.setDebugMsgTypes( ERROR | MESH\_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG\_TYPES | REMOTE ); // all types on
mesh.setDebugMsgTypes( ERROR | STARTUP ); // set before init() so that you can see startup messages
//Initialize the mesh with the details defined earlier.
mesh.init( MESH\_PREFIX, MESH\_PASSWORD, \&userScheduler, MESH\_PORT );
//Assign all the callback functions to their corresponding events.
mesh.onReceive(\&receivedCallback);
mesh.onNewConnection(\&newConnectionCallback);
mesh.onChangedConnections(\&changedConnectionCallback);
mesh.onNodeTimeAdjusted(\&nodeTimeAdjustedCallback);
//Finally, add the taskSendMessage function to the userScheduler.
//The scheduler is responsible for handling and running the tasks at the right time.
userScheduler.addTask( taskSendMessage );
//Finally, enable the taskSendMessage, so that the program starts sending the messages to the mesh.
taskSendMessage.enable();
}
void loop() {
//Button Read
if (digitalRead(Button\_PIN1) == HIGH){
M1 = 2;
}
else{
M1 = 0;
}
//Button Read + LED
if (digitalRead(Button\_PIN2) == HIGH){
R = random(0,255);
G = random(0,255);
B = random(0,255);
FastLED.show(); FastLED.delay(1000 / UPDATES_PER_SECOND);
}
else{
R = 0;
G = 0;
B = 0;
for(int i = 0; i < NUM\_LEDS; i++ ){
leds\[i].setRGB(R,G,B); // Set Color HERE!!!
}
for(int i = 0; i < NUM_LEDS; i++ ){ leds[i].setRGB(R,G,B); // Set Color HERE!!! }
FastLED.show(); FastLED.delay(1000 / UPDATES_PER_SECOND);
FastLED.show(); FastLED.delay(1000 / UPDATES_PER_SECOND);ChatGPT prompt 2 Answer
Yes, you can enhance your current ESP32-S3 mesh network setup using the painlessMesh
library to send multiple sensor readings and visualize them in real-time with p5.js. Here's how you can achieve this:
🔧 Enhancing Your ESP32-S3 Mesh Network
1. Integrate Multiple Sensors:
Expand your Message()
function to include readings from various sensors. For instance, if you're using temperature and humidity sensors, you can structure your JSON message as follows:
String Message() {
JSONVar jsonReadings;
jsonReadings["node"] = nodeNumber;
jsonReadings["temperature"] = temperatureValue;
jsonReadings["humidity"] = humidityValue;
// Add more sensor readings as needed
final_message = JSON.stringify(jsonReadings);
return final_message;
}
2. Broadcasting Messages: Instead of sending messages to a specific node, you can broadcast them to all nodes in the mesh network:
This approach ensures that all nodes, including a designated gateway node, receive the sensor data.
🌐 Visualizing Data with p5.js
1. Setting Up Serial Communication: On the gateway node (which could be connected to your computer), ensure that it forwards received mesh messages to the serial port:
2. p5.js Sketch to Read Serial Data:
Use the p5.js serialport
library to read data from the serial port. Here's a basic example:
let serial;
let latestData = "";
function setup() {
createCanvas(400, 300);
serial = new p5.SerialPort();
serial.open("/dev/tty.usbserial-XXXXX"); // Replace with your serial port
serial.on('data', gotData);
}
function gotData() {
let currentString = serial.readLine();
if (!currentString) return;
latestData = currentString;
}
function draw() {
background(220);
text(latestData, 10, 10);
}
This sketch reads incoming serial data and displays it on the canvas. You can parse the JSON string and visualize specific sensor values as needed.
📚 Additional Resources
-
painlessMesh Documentation: Provides detailed information on setting up and managing mesh networks. (GitHub)(TechRM)
-
Visualizing Sensor Data: For more advanced visualization techniques, consider exploring resources on integrating ESP32 with web-based dashboards. (Random Nerd Tutorials)(TechRM)
By implementing these enhancements, your ESP32-S3 mesh network will be capable of transmitting multiple sensor readings, and you'll be able to visualize the data in real-time using p5.js. If you need further assistance or have specific requirements, feel free to ask!
ChatGPT prompt 3
it says that : TypeError: p5.SerialPort is not a constructor
ChatGPT prompt 3 - Answer
The error message "TypeError: p5.SerialPort is not a constructor" indicates that the p5.serialport
library is not properly included in your p5.js project. This library is essential for serial communication between p5.js and devices like Arduino.
🛠️ How to Fix the Error
1. Include the p5.serialport
Library:
In the p5.js Web Editor (https://editor.p5js.org), you need to manually add the p5.serialport
library to your project. Here's how:
-
Open your sketch in the p5.js Web Editor.
-
Click on the "Files" tab.
-
Open the
index.html
file. -
Within the
<head>
section, add the following line:(itp.nyu.edu)
- Save the changes.
2. Use the p5.serialcontrol
Application:
Since web browsers cannot directly access serial ports due to security restrictions, you'll need to use the p5.serialcontrol
application as a bridge between your browser-based p5.js sketch and the serial device. Here's what to do:(itp.nyu.edu)
- Download the
p5.serialcontrol
application from the official repository: https://github.com/p5-serial/p5.serialcontrol. - Install and run the application on your computer.
- Ensure your Arduino or other serial device is connected to your computer via USB.
- In the
p5.serialcontrol
application, select the appropriate serial port corresponding to your device.(Stack Overflow)
3. Adjust Your p5.js Sketch:
With the library included and the p5.serialcontrol
application running, you can now use serial communication in your p5.js sketch. Here's a basic example:
let serial;
let latestData = "waiting for data";
function setup() {
createCanvas(400, 300);
serial = new p5.SerialPort();
serial.on('list', printList);
serial.on('connected', serverConnected);
serial.on('open', portOpen);
serial.on('data', serialEvent);
serial.on('error', serialError);
serial.on('close', portClose);
serial.list();
serial.open('/dev/tty.usbmodem14101'); // Replace with your port
}
function printList(portList) {
for (let i = 0; i < portList.length; i++) {
print(i + " " + portList[i]);
}
}
function serverConnected() {
print('Connected to server.');
}
function portOpen() {
print('The serial port opened.');
}
function serialEvent() {
latestData = serial.readLine();
print(latestData);
}
function serialError(err) {
print('Something went wrong with the serial port. ' + err);
}
function portClose() {
print('The serial port closed.');
}
function draw() {
background(220);
text(latestData, 10, 10);
}
Ensure you replace '/dev/tty.usbmodem14101'
with the actual port name of your device, which you can find using the serial.list()
function or within the p5.serialcontrol
application.(Stack Overflow)
Note: The p5.js Web Editor has limitations regarding serial communication due to browser security policies. For more advanced serial communication features, consider developing and running your p5.js sketches locally using a code editor like Visual Studio Code, and serving your project using a local server.(itp.nyu.edu)
For further guidance on setting up serial communication between p5.js and Arduino, you can refer to this comprehensive tutorial: Lab: Serial Input to P5.js Using the p5.serialport library - ITP - NYU.(itp.nyu.edu)
If you need assistance adapting your specific project for serial communication or have other questions, feel free to ask!
ChatGPT prompt 4
you have given me this link https://github.com/p5-serial/p5.serialcontrol but on the page it says the project is deprecated since 2022 should I still use it ?
ChatGPT prompt 4 - Answer
You're absolutely right to question that—you should not rely on p5.serialcontrol
anymore, especially since it's marked deprecated as of 2022.
🔥 What That Means
- The project is no longer maintained.
- It may break with browser or OS updates.
- It's not recommended for new projects.
✅ Recommended Alternatives
-
For Bluetooth (ESP32 BLE): Use
p5.ble.js
(as you're already doing)—modern, web-native, and perfect for browser-based projects like those on editor.p5js.org. -
For USB Serial (wired connection):
-
Use Web Serial API directly (if you're working in a browser that supports it, like Chrome).
- You can also try Serial.js or build your own serial handler using JavaScript + Web Serial.
🌟 Summary
- ❌ Avoid using
p5.serialcontrol
. - ✅ Use
p5.ble.js
for wireless BLE (ideal with ESP32). - ✅ Use Web Serial API for direct USB serial, if needed.
Would you like a working example using Web Serial or should we stick to p5.ble.js
for your current ESP32 project?
ChatGPT prompt 5
write me a code that connects to the mesh network of painlessMesh and converts the sent data to a p5.js interface that is open in painlessMesh connection is made with my laptop
ChatGPT prompt 5 - Answer
To visualize data from your ESP32 mesh network in a p5.js interface running on your laptop, you'll need to establish a communication bridge between the mesh network and your computer. Since painlessMesh
operates over Wi-Fi, and p5.js runs in the browser, direct communication isn't straightforward. However, you can achieve this by designating one ESP32 node as a gateway that connects the mesh network to your laptop via serial communication.
Here's an overview of the setup:
-
Mesh Network Setup:
-
Use the
painlessMesh
library to establish a mesh network among multiple ESP32 nodes. -
Each node collects sensor data and sends it through the mesh network.
-
Gateway Node Configuration:
-
Designate one ESP32 node as the gateway.
-
This node will receive data from the mesh network and forward it to your laptop via serial communication.
-
Laptop Interface with p5.js:
-
On your laptop, use a p5.js sketch to read the serial data sent by the gateway node.
- Visualize the data in real-time using p5.js's drawing capabilities.
Implementing the Gateway Node:
Here's a simplified example of how the gateway node can be programmed:
#include <painlessMesh.h>
#define MESH_PREFIX "yourMeshNetwork"
#define MESH_PASSWORD "yourPassword"
#define MESH_PORT 5555
Scheduler userScheduler;
painlessMesh mesh;
void receivedCallback(uint32_t from, String &msg) {
Serial.println(msg); // Forward the received message to the serial port
}
void setup() {
Serial.begin(115200);
mesh.setDebugMsgTypes(ERROR | STARTUP);
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
mesh.onReceive(&receivedCallback);
}
void loop() {
mesh.update();
}
Setting Up the p5.js Interface:
To read serial data in p5.js, you can use the Web Serial API. Note that this API is only supported in certain browsers like Chrome. Here's how you can set up your p5.js sketch:(itp.nyu.edu)
-
Include the Web Serial API:
-
Ensure your browser supports the Web Serial API.
-
You don't need to include any additional libraries; the API is built into supported browsers.
-
Request Serial Port Access:
-
Prompt the user to select the appropriate serial port connected to the ESP32 gateway node.
-
Read and Visualize Data:
-
Once connected, read incoming serial data and use p5.js functions to visualize it.
Here's a basic example of a p5.js sketch using the Web Serial API:
let port;
let reader;
let inputDone;
let inputStream;
async function connect() {
// Prompt user to select any serial port.
port = await navigator.serial.requestPort();
// Wait for the serial port to open.
await port.open({ baudRate: 115200 });
const decoder = new TextDecoderStream();
inputDone = port.readable.pipeTo(decoder.writable);
inputStream = decoder.readable;
reader = inputStream.getReader();
readLoop();
}
async function readLoop() {
while (true) {
const { value, done } = await reader.read();
if (done) {
// Allow the serial port to be closed later.
reader.releaseLock();
break;
}
if (value) {
console.log(value);
// Process the incoming data here
}
}
}
function setup() {
createCanvas(400, 300);
const connectButton = createButton('Connect');
connectButton.mousePressed(connect);
}
function draw() {
background(220);
// Visualization code goes here
}
-
Browser Compatibility: The Web Serial API is only supported in certain browsers like Chrome and Edge. Ensure you're using a compatible browser.
-
Security: The browser will prompt the user to grant access to the serial port. This is a security feature to prevent unauthorized access.
-
Data Format: Ensure that the data sent from the ESP32 gateway node is in a format that's easy to parse in JavaScript, such as JSON or newline-delimited strings.
By setting up your ESP32 mesh network with a designated gateway node and using the Web Serial API in p5.js, you can effectively visualize real-time data from your mesh network directly in your browser.
And now we are here :
Tinkering with my code. The ESP32-S3 reboots periodically... The node that is sending messages. The receiver node does not reboot.
I am debugging.
This takes sooo much time.
Still debugging
I hate this.
ChatGPT prompt 6 - Debugging
follow up serial monitor message still restarting
15:31:04.189 -> setLogLevel: ERROR | STARTUP |
15:31:04.189 -> STARTUP: init(): 1
15:31:04.189 -> STARTUP: AP tcp server established on port 5555
15:31:11.043 -> --> startHere: New Connection, nodeId = 2426472576
15:31:11.043 -> Changed connections
15:31:11.043 -> Adjusted time 3065797241. Offset = -1237200867
15:31:11.276 -> Adjusted time 3066029299. Offset = -1306
15:31:36.165 ->
15:31:36.165 -> abort() was called at PC 0x42088da7 on core 1
15:31:36.165 ->
15:31:36.165 ->
15:31:36.165 -> Backtrace: 0x403765e9:0x3fcab190 0x4037d7f5:0x3fcab1b0 0x403841e5:0x3fcab1d0 0x42088da7:0x3fcab250 0x42088ddc:0x3fcab270 0x42088595:0x3fcab290 0x42088f39:0x3fcab2b0 0x42005c9d:0x3fcab2d0 0x42005cd1:0x3fcab2f0 0x42005e3d:0x3fcab310 0x4200ac96:0x3fcab330 0x4200cb39:0x3fcab440 0x4200cbba:0x3fcab490 0x4201802c:0x3fcab4b0 0x4037e4a2:0x3fcab4d0
15:31:36.165 ->
15:31:36.165 ->
15:31:36.165 ->
ChatGPT prompt 6 - Debugging - Answer
Thank you for providing detailed information about your ESP32-S3 setup and the serial output. Based on the serial logs and the code you've shared, it appears that the device is encountering a runtime error leading to a reboot, specifically:
This message indicates that the program has encountered a critical error, such as an illegal memory access or a failed assertion, causing it to abort execution. Let's analyze potential causes and solutions.
🔍 Potential Causes
-
Memory Corruption or Invalid Access: The error message suggests that the program might be accessing memory it shouldn't, leading to a crash. This can happen due to:
-
Dereferencing null or invalid pointers.
- Buffer overflows.
-
Improper use of dynamic memory allocation.
-
FastLED Library Initialization: The
FastLED
library is known to be sensitive to the order and timing of its initialization. IfFastLED.addLeds()
is called before the LED strip is properly powered or connected, it might cause issues. -
Incorrect Pin Definitions: Using undefined or incorrect pin numbers like
D0
,D6
, andD8
can lead to unexpected behavior. The ESP32-S3 doesn't define pins asD0
,D6
, etc., by default. Instead, it uses GPIO numbers. -
Blocking Code in
loop()
: Theloop()
function contains a call tosendMessage()
followed bymesh.update()
. IfsendMessage()
or any function it calls is blocking or takes a long time to execute, it might interfere with the mesh network's timing, leading to a crash.
✅ Recommended Solutions
- Use GPIO Numbers Instead of
D
Notation: ReplaceD0
,D6
, andD8
with their corresponding GPIO numbers. For example:
Ensure these GPIOs are suitable for your application and not reserved for other functions.
-
Initialize FastLED Carefully: Ensure that the LED strip is properly connected and powered before initializing
FastLED
. Also, consider moving the LED test code out of thesetup()
function to prevent potential blocking during initialization. -
Avoid Blocking Code in
loop()
: Modify theloop()
function to minimize blocking operations. For example:
Move the sendMessage()
call to a scheduled task or timer to prevent blocking the main loop.
- Add Null Checks After JSON Parsing: When parsing JSON data, always check if the parsing was successful before accessing its members:
JSONVar myObject = JSON.parse(msg.c_str());
if (JSON.typeof(myObject) == "undefined") {
Serial.println("Parsing input failed!");
return;
}
-
Use Exception Decoder for Detailed Debugging: Utilize the EspExceptionDecoder tool to decode the backtrace and pinpoint the exact line in your code causing the crash. This can provide more insight into the issue.
-
Update Libraries and Frameworks: Ensure that all your libraries, including
painlessMesh
,FastLED
, and the ESP32 board support package, are up to date. Compatibility issues can sometimes cause unexpected behavior.
🧪 Additional Debugging Steps
- Monitor Free Heap Memory: Periodically check the available heap memory to detect potential memory leaks:
- Enable Detailed Logging: Increase the verbosity of logging to capture more detailed information:
mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE);
- Test with Minimal Code: Create a minimal version of your code that only initializes the mesh network without additional functionalities. Gradually add components back to identify the source of the crash.
🌟 Final Thoughts
Debugging embedded systems can be challenging, but by systematically isolating components and monitoring system resources, you can identify and resolve issues effectively. Remember to consult the Fab Academy Evaluation Criteria to ensure your project meets the necessary standards.
Stay persistent and curious—every challenge is an opportunity to learn and grow. If you need further assistance, feel free to ask!
I am changing this, changing that.
Trying this, trying that.
But this fucking thing is still rebooting every 20-30s...
Further testing
It seems it reboots when there is another ESP32 on the channel.
Lets do some interface to change our mind...
ChatGPT prompt 7 - Interface
could you add the following modifications to this code:
- add the possibility to upload an image of a floor plan
- depending on the node number the message is received the updated numbers need to be at different positions on the screen (each room has different node number)
let port;
let reader;
let inputDone;
let inputStream;
async function connect() {
// Prompt user to select any serial port.
port = await navigator.serial.requestPort();
// Wait for the serial port to open.
await port.open({ baudRate: 115200 });
const decoder = new TextDecoderStream();
inputDone = port.readable.pipeTo(decoder.writable);
inputStream = decoder.readable;
reader = inputStream.getReader();
readLoop();
}
async function readLoop() {
while (true) {
const { value, done } = await reader.read();
if (done) {
// Allow the serial port to be closed later.
reader.releaseLock();
break;
}
if (value) {
console.log(value);
// Process the incoming data here
}
}
}
function setup() {
createCanvas(400, 300);
const connectButton = createButton('Connect');
connectButton.mousePressed(connect);
}
function draw() {
background(220);
// Visualization code goes here
}
ChatGPT prompt 7 - Interface - Answer
Here's the enhanced version of your sketch, now supporting drag-and-drop calibration of node positions directly on the floor plan:
let port;
let reader;
let inputDone;
let inputStream;
let floorPlan;
let imgInput;
let nodeData = {};
let nodePositions = {
"1": { x: 100, y: 100 },
"2": { x: 300, y: 100 },
"3": { x: 100, y: 250 },
"4": { x: 300, y: 250 }
};
let draggingNode = null;
let dragOffset = { x: 0, y: 0 };
function setup() {
createCanvas(600, 400);
textSize(16);
textAlign(LEFT, CENTER);
const connectButton = createButton('Connect Serial');
connectButton.position(10, height + 10);
connectButton.mousePressed(connect);
imgInput = createFileInput(handleFile);
imgInput.position(150, height + 10);
}
function draw() {
background(255);
if (floorPlan) {
image(floorPlan, 0, 0, width, height);
} else {
fill(100);
text("Upload a floor plan image", 20, 20);
}
for (let node in nodeData) {
let pos = nodePositions[node];
let val = nodeData[node];
fill(0);
ellipse(pos.x, pos.y, 10, 10);
text(`Node ${node}`, pos.x + 10, pos.y);
text(`Temp: ${val.temperature}°C`, pos.x + 10, pos.y + 20);
text(`Hum: ${val.humidity}%`, pos.x + 10, pos.y + 40);
}
}
function handleFile(file) {
if (file.type === 'image') {
floorPlan = loadImage(file.data);
}
}
async function connect() {
try {
port = await navigator.serial.requestPort();
await port.open({ baudRate: 115200 });
const decoder = new TextDecoderStream();
inputDone = port.readable.pipeTo(decoder.writable);
inputStream = decoder.readable;
reader = inputStream.getReader();
readLoop();
} catch (err) {
console.error('Serial connection failed:', err);
}
}
async function readLoop() {
while (true) {
const { value, done } = await reader.read();
if (done) {
reader.releaseLock();
break;
}
if (value) {
try {
const data = JSON.parse(value.trim());
const node = data.node.toString();
nodeData[node] = {
temperature: data.temperature,
humidity: data.humidity
};
if (!(node in nodePositions)) {
nodePositions[node] = { x: random(width), y: random(height) };
}
} catch (e) {
console.error("Invalid JSON:", value);
}
}
}
}
function mousePressed() {
for (let node in nodePositions) {
let pos = nodePositions[node];
if (dist(mouseX, mouseY, pos.x, pos.y) < 10) {
draggingNode = node;
dragOffset.x = mouseX - pos.x;
dragOffset.y = mouseY - pos.y;
break;
}
}
}
function mouseDragged() {
if (draggingNode) {
nodePositions[draggingNode].x = mouseX - dragOffset.x;
nodePositions[draggingNode].y = mouseY - dragOffset.y;
}
}
function mouseReleased() {
draggingNode = null;
}
🧩 Features Added:
- Drag each node to its real-world location on the uploaded floor plan.
- JSON-based updates dynamically appear at the dragged positions.
- Unknown nodes are initialized with random screen positions and can also be dragged.
Would you like to save the final node positions to a file or load them from a file next time?
ChatGPT prompt 8 - Interface JSON error - Answer
last code gives Invalid JSON error is there a way to correct it ?
ChatGPT prompt 8 - Interface JSON error - Answer
Yes, the "Invalid JSON" error typically occurs when the data coming from the serial port is not well-formed JSON, arrives incomplete, or includes noise characters like newlines or extra data chunks.
✅ Here's how to fix it:
We need to clean and buffer the incoming serial data to ensure we parse only complete JSON strings. Update your readLoop()
function like this:
let buffer = '';
async function readLoop() {
while (true) {
const { value, done } = await reader.read();
if (done) {
reader.releaseLock();
break;
}
if (value) {
buffer += value;
// Look for newlines or similar delimiters to separate complete messages
let lines = buffer.split('\n');
buffer = lines.pop(); // Save last (possibly incomplete) part for next time
for (let line of lines) {
line = line.trim();
if (line.length > 0) {
try {
const data = JSON.parse(line);
const node = data.node.toString();
nodeData[node] = {
temperature: data.temperature,
humidity: data.humidity
};
if (!(node in nodePositions)) {
nodePositions[node] = { x: random(width), y: random(height) };
}
} catch (e) {
console.error("Invalid JSON:", line);
}
}
}
}
}
}
📌 What This Does:
- Buffers incoming chunks until a newline is found.
- Parses only complete lines, avoiding partial or garbled JSON.
- Improves reliability when receiving fast or inconsistent serial input.
Let me know if you'd also like to add a visual debug console inside the canvas to track what data is being parsed.
So I had a call with Nicola who was present in the FabLab about the issue with my code. We discussed the history of the code and what I could look into for the cause of the issue.
After that I tried different stuff. Than I asked ChatGPT again. (This week is more about chatGPT than my work ... And I am really surprised how well it works. I only used it to make my email more formal...)
ChatGPT prompt 9 - The original bug
I am using this code
/*
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp-mesh-esp32-esp8266-painlessmesh/
This is a simple example that uses the painlessMesh library: https://github.com/gmag11/painlessMesh/blob/master/examples/basic/basic.ino
*/
//BlueTooth MESH
#include "painlessMesh.h"
//JSON file creation & Reading
#include <Arduino_JSON.h>
//#include <ArduinoJson.h>
//RGB LED
#include <FastLED.h>
//LED SETUP
#define LED_PIN 1 //GPIO //D0 //or D9
#define NUM_LEDS 1
#define BRIGHTNESS 32
#define LED_TYPE WS2811
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];
#define UPDATES_PER_SECOND 100
CRGBPalette16 currentPalette;
TBlendType currentBlending;
extern CRGBPalette16 myRedWhiteBluePalette;
extern const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM;
int R;
int G;
int B;
//Button
#define Button_PIN1 43 //GPIO //D6
#define Button_PIN2 7 //GPIO //D8
//#define Button_PIN2 D6 //or D8
//the name of the MESH network
#define MESH_PREFIX "whateverYouLike"
//password of the MESH network
#define MESH_PASSWORD "somethingSneaky"
// This refers to the the TCP port that you want the mesh server to run on. The default is 5555
#define MESH_PORT 5555
//Number for this node
int nodeNumber = 2;
//Node ID to send a specific message
int Node_ID = 1;
//String to send to other nodes with sensor readings
String final_message;
//It is recommended to avoid using delay() in the mesh network code.
//To maintain the mesh, some tasks need to be performed in the background.
//Using delay() will stop these tasks from happening and can cause the mesh to lose stability/fall apart.
//The following line creates a new Scheduler called userScheduler
Scheduler userScheduler; // to control your personal task
//Create a painlessMesh object called mesh to handle the mesh network
painlessMesh mesh;
// User stub
void sendMessage() ; // Prototype so PlatformIO doesn't complain
String Message(); // Prototype for sending message
//Create a task called taskSendMessage responsible for calling the sendMessage() function every X (the number) second as long as the program is running.
Task taskSendMessage( TASK_SECOND * 1 , TASK_FOREVER, &sendMessage );
int temperatureValue = 1;
int humidityValue = 1;
String Message() {
JSONVar jsonReadings;
jsonReadings["node"] = nodeNumber;
jsonReadings["temperature"] = temperatureValue;
jsonReadings["humidity"] = humidityValue;
// Add more sensor readings as needed
final_message = JSON.stringify(jsonReadings);
return final_message;
}
//The sendMessage() function sends a message to all nodes in the message network (broadcast).
void sendMessage() {
String msg = Message();
//board chip ID
//msg += mesh.getNodeId();
//Replaced by nodeNumber
//To broadcast a message, simply use the sendBroadcast() method on the mesh object and pass as argument the message (msg) you want to send.
mesh.sendBroadcast(msg);
//Send to specific Node
//mesh.sendSingle(Node_ID, msg);
//Every time a new message is sent, the code changes the interval between messages (one to five seconds).
taskSendMessage.setInterval( random( TASK_SECOND * 1, TASK_SECOND * 5 ));
}
//several callback functions are created that will be called when specific events happen on the mesh.
//The receivedCallback() function prints the message sender (from) and the content of the message (msg.c_str()).
void receivedCallback( uint32_t from, String &msg ) {
//Serial.printf("Received from %u msg=%s\n", from, msg.c_str());
//The message comes in JSON format, so, we can access the variables as follows:
JSONVar myObject = JSON.parse(msg.c_str());
if (JSON.typeof(myObject) == "undefined") {
Serial.println("Parsing input failed!");
return;
}
int node = myObject["node"];
int temperature_in = myObject["temperature"];
int humidity_in = myObject["humidity"];
//Serial.print(M1_I);
}
//The newConnectionCallback() function runs whenever a new node joins the network.
//This function simply prints the chip ID of the new node.
//You can modify the function to do any other task.
void newConnectionCallback(uint32_t nodeId) {
Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);
}
//The changedConnectionCallback() function runs whenever a connection changes on the network (when a node joins or leaves the network).
void changedConnectionCallback() {
Serial.printf("Changed connections\n");
}
//The nodeTimeAdjustedCallback() function runs when the network adjusts the time, so that all nodes are synchronized.
//It prints the offset.
void nodeTimeAdjustedCallback(int32_t offset) {
Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
}
void setup() {
pinMode(Button_PIN1, INPUT);
pinMode(Button_PIN2, INPUT);
Serial.begin(115200);
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
FastLED.setBrightness( BRIGHTNESS );
currentPalette = RainbowColors_p;
currentBlending = LINEARBLEND;
mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
//mesh.setDebugMsgTypes( ERROR | STARTUP ); // set before init() so that you can see startup messages
//Initialize the mesh with the details defined earlier.
mesh.init( MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT );
//Assign all the callback functions to their corresponding events.
mesh.onReceive(&receivedCallback);
mesh.onNewConnection(&newConnectionCallback);
mesh.onChangedConnections(&changedConnectionCallback);
mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
//Finally, add the taskSendMessage function to the userScheduler.
//The scheduler is responsible for handling and running the tasks at the right time.
userScheduler.addTask( taskSendMessage );
//Finally, enable the taskSendMessage, so that the program starts sending the messages to the mesh.
taskSendMessage.enable();
//Upload Check
int time = 0;
R = 250;//random(0,255);
G = 0;//random(0,255);
B = 0;//random(0,255);
while (time <= 100){
for(int i = 0; i < NUM_LEDS; i++ ){
leds[i].setRGB(R,G,B); // Set Color HERE!!!
}
FastLED.show();
FastLED.delay(1000 / UPDATES_PER_SECOND);
time++;
}
R = 0;
G = 0;
B = 0;
for(int i = 0; i < NUM_LEDS; i++ ){
leds[i].setRGB(R,G,B); // Set Color HERE!!!
}
FastLED.show();
FastLED.delay(1000 / UPDATES_PER_SECOND);
}
void loop() {
if(digitalRead(Button_PIN1) == HIGH){
temperatureValue +=1 ;//random(0,100);
}
else if(digitalRead(Button_PIN2) == HIGH){
humidityValue +=1 ;//random(0,100);
}
sendMessage();
//To keep the mesh running, add mesh.update() to the loop().
// it will run the user scheduler as well
mesh.update();
//For debugging.
//Periodically check the available heap memory to detect potential memory leaks
//Serial.println(ESP.getFreeHeap());
}
but even if it is supposed to send only 1 message every second it send a lot of messages every millisecond do you have any idea why ?
And the solution was so stupid. I really feel dum. At least ChatGPT cannot repair 3D printers (for now...)
ChatGPT prompt 9 - The original bug - Answer
Yes, the issue lies in your loop()
function — you’re calling sendMessage()
directly on every loop iteration, in addition to using the taskSendMessage
scheduler.
This results in two competing mechanisms sending messages:
taskSendMessage
sends messages at random intervals between 1–5 seconds (as configured).- The direct call to
sendMessage()
inloop()
causes it to fire as fast as the loop runs (thousands of times per second).
✅ How to fix it
You should remove the direct call to sendMessage()
from the loop()
function. The taskSendMessage
is already handling scheduled sending correctly.
🔧 Replace this:
void loop() {
if(digitalRead(Button_PIN1) == HIGH){
temperatureValue +=1;
}
else if(digitalRead(Button_PIN2) == HIGH){
humidityValue +=1;
}
sendMessage(); // ❌ This is flooding your mesh
mesh.update();
}
✅ With this:
void loop() {
if(digitalRead(Button_PIN1) == HIGH){
temperatureValue +=1;
}
else if(digitalRead(Button_PIN2) == HIGH){
humidityValue +=1;
}
mesh.update(); // This will handle scheduled messages properly
}
Now your node will only send data according to the scheduler (taskSendMessage
), which fires once every 1–5 seconds, avoiding message flooding. Let me know if you’d like to adjust the timing more precisely!
And now everything work.
I have multiple data coming to a webpage.
Final p5.js Code
let port;
let reader;
let inputDone;
let inputStream;
let floorPlan;
let imgInput;
let nodeData = {};
let nodePositions = {
"1": { x: 100, y: 100 },
"2": { x: 300, y: 100 },
"3": { x: 100, y: 250 },
"4": { x: 300, y: 250 }
};
let draggingNode = null;
let dragOffset = { x: 0, y: 0 };
function setup() {
createCanvas(800, 600);
textSize(16);
textAlign(LEFT, CENTER);
const connectButton = createButton('Connect Serial');
connectButton.position(10, height + 10);
connectButton.mousePressed(connect);
imgInput = createFileInput(handleFile);
imgInput.position(150, height + 10);
}
function draw() {
background(255);
if (floorPlan) {
image(floorPlan, 0, 0, width, height);
} else {
fill(100);
text("Upload a floor plan image", 20, 20);
}
for (let node in nodeData) {
let pos = nodePositions[node];
let val = nodeData[node];
fill(0);
ellipse(pos.x, pos.y, 10, 10);
text(`Node ${node}`, pos.x + 10, pos.y);
text(`Temp: ${val.temperature}°C`, pos.x + 10, pos.y + 20);
text(`Hum: ${val.humidity}%`, pos.x + 10, pos.y + 40);
}
}
function handleFile(file) {
if (file.type === 'image') {
floorPlan = loadImage(file.data);
}
}
async function connect() {
try {
port = await navigator.serial.requestPort();
await port.open({ baudRate: 115200 });
const decoder = new TextDecoderStream();
inputDone = port.readable.pipeTo(decoder.writable);
inputStream = decoder.readable;
reader = inputStream.getReader();
readLoop();
} catch (err) {
console.error('Serial connection failed:', err);
}
}
let buffer = '';
async function readLoop() {
while (true) {
const { value, done } = await reader.read();
if (done) {
reader.releaseLock();
break;
}
if (value) {
buffer += value;
// Look for newlines or similar delimiters to separate complete messages
let lines = buffer.split('\n');
buffer = lines.pop(); // Save last (possibly incomplete) part for next time
for (let line of lines) {
line = line.trim();
if (line.length > 0) {
try {
const data = JSON.parse(line);
const node = data.node.toString();
nodeData[node] = {
temperature: data.temperature,
humidity: data.humidity
};
if (!(node in nodePositions)) {
nodePositions[node] = { x: random(width), y: random(height) };
}
} catch (e) {
console.error("Invalid JSON:", line);
}
}
}
}
}
}
function mousePressed() {
for (let node in nodePositions) {
let pos = nodePositions[node];
if (dist(mouseX, mouseY, pos.x, pos.y) < 10) {
draggingNode = node;
dragOffset.x = mouseX - pos.x;
dragOffset.y = mouseY - pos.y;
break;
}
}
}
function mouseDragged() {
if (draggingNode) {
nodePositions[draggingNode].x = mouseX - dragOffset.x;
nodePositions[draggingNode].y = mouseY - dragOffset.y;
}
}
function mouseReleased() {
draggingNode = null;
}
Final Measuring MESH Code
/*
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp-mesh-esp32-esp8266-painlessmesh/
This is a simple example that uses the painlessMesh library: https://github.com/gmag11/painlessMesh/blob/master/examples/basic/basic.ino
*/
//BlueTooth MESH
#include "painlessMesh.h"
//JSON file creation & Reading
#include <Arduino_JSON.h>
//#include <ArduinoJson.h>
//RGB LED
#include <FastLED.h>
//LED SETUP
#define LED_PIN 1 //GPIO //D0 //or D9
#define NUM_LEDS 1
#define BRIGHTNESS 32
#define LED_TYPE WS2811
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];
#define UPDATES_PER_SECOND 100
CRGBPalette16 currentPalette;
TBlendType currentBlending;
extern CRGBPalette16 myRedWhiteBluePalette;
extern const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM;
int R;
int G;
int B;
//Button
#define Button_PIN1 43 //GPIO //D6
#define Button_PIN2 7 //GPIO //D8
//#define Button_PIN2 D6 //or D8
//the name of the MESH network
#define MESH_PREFIX "whateverYouLike"
//password of the MESH network
#define MESH_PASSWORD "somethingSneaky"
// This refers to the the TCP port that you want the mesh server to run on. The default is 5555
#define MESH_PORT 5555
//Number for this node
int nodeNumber = 3;
//Node ID to send a specific message
int Node_ID = 1;
//String to send to other nodes with sensor readings
String final_message;
//It is recommended to avoid using delay() in the mesh network code.
//To maintain the mesh, some tasks need to be performed in the background.
//Using delay() will stop these tasks from happening and can cause the mesh to lose stability/fall apart.
//The following line creates a new Scheduler called userScheduler
Scheduler userScheduler; // to control your personal task
//Create a painlessMesh object called mesh to handle the mesh network
painlessMesh mesh;
// User stub
void sendMessage() ; // Prototype so PlatformIO doesn't complain
String Message(); // Prototype for sending message
//Create a task called taskSendMessage responsible for calling the sendMessage() function every X (the number) second as long as the program is running.
Task taskSendMessage( TASK_SECOND * 10 , TASK_FOREVER, &sendMessage );
int temperatureValue = 1;
int humidityValue = 1;
String Message() {
JSONVar jsonReadings;
jsonReadings["node"] = nodeNumber;
jsonReadings["temperature"] = temperatureValue;
jsonReadings["humidity"] = humidityValue;
// Add more sensor readings as needed
final_message = JSON.stringify(jsonReadings);
return final_message;
}
//The sendMessage() function sends a message to all nodes in the message network (broadcast).
void sendMessage() {
String msg = Message();
//board chip ID
//msg += mesh.getNodeId();
//Replaced by nodeNumber
//To broadcast a message, simply use the sendBroadcast() method on the mesh object and pass as argument the message (msg) you want to send.
mesh.sendBroadcast(msg);
//Send to specific Node
//mesh.sendSingle(Node_ID, msg);
//Every time a new message is sent, the code changes the interval between messages (one to five seconds).
//taskSendMessage.setInterval( random( TASK_SECOND * 1, TASK_SECOND * 5 ));
}
//several callback functions are created that will be called when specific events happen on the mesh.
//The receivedCallback() function prints the message sender (from) and the content of the message (msg.c_str()).
void receivedCallback( uint32_t from, String &msg ) {
//Serial.printf("Received from %u msg=%s\n", from, msg.c_str());
//The message comes in JSON format, so, we can access the variables as follows:
JSONVar myObject = JSON.parse(msg.c_str());
if (JSON.typeof(myObject) == "undefined") {
Serial.println("Parsing input failed!");
return;
}
int node = myObject["node"];
int temperature_in = myObject["temperature"];
int humidity_in = myObject["humidity"];
//Serial.print(M1_I);
}
//The newConnectionCallback() function runs whenever a new node joins the network.
//This function simply prints the chip ID of the new node.
//You can modify the function to do any other task.
void newConnectionCallback(uint32_t nodeId) {
Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);
}
//The changedConnectionCallback() function runs whenever a connection changes on the network (when a node joins or leaves the network).
void changedConnectionCallback() {
Serial.printf("Changed connections\n");
}
//The nodeTimeAdjustedCallback() function runs when the network adjusts the time, so that all nodes are synchronized.
//It prints the offset.
void nodeTimeAdjustedCallback(int32_t offset) {
Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
}
void setup() {
pinMode(Button_PIN1, INPUT);
pinMode(Button_PIN2, INPUT);
Serial.begin(115200);
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
FastLED.setBrightness( BRIGHTNESS );
currentPalette = RainbowColors_p;
currentBlending = LINEARBLEND;
mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
//mesh.setDebugMsgTypes( ERROR | STARTUP ); // set before init() so that you can see startup messages
//Initialize the mesh with the details defined earlier.
mesh.init( MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT );
//Assign all the callback functions to their corresponding events.
mesh.onReceive(&receivedCallback);
mesh.onNewConnection(&newConnectionCallback);
mesh.onChangedConnections(&changedConnectionCallback);
mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
//Finally, add the taskSendMessage function to the userScheduler.
//The scheduler is responsible for handling and running the tasks at the right time.
userScheduler.addTask( taskSendMessage );
//Finally, enable the taskSendMessage, so that the program starts sending the messages to the mesh.
taskSendMessage.enable();
//Upload Check
int time = 0;
R = 250;//random(0,255);
G = 0;//random(0,255);
B = 0;//random(0,255);
while (time <= 100){
for(int i = 0; i < NUM_LEDS; i++ ){
leds[i].setRGB(R,G,B); // Set Color HERE!!!
}
FastLED.show();
FastLED.delay(1000 / UPDATES_PER_SECOND);
time++;
}
R = 0;
G = 0;
B = 0;
for(int i = 0; i < NUM_LEDS; i++ ){
leds[i].setRGB(R,G,B); // Set Color HERE!!!
}
FastLED.show();
FastLED.delay(1000 / UPDATES_PER_SECOND);
}
void loop() {
if(digitalRead(Button_PIN1) == HIGH){
temperatureValue +=1 ;//random(0,100);
}
else if(digitalRead(Button_PIN2) == HIGH){
humidityValue +=1 ;//random(0,100);
}
//sendMessage();
//To keep the mesh running, add mesh.update() to the loop().
// it will run the user scheduler as well
mesh.update();
//For debugging.
//Periodically check the available heap memory to detect potential memory leaks
//Serial.println(ESP.getFreeHeap());
}
Final Gateway MESH Code
So from ESP to the WebSite
/*
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp-mesh-esp32-esp8266-painlessmesh/
This is a simple example that uses the painlessMesh library: https://github.com/gmag11/painlessMesh/blob/master/examples/basic/basic.ino
*/
//BlueTooth MESH
#include "painlessMesh.h"
//JSON file creation & Reading
#include <Arduino_JSON.h>
//#include <ArduinoJson.h>
//RGB LED
#include <FastLED.h>
//LED SETUP
#define LED_PIN D0 //or D9
#define NUM_LEDS 1
#define BRIGHTNESS 32
#define LED_TYPE WS2811
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];
#define UPDATES_PER_SECOND 100
CRGBPalette16 currentPalette;
TBlendType currentBlending;
extern CRGBPalette16 myRedWhiteBluePalette;
extern const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM;
int R;
int G;
int B;
//Button
#define Button_PIN1 D6 //or D8
#define Button_PIN2 D8
//#define Button_PIN2 D6 //or D8
//the name of the MESH network
#define MESH_PREFIX "whateverYouLike"
//password of the MESH network
#define MESH_PASSWORD "somethingSneaky"
// This refers to the the TCP port that you want the mesh server to run on. The default is 5555
#define MESH_PORT 5555
//Number for this node
int nodeNumber = 1;
//Node ID to send a specific message
int Node_ID = 1;
//String to send to other nodes with sensor readings
String final_message;
//It is recommended to avoid using delay() in the mesh network code.
//To maintain the mesh, some tasks need to be performed in the background.
//Using delay() will stop these tasks from happening and can cause the mesh to lose stability/fall apart.
//The following line creates a new Scheduler called userScheduler
Scheduler userScheduler; // to control your personal task
//Create a painlessMesh object called mesh to handle the mesh network
painlessMesh mesh;
// User stub
void sendMessage() ; // Prototype so PlatformIO doesn't complain
String Message(); // Prototype for sending message
//Create a task called taskSendMessage responsible for calling the sendMessage() function every X (the number) second as long as the program is running.
Task taskSendMessage( TASK_SECOND * 5 , TASK_FOREVER, &sendMessage );
int temperatureValue;
int humidityValue;
String Message() {
JSONVar jsonReadings;
jsonReadings["node"] = nodeNumber;
jsonReadings["temperature"] = temperatureValue;
jsonReadings["humidity"] = humidityValue;
// Add more sensor readings as needed
final_message = JSON.stringify(jsonReadings);
return final_message;
}
//The sendMessage() function sends a message to all nodes in the message network (broadcast).
void sendMessage() {
String msg = Message();
//board chip ID
//msg += mesh.getNodeId();
//Replaced by nodeNumber
//To broadcast a message, simply use the sendBroadcast() method on the mesh object and pass as argument the message (msg) you want to send.
mesh.sendBroadcast(msg);
//Send to specific Node
//mesh.sendSingle(Node_ID, msg);
//Every time a new message is sent, the code changes the interval between messages (one to five seconds).
taskSendMessage.setInterval( random( TASK_SECOND * 1, TASK_SECOND * 5 ));
}
//several callback functions are created that will be called when specific events happen on the mesh.
//The receivedCallback() function prints the message sender (from) and the content of the message (msg.c_str()).
void receivedCallback(uint32_t from, String &msg) {
JSONVar myObject = JSON.parse(msg.c_str());
if (JSON.typeof(myObject) == "undefined") {
Serial.println("Parsing input failed!");
return;
}
Serial.println(msg); // Forward the received message to the serial port
}
//The newConnectionCallback() function runs whenever a new node joins the network.
//This function simply prints the chip ID of the new node.
//You can modify the function to do any other task.
void newConnectionCallback(uint32_t nodeId) {
Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);
}
//The changedConnectionCallback() function runs whenever a connection changes on the network (when a node joins or leaves the network).
void changedConnectionCallback() {
Serial.printf("Changed connections\n");
}
//The nodeTimeAdjustedCallback() function runs when the network adjusts the time, so that all nodes are synchronized.
//It prints the offset.
void nodeTimeAdjustedCallback(int32_t offset) {
Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
}
void setup() {
Serial.begin(115200);
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
FastLED.setBrightness( BRIGHTNESS );
currentPalette = RainbowColors_p;
currentBlending = LINEARBLEND;
//mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
mesh.setDebugMsgTypes( ERROR | STARTUP ); // set before init() so that you can see startup messages
//Initialize the mesh with the details defined earlier.
mesh.init( MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT );
//Assign all the callback functions to their corresponding events.
mesh.onReceive(&receivedCallback);
mesh.onNewConnection(&newConnectionCallback);
mesh.onChangedConnections(&changedConnectionCallback);
mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
//Finally, add the taskSendMessage function to the userScheduler.
//The scheduler is responsible for handling and running the tasks at the right time.
userScheduler.addTask( taskSendMessage );
//Finally, enable the taskSendMessage, so that the program starts sending the messages to the mesh.
taskSendMessage.enable();
//Upload Check
int time = 0;
R = random(0,255);
G = random(0,255);
B = random(0,255);
while (time <= 500){
for(int i = 0; i < NUM_LEDS; i++ ){
leds[i].setRGB(R,G,B); // Set Color HERE!!!
}
FastLED.show();
FastLED.delay(1000 / UPDATES_PER_SECOND);
time++;
}
R = 0;
G = 0;
B = 0;
for(int i = 0; i < NUM_LEDS; i++ ){
leds[i].setRGB(R,G,B); // Set Color HERE!!!
}
FastLED.show();
FastLED.delay(1000 / UPDATES_PER_SECOND);
}
void loop() {
//sendMessage();
//To keep the mesh running, add mesh.update() to the loop().
// it will run the user scheduler as well
mesh.update();
}
Now I just need to hook up the real sensors.
Learning Outcome¶
I think I did more debugging than actual learning. But from what I managed to do with p5.js I feel like it is as difficult / easy as with Arduino. Easy when it works. Hard when it does not.
Assignment Check¶
- individual assignment:
- write an application that interfaces a user with an input &/or output device that you made
- Done
- write an application that interfaces a user with an input &/or output device that you made
- group assignment:
- compare as many tool options as possible