Hi @Bellatore69, I’ve used ESP8266 on a few small projects, never ESP32, but I think in respect of communicating they are the same. If that’s wrong, someone please correct me.
I use UDP messages. (Processing, library manager, UDP). One of life’s challenges is going to a code club with an ESP8266 (running a fan speed control) and a Pi with an application (web-server written in js, but Processing can do the same comms) You don’t know what IP addresses the devices will get, so I make the ESP do a UDP broadcast a message each minute. The message has the source IP and contains the name of the program running in the ESP. The Pi receives broadcast messages and looks for that name. Once found, it can send frequent UDP requests to the ESP get response.
I’m away from home at the moment, without ESP and latest versions of programs, but what follows is:
ESP_Fan_Speed.ino - ESP code. If you load it and go to it’s web-page you’ll recognise it as starting from one of the given examples. +Extra buttons, +OTA, +UDP messages. You’ll have to hard code your WiFi details on line 86.
UDP_Scan.js - the relevant parts of my js server to see the recognition of ESP program name and subsequent enquiry.
ESP_Control_UDP.pde - Hard-coded ESP’s IP address on line 45. Run, click on window, press ‘A’ and it should enquire to ESP and get reply.
The part I don’t have with me is the Processing code to get the IP address from the broadcast mesage., but once you have some messages working it’s obvious.
Hope this makes sense and helps,
Richard.
ESP_Fan_Speed.ino
// Import required libraries
// For WiFi and OTA
#include <Arduino.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <AsyncElegantOTA.h>
#include <WiFiUdp.h>
// For timer interrupts
#include <Ticker.h>
#define pM pinMode
#define dR digitalRead
#define aW analogWrite
#define dW digitalWrite
#define rcv_pkt_size 100 // examples had another value, WMini crashed with that
const boolean TT = true;
const boolean FF = false;
float DTTemp0;
float DTTemp1;
// Network credentials
const char* ssid1 = "X"; const char* password1 = "xxx";
const char* ssid0 = "Y"; const char* password0 = "yyy";
WiFiUDP Udp;
unsigned int localUdpPort = 4210; // local port to listen on
char incomingPacket[255]; // buffer for incoming packets
char replyPacket[] = "Hi there! received your message :-)"; // a reply string to send back
int secs = 0; // 0..59
int mins = 0; // 0..59
int hors = 0; // 0..23
int days = 0;
bool ledState = 0;
const int fanOn__Pin = D5; // fan on-off
const int fanSL__Pin = D6;
const int fanPV__Pin = D4; // Pulse input
const int ledRd1_Pin = D1; // Red Led - separate (green broken)
const int ledGr__Pin = D2; // Green LED - red/green
const int ledRd2_Pin = D3; // Red LED - red/green
// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
<title>ESP Fan Speed Control</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<style>
html {font-family: Arial, Helvetica, sans-serif;text-align: left;}
h1 {font-size: 1.8rem; color: white;}
h2{font-size: 1.5rem; font-weight: bold; color: #143642;}
.topnav {overflow: hidden; background-color: #143642;}
body {margin: 0;}
.content {padding: 30px; max-width: 600px; margin: 0 auto;}
.card {
background-color: #F8F7F9;;
box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
padding-top:10px;
padding-bottom:20px;
}
.button {
padding: 12px 12px;
font-size: 24px;
text-align: center;
outline: none;
color: #fff;
background-color: #0f8b8d;
border: none;
border-radius: 5px;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
/*.button:hover {background-color: #0f8b8d}*/
.button:active {
background-color: #0f8b8d;
box-shadow: 2 2px #CDCDCD;
transform: translateY(2px);
}
.state {
padding-right: 35px;
font-size: 2.0rem;
color:#4c4c4c;
font-weight: bold;
}
.r1 {
margin: 0;
padding-top: 0px;
padding-bottom: 0px;
font-size: 1.5rem;
color:#50D010;
}
</style>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
</head>
<body>
<div class="topnav">
<h1>ESP8266 Fan Speed Control</h1>
</div>
<div class="content">
<div class="card">
<p><button id="button" class="button">Toggle Red LED.</button> <span id="stat1" class="state">%STATE%</span></p>
<p><button id="buttn2" class="button">Fan Off</button>
<button id="buttn3" class="button">Fan On</button>
<span id="FN" class="state">%FanOn%</span></p>
<p>
<button id="ButFS1" class="button">Fan Speed 1</button>
<button id="ButFS2" class="button">2</button>
<button id="ButFS3" class="button">3</button>
<button id="ButFS4" class="button">4</button>
<button id="ButFS5" class="button">5</button>
<span id="FSL" class="state">%FanSL%</span>
<span id="FSPV" class="state">%FanPV%</span>rev/min
</p>
<p class="r1"><span>ESP Count: </span> <span id="EC">EC</span></p>
<p>More info and source code at:</p>
<p>sites.google.com/view/processing-and-arduino-notes.</p>
<p>That's a reference, but not a link, because the link made an error.</p>
<p>You need internet, not ESP WiFi.</p>
<p>On that page, near the end, see 'ESP8266 Fan Control'.</p>
<br>
<p>%file%</p>
</div>
</div>
<script>
var gateway = `ws://${window.location.hostname}/ws`;
var websocket;
window.addEventListener('load', onLoad);
function initWebSocket() {
console.log('G ' + gateway);
console.log('Trying to open a WebSocket connection...');
websocket = new WebSocket(gateway);
websocket.onopen = onOpen;
websocket.onclose = onClose;
websocket.onmessage = onMessage; // <-- add this line
}
function onOpen(event) {console.log('Connection opened');}
function onClose(event) {console.log('Connection closed'); setTimeout(initWebSocket, 2000);}
function onMessage(event) {
var state;
var ary;
var id;
var colour;
//console.log(event.data);
if (event.data == "1") {state = "ON" ;
document.getElementById('stat1').innerHTML = state;
}
if (event.data == "0") {state = "OFF";
document.getElementById('stat1').innerHTML = state;
}
ary = event.data.split(",");
id = 'EC'; if (ary[0] == id) {document.getElementById(id).innerHTML = ary[1];}
id = 'T0'; if (ary[0] == id) {document.getElementById(id).innerHTML = ary[1];}
id = 'T1'; if (ary[0] == id) {document.getElementById(id).innerHTML = ary[1];}
id = 'FN'; if (ary[0] == id) {document.getElementById(id).innerHTML = ary[1];}
id = 'FSL'; if (ary[0] == id) {document.getElementById(id).innerHTML = ary[1];}
id = 'FSPV'; if (ary[0] == id) {document.getElementById(id).innerHTML = ary[1];}
}
function onLoad(event) {
initWebSocket();
initButton();
}
function initButton() {
document.getElementById('button').addEventListener('click', toggle);
document.getElementById('buttn3').addEventListener('click', On_);
document.getElementById('buttn2').addEventListener('click', Off);
document.getElementById('ButFS1').addEventListener('click', ButFS1);
document.getElementById('ButFS2').addEventListener('click', ButFS2);
document.getElementById('ButFS3').addEventListener('click', ButFS3);
document.getElementById('ButFS4').addEventListener('click', ButFS4);
document.getElementById('ButFS5').addEventListener('click', ButFS5);
}
function toggle(){websocket.send('toggle');}
function On_(){websocket.send('On_');}
function Off(){websocket.send('Off');}
function ButFS1(){websocket.send('FS1');}
function ButFS2(){websocket.send('FS2');}
function ButFS3(){websocket.send('FS3');}
function ButFS4(){websocket.send('FS4');}
function ButFS5(){websocket.send('FS5');}
</script>
</body>
</html>)rawliteral";
// these relic for smaller fan on other project?
int fanSenseState0 = 0;
int fanSenseState1 = 0; // 0 = normal, 1 = stuck
Ticker blinker;
// SWiF2-80P Fan (Yellow)
int fanOn = 0; // User on/off
int fan_state; // 0/1 as fan goes round
int led_state;
int fan_SL;
int fan_count;
int fan_speed;
ICACHE_RAM_ATTR void fan_ISR() {
// 2 per rev
fan_state = (~fan_state) & 1;
dW(ledGr__Pin, fan_state);
//if (fan_state) {fan_count++;} // 1 per rev
fan_count++;
}
ICACHE_RAM_ATTR void timer_ISR() {
// each 2 seconds
led_state = (~led_state) & 1;
//dW(ledRd1_Pin, led_state);
fan_speed = fan_count * 15;
Serial.print("Fan Count: ");
Serial.print(fan_count);
Serial.print(" /2Sec "); // half revs per 2 sec.
Serial.print(fan_speed);
Serial.print(" R/min");
Serial.println();
ws.textAll("FSPV," + String(fan_speed)); // R/Min
fan_count = 0;
}
void notifyClients() {
ws.textAll(String(ledState));
}
void handleWebSocketMessage(void *arg, uint8_t *data, size_t len) {
AwsFrameInfo *info = (AwsFrameInfo*)arg;
if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
data[len] = 0;
if (strcmp((char*)data, "toggle") == 0) {
ledState = !ledState;
notifyClients();
}
if (strcmp((char*)data, "Off") == 0) {
fanOn = 0;
dW(fanOn__Pin, 0);
ws.textAll("FN,0");
}
if (strcmp((char*)data, "On_") == 0) {
fanOn = 1;
dW(fanOn__Pin, 1);
ws.textAll("FN,1");
}
if (strcmp((char*)data, "FS1") == 0) {
aW(fanSL__Pin, 1);
fan_SL = 1;
ws.textAll("FSL,1");
}
if (strcmp((char*)data, "FS2") == 0) {
aW(fanSL__Pin, 430);
fan_SL = 2;
ws.textAll("FSL,2");
}
if (strcmp((char*)data, "FS3") == 0) {
aW(fanSL__Pin, 590);
fan_SL = 3;
ws.textAll("FSL,3");
}
if (strcmp((char*)data, "FS4") == 0) {
aW(fanSL__Pin, 680);
fan_SL = 4;
ws.textAll("FSL,4");
}
if (strcmp((char*)data, "FS5") == 0) {
aW(fanSL__Pin, 1023);
fan_SL = 5;
ws.textAll("FSL,5");
}
}
}
void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type,
void *arg, uint8_t *data, size_t len) {
switch (type) {
case WS_EVT_CONNECT:
Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
break;
case WS_EVT_DISCONNECT:
Serial.printf("WebSocket client #%u disconnected\n", client->id());
break;
case WS_EVT_DATA:
handleWebSocketMessage(arg, data, len);
break;
case WS_EVT_PONG:
case WS_EVT_ERROR:
break;
}
}
void initWebSocket()
{
ws.onEvent(onEvent);
server.addHandler(&ws);
}
String processor(const String& var)
{
Serial.println(var);
if (var == "STATE")
{
if (ledState) {
return "ON" ;
}
else {
return "OFF";
}
}
if (var == "FanOn") {
return String(fanOn);
}
if (var == "FanPV") {
return String(fan_speed);
}
if (var == "FanSL") {
return String(fan_SL);
}
if (var == "file") return String(__FILE__ __DATE__ __TIME__);
return String();
}
void set_udp_msg(char msg[]) {
// going to assume the msg we've been given is long enough
char fMsg[80];
int ix;
sprintf(fMsg, "%s", __FILE__);
ix = 0;
while (1) {
if (fMsg[ix] == '.') {
fMsg[ix] = 0; break;
}
ix++;
}
sprintf(msg, "%s,%d,%d,%d,%d,%d,%d", fMsg, hors, mins, secs, fanOn, fan_SL, fan_speed);
}
void UDP_Brd() {
// UDP Broadcast
char msg[120]; // crude guess at length
IPAddress ipa(0, 0, 0, 0);
ipa[0] = WiFi.localIP()[0];
ipa[1] = WiFi.localIP()[1];
ipa[2] = WiFi.localIP()[2];
ipa[3] = 255;
set_udp_msg(msg);
Udp.beginPacket(ipa, 8123); // Port??
Udp.write(msg);
Udp.endPacket();
}
int udpCount = 0; // count udp receive
void doUDP() {
// If there's any UDP message, receive and send response
char packetBuffer[rcv_pkt_size];
char msg[10 * 12]; // crude assume bytes * values
char cmdCh;
int packetSize = Udp.parsePacket();
if (packetSize)
{
udpCount++;
IPAddress remote = Udp.remoteIP();
// Print...
Serial.print(" Rcv size: ");
Serial.print(packetSize);
Serial.print("From: ");
for (int i = 0; i < 4; i++)
{
Serial.print(remote[i], DEC);
if (i < 3) {
Serial.print(".");
}
}
Serial.print(", port ");
Serial.print(Udp.remotePort());
// read the packet into packetBufffer
Udp.read(packetBuffer, rcv_pkt_size);
Serial.print("Contents: ~");
Serial.print(packetBuffer);
Serial.print("~");
cmdCh = packetBuffer[0];
if (cmdCh == 'A') {
// send a reply, to the IP address and port that sent us the packet we received
set_udp_msg(msg);
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(msg);
Udp.endPacket();
}
}
}
void print_addr()
{
// Print the IP address to serial
Serial.printf("Listening on IP %s, UDP port %d. ", WiFi.localIP().toString().c_str(), localUdpPort);
Serial.println();
}
// State values.
int actCont; // counter controlling request and read of BME280
/*
We don't know if int is 16 or 31 bit. Board dependent.
unsigned 16 bit max is 65535
if seconds then 1092 minutes
18 hours
millis() is unsiged long, overflow is 50 days
*/
void setup() {
bool status;
int tries;
delay(2000);
Serial.begin(9600);
delay(500);
pM(fanOn__Pin , OUTPUT);
pM(fanSL__Pin , OUTPUT);
pM(ledRd1_Pin , OUTPUT);
pM(ledGr__Pin , OUTPUT);
pM(ledRd2_Pin , OUTPUT);
pM(fanPV__Pin , INPUT_PULLUP);
WiFi.mode(WIFI_AP_STA);
// Connect to Wi-Fi - try 2 nets in turn
if (WiFi.status() != WL_CONNECTED) {
WiFi.begin(ssid0, password0, 10);
tries = 0;
while (WiFi.status() != WL_CONNECTED)
{
delay(1000);
Serial.print("Connecting to WiFi...~");
Serial.print(" ");
Serial.print(ssid0);
Serial.print(" ");
Serial.print(tries);
Serial.println();
tries++;
if (tries > 5) {
break;
}
}
}
if (WiFi.status() != WL_CONNECTED) {
WiFi.begin(ssid1, password1);
tries = 0;
while (WiFi.status() != WL_CONNECTED)
{
delay(1000);
Serial.print("Connecting to WiFi...");
Serial.print(" ");
Serial.print(ssid1);
Serial.print(" ");
Serial.print(tries);
Serial.println();
tries++;
if (tries > 5) {
break;
}
}
}
// Print ESP Local IP Address
Serial.println(WiFi.localIP());
initWebSocket();
// Route for root / web page
server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
request->send_P(200, "text/html", index_html, processor);
});
// Start ElegantOTA
AsyncElegantOTA.begin(&server);
// Start server
server.begin();
Udp.begin(localUdpPort);
// Set the interrupt for fan and timer
blinker.attach_ms(2000, timer_ISR);
//attachInterrupt(digitalPinToInterrupt(D4), fan_ISR, RISING);
attachInterrupt(D4, fan_ISR, RISING);
delay(4000); // what for?
}
long lopCount = 0;
int sec0 = 0;
int sec1;
int sw1Stat0 = -2;
int sw1Stat1;
int sw2Stat0 = -2;
int sw2Stat1;
int serCount;
void loop()
{
int port_ix;
int new_in;
int in_val_ix;
char timeMsg[50];
char uMsg[50];
char fMsg[50];
int ix;
// Calculate dd hh:mm:ss
sec1 = millis() / 1000;
if (sec1 != sec0) {
secs++;
if (secs >= 60) {
mins++;
if (mins >= 60) {
hors++;
if (hors >= 24) {
days++;
hors = 0;
}
mins = 0;
}
secs = 0;
}
sec0 = sec1;
}
sprintf(timeMsg, "%02d:%02d:%02d", hors, mins, secs);
if (lopCount % 100 == 0)
{
// each second...
if (actCont % 2 == 0) {
ws.textAll("EC," + String(actCont));
ws.textAll("T0," + String(actCont % 57));
ws.textAll("T1," + String(actCont % 31));
}
if (actCont % 20 == 0)
{
Serial.printf("%s %s %s\n", __FILE__, __DATE__, __TIME__);
print_addr();
}
if (actCont % 15 == 0) {
UDP_Brd();
}
actCont++;
}
if (lopCount % 100 == 0 && actCont % 2 == 0)
{
Serial.print(lopCount);
Serial.print(" ");
if (actCont % (2 * 3) == 2 * 3 - 1) {
Serial.println();
}
if (actCont % (2 * 30) == 0)
{
Serial.println();
Serial.print(__FILE__);
Serial.print(" ");
Serial.print(__DATE__);
Serial.print(" ");
Serial.print(__TIME__);
Serial.println();
}
}
if (lopCount % 20 == 0)
{
// copy the web-driven state to the led
dW(ledRd1_Pin, ledState);
ws.cleanupClients();
}
if (lopCount % 10 == 0) {
// Any udp action required?
doUDP();
}
//fanSenseState1 = dR(SpedPin);
if (fanSenseState1 != fanSenseState0) {
ws.textAll("F1," + String(fanSenseState1));
serCount++;
if (serCount > 80) {
serCount = 0;
Serial.println();
}
fanSenseState0 = fanSenseState1;
}
int period = 600;
if (lopCount % (period / 2) == 0) {
}
// Copy speed pulse sense pin to led.
//dW(ledGr__Pin, dR(fanPV__Pin));
lopCount++;
delay(10);
}
UDP_Scan.js
// This is not a working program. It's an extract of
// the code lines that exchanges UDP messages with the
// ESP8255 running Fan_Speed_Control.
const dgram = require('dgram');
const srvr_udp = dgram.createSocket('udp4');
var iESPTimeout = 0;
var ESP_IPAddr;
var ESP_Port;
srvr_udp.bind(8123);
srvr_udp.on('message', (msg, rinfo) => {
var ix;
var sMsg;
var vals;
var len1;
len1 = msg.length;
if (1)
{
console.log(procs.getDateTime(1)
+ ` Rcv: from ${rinfo.address}:${rinfo.port} ${msg}`);
}
sMsg = msg.toString();
//console.log(sMsg);
vals = sMsg.split(',');
if (vals[0] == "ESP_Fan_Speed") {
if (iESPTimeout == 0) {
// Store the address to send request
ESP_IPAddr = rinfo.address;
ESP_Port = rinfo.port
}
//console.log("Fan speed: " + vals[5] + ' rpm.') ;
FanOn = vals[4];
FanSet = vals[5];
FanSpeed = vals[6];
iESPTimeout = 20;
}
});
function udp_req() { // Send request to ESP for data.
var msg;
msg = 'A';
srvr_udp.send(msg, 0, msg.length, ESP_Port, ESP_IPAddr, function (err, bytes) {});
}
if (iUpTime % 2 == 0 && iESPTimeout) {udp_req();}
ESP_Control_UDP.pde
/**
* (./) udp.pde - how to use UDP library as unicast connection
* (cc) 2006, Cousot stephane for The Atelier Hypermedia
* (->) http://hypermedia.loeil.org/processing/
*
* Create a communication between Processing<->Pure Data @ http://puredata.info/
* This program also requires to run a small program on Pd to exchange data
* (hum!!! for a complete experimentation), you can find the related Pd patch
* at http://hypermedia.loeil.org/processing/udp.pd
*
* -- note that all Pd input/output messages are completed with the characters
* ";\n". Don't refer to this notation for a normal use. --
03/02/2021 22:24:46
- trying to get this to talk ESP8266 but it's not receiving owt.
- think that it's ESP's problem.
- Have to get something else to prove it.
06/02/2021 20:11:34
- this is working, but talking to ESP_UDP_Server.
- we have 10 simulated values, sent each time we request, and plotting on GUI.
*/
// import UDP library
import hypermedia.net.*;
final boolean T = true;
final boolean F = false;
UDP udp; // define the UDP object
int iDisplayIx = 0;
int iDisplayLines = 20;
boolean bNewData = F;
String sRcvMsg;
void send_msg()
{
String message = str( key ); // the message to send
String ip = "192.168.1.8"; // the remote IP address
int port = 4210; // the destination port
// formats the message for Pd
//message = message + ";"; //\n";
//print(frameCount); print(" Send ");
//send the message
udp.send(message, ip, port);
}
long count;
void setup()
{
size(1100, 500);
frameRate(20);
// create a new datagram connection on port 6000
// and wait for incomming message
udp = new UDP( this, 8889 );
//udp.log( true ); // <-- printout the connection activity
udp.listen( true );
background(0);
count = 0;
}
//process events
void draw()
{
int iValues[];
int data_ix;
if (count % 20 == 0)
{
send_msg();
}
if (bNewData)
{
//print(iDisplayIx); print(" " );
if (iDisplayIx == 0)
{
//background(0);
fill(0);
stroke(0);
rect(0, 0, 460, 420);
}
fill(255, 255, 0);
textSize(20);
text(sRcvMsg, 10, 30 + iDisplayIx * 20);
iDisplayIx++;
iDisplayIx %= iDisplayLines;
iValues = int(split(sRcvMsg, ","));
//printArray(iValues);
//println();
bNewData = F;
stroke(10);
line(500 + (frameCount / 20) % 600, height - 10, 500 + (frameCount / 20) % 600, height - 10 - 1000 * 4 / 10);
pushStyle();
colorMode(HSB);
//stroke(255, 255, 0);
for (data_ix = 0; data_ix < iValues.length - 1; data_ix++ )
{
stroke((data_ix + 1) * 255 / iValues.length, 255, 255);
point(500 + (frameCount / 20) % 600, height - 10 - iValues[data_ix] * 4 / 10);
}
popStyle();
}
count++;
}
/**
* on key pressed event:
* send the current key value over the network
*/
void keyPressed()
{
send_msg();
}
/**
* To perform any action on datagram reception, you need to implement this
* handler in your code. This method will be automatically called by the UDP
* object each time he receive a nonnull message.
* By default, this method have just one argument (the received message as
* byte[] array), but in addition, two arguments (representing in order the
* sender IP address and his port) can be set like below.
*/
// void receive( byte[] data ) { // <-- default handler
void receive( byte[] data, String ip, int port ) // <-- extended handler
{
// get the "real" message =
// forget the ";\n" at the end <-- !!! only for a communication with Pd !!!
//data = subset(data, 0, data.length-2);
sRcvMsg = new String(data);
// print the result
println( "receive: \""+sRcvMsg+"\" from "+ip+" on port "+port );
bNewData = T;
}