Aide pour développer App téléphone <-> Arduino <-> Leds

Bonjour.
J'ai pour projet de faire un système d'aide au rangement par casier grâce a des leds.
Imaginez une liste d'objets, chacun rangé dans un casier différent. La liste des objets est gérée sur le téléphone et, lorsque je sélectionne l'objet dans la liste, sons casier s'éclaire grâce à une led instantanément.
La partie électronique et code est faite, il ne manque qu'à pouvoir changer les valeurs de "coordonnée" dans l'arduino lorsque je clique sur le nom de l'objet dans l'app.
En résumé, j'ai besoin d'aide pour :

  • établir une liaison wifi entre arduino uno wifi et mon tel
  • créer une appli "visuelle" me permettant d'enregistrer les objets et leur coordonnées.
  • pouvoir modifier indirectement le code dans l'arduino lorsque je clique sur un objet dans l'app.
    Je pense que le travail est colossal mais on ne sait jamais... ^^

Voici le code pour un exemple de tableau à 16 cases (4x4) et, pour l'exemple, 4 leds en diagonale allumées avec des couleurs différentes :

#include <Adafruit_NeoPixel.h>

Adafruit_NeoPixel leds(16, 6, NEO_GRB + NEO_KHZ800);

void setup() {
  leds.begin();
  leds.show();
}

void loop() {
  leds.setPixelColor(0, leds.Color(0,255,0));
  leds.setPixelColor(5, leds.Color(255,0,0));
  leds.setPixelColor(10, leds.Color(0,0,255));
  leds.setPixelColor(15, leds.Color(0,0,255));
  leds.show();
}

Je précise que j'ai quelques projets à mon compteur mais reste dans la case débutant. Et je n'ai aucune notion de programmation d'appli :frowning:
Merci d'avance

Bonjour galien

Super projet :wink:

Pourquoi pas en Bluetooth?

Au départ, il faudrait déjà, pour démarrer, par commander tes LED par des commandes "locales" introduites depuis la ligne de commande de l'IDE en créant des commandes comme:
LED0502
Qui allume la LED 5 à la couleur 2.
Ces commandes seraient reprises par l'application sur smartphone.

Si tu veux un exemple ...

A+
Cordialement
jpbbricole

Bonjour jpbbricole!
Et merci beaucoup pour ta réponse.
En bluetooth, en effet, c'est tout à fait faisable. Bien que cela demande le temps de connexion à chaque utilisation contrairement au wifi qui est +/- permanent mais, en soi, ce n'est pas bloquant. Si cela peut rendre le projet plus accessible, je prends ^^!

Je pense comprendre l'idée des commandes locales : simplifier la commande pour être plus pratique. Mais je ne saurai pas les mettre directement en application. Donc si exemples, tutos ou autres il y a, je veux bien les tuyaux stp :slight_smile:

Pour l'app mobile, je vais débuter avec des tutos de base sur MIT App Inventor 2 en espérant atteindre rapidement la compétence de créer une liste modifiable avec des objets dont je pourrais rattacher les noms à une "adresse" et à la commande led inhérente.
Exemple : cliquer sur la case "figurine" (préalablement "adressée") de l'app enverra automatiquement une commande LED0502 (par exemple) à l'arduino.

Bonsoir galien
Avec un ESP en mode access point Wifi, c'est aussi valable, j'ai trouvé la "paire" MIT App Inventor (MAI)/sketch ESP, je te passe ça demain.

C'est aussi pour demain :wink:

Recherche des tutos qui communiquen en Wifi.

Bonne soirée
jpbbricole

Bonjour galien

Comme exemple, je t'ai fait un bout de programme pour gérer tes LED via des commandes dans le moniteur:
image
Ces ordres sont LED, LUM, TEMPO et OFF, ils sont détaillés, dans le sketch, audessus de void cmdExecute(String cmdRx)
Au départ la durée de l'allumage en secondes est défini par:
int ledOnTemps = 0; // Durée d'allumage de la LED, si 0 = continu

Le programme:

/*
    Name:       AF_galien_RgmtCasierLed.ino
    Created:	11.03.2025
    Author:     galien/jpbbricole
	Remarque:	Commande de LED pour casier de rangement, depuis smartphone
				https://forum.arduino.cc/t/aide-pour-developper-app-telephone-arduino-leds/1362675
	13.03.2025	Première version #5
*/
#include <Adafruit_NeoPixel.h>

//------------------------------------- LED
const int ledPin = 6; // Connexion du bus des LED

const int ledNombre = 16; // Nombre de LED LED
int ledOnTemps = 0; // Durée d'allumage de la LED, si 0 = continu

Adafruit_NeoPixel leds(ledNombre, ledPin, NEO_GRB + NEO_KHZ800);
enum ledlColorsIndex {ledsColOff, ledsColGreen, ledsColBlue, ledsColRed, ledsColOrange, ledsColYellow, ledsColWithe, ledsColNombre};
uint32_t ledsColors[ledsColNombre];// Valeurs RGB pour les couleurs (Tableau)
const int ledsBrightMax = 25;// LED Luminosité maximum

void setup() 
{
	Serial.begin(115200); // Pour la console et l'introduction des commandes
	signLedInitialisation();

	leds.show();
	delay(500);
	leds.clear();
	leds.show();
}

void loop() 
{
	if (Serial.available()) // Si commande dans la console
	{
		cmdExecute(Serial.readStringUntil('\n')); // Lire la commande jusqu'au caractère nouvelle ligne
	}
		
}

//------------------------------------- Exécution de la commande
/*
	Commandes non sensibles à la casse
	Commandes reconnues:
	LEDllcc		commande de la LED ll à la couleur cc (ledlColorsIndex)
	LUMlll		commande de luminosité générale (0-255)
	TEMPOttt	Temps d'allumage des LED. Si 0 alumage continu
	OFF			Extinction complète
*/
void cmdExecute(String cmdRx)
{
	cmdRx.trim(); // Commande nettoyage des caractères "parasites"
	// https://www.arduino.cc/reference/fr/language/variables/data-types/string/functions/trim/ 
	cmdRx.toUpperCase(); // Commande en majuscules
	cmdRx.replace(" ", ""); // Commande suppression des espaces
	
	if (cmdRx.startsWith(F("LED"))) // Si commande LED
	{
		cmdRx.replace(F("LED"), ""); // On supprime la commande pour ne garder que les paramètres
		int ledNum = cmdRx.substring(0, 2).toInt(); // Extraction du numéro de la LED
		int ledCol = cmdRx.substring(2).toInt(); // Extraction de la couleur de la LED
		
		ledAllumage(ledNum, ledCol); // Commande allumage
	}
	else if (cmdRx.startsWith(F("LUM"))) // Si commande luminosité
	{
		cmdRx.replace(F("LUM"), "");
		Serial.println("Luminosite: " + cmdRx);

		leds.setBrightness(cmdRx.toInt());
		leds.show();
	}
	else if (cmdRx.startsWith(F("TEMPO"))) // Si commande temps d'allumage si 0 allumage continu
	{
		cmdRx.replace(F("TEMPO"), "");

		Serial.println("Temporisation: " + cmdRx);
		ledOnTemps = cmdRx.toInt();
	}
	else if (cmdRx == (F("OFF"))) // Si commande extinction complète
	{
		Serial.println("Extinction");
		leds.clear();
		leds.show();
	}
	else
	{
		Serial.println("Commande inconnue: " + cmdRx);
	}
}

void ledAllumage(int ledNum, int ledCouleur)
{
	Serial.print("Allumage LED " + String(ledNum));
	Serial.println("\tcouleur " + String(ledCouleur));

	leds.setPixelColor(ledNum, ledsColors[ledCouleur]); // Allumage de la LED
	leds.show();

	if (ledOnTemps > 0)
	{
		delay(ledOnTemps * 1000);
		leds.setPixelColor(ledNum, ledsColors[ledsColOff]); // Extinction de la LED
		leds.show();
	}
}

void signLedInitialisation() // Initialisation des leds et des couleurs
{
	ledsColors[ledsColOff] = leds.Color(0, 0, 0);
	ledsColors[ledsColRed] = leds.Color(255, 0, 0);     // Définition des couleurs
	ledsColors[ledsColGreen] = leds.Color(0, 255, 0);
	ledsColors[ledsColBlue] = leds.Color(0, 0, 255);
	ledsColors[ledsColYellow] = leds.Color(255, 255, 0);
	ledsColors[ledsColOrange] = leds.Color(255, 165, 0);
	ledsColors[ledsColWithe] = leds.Color(255, 255, 255);

	leds.begin();
	leds.setBrightness(ledsBrightMax);
	leds.clear();
	leds.show();
}

Ensuite, une fois le programme MAI fonctionnel ainsi que son serveur Web sur ESP, le programme ci-dessus viendra simplement se greffer au serveur.

N'hésites surtout pas à poser des questions, si nécessaire :wink:

A+
Bonne journée
jpbbricole

Une autre approche serait en WiFi avec un ESP32 qui embarquerait une petite application Web il n’y aurait ainsi rien à installer sur le téléphone et ça marcherait avec n’importe quel navigateur web connecté au réseau de l’ESP32.

Une interface admin simple pourrait servir à associer la led à un objet et si les leds étaient addressables comme un bandeau led (même si ce n’est pas un bandeau on peut les chaîner) alors on aurait un truc tout en un facile à déployer.

J’ai un truc un peu similaire quelque part - faudrait que je remette la main dessus

Bonjour J-M-L

Ce serai même la solution la plus simple.

Bonne journée
jpbbricole

L'un n'empêche pas l'autre.
La même API HTTP, pourrais aussi bien répondre à une application MAI, qu'une application WEB.
cela évite la "lourdeur" du parsing des commandes textes.

Bonjour J-M-L

Je me suis amusé (même bien amusé :wink:) à l'exercice avec la "complicité" de ChatGPT pour la partie application Web et ça donne ceci:


Le temps d'allumage du casier est défini dans le tableau;

ledOffTempoDef ledOn[] = // Si tempo = 0, On/Off/On/Off ...
{ 
	{ledOnTempo, 0}, {ledOnTempo, 0}, {ledOnTempo, 0}, {0, 0},
	{ledOnTempo, 0}, {ledOnTempo, 0}, {ledOnTempo, 0}, {ledOnTempo, 0},
	{ledOnTempo, 0}, {ledOnTempo, 0}, {ledOnTempo, 0}, {ledOnTempo, 0},
	{ledOnTempo, 0},			 {ledOnTempo, 0}, {ledOnTempo, 0}, {ledOnTempo, 0},
};

On voit que le casier
image
est en allumage On/Off.

Il faut ajuster ces paramètres à son propre réseau:

// Configuration réseau wifi
const char* wifiSsid = "xxxxxxxxx";
const char* wifiPassword = "xxxxxxxxxx";
const char* serveurHostName = "galien-casiers";

Le bus des LED est:
const int ledBusPin = 23; // Connexion du bus des LED

Le programme:

/*
Name:       AF_galien_RgmtCasierWeb.ino
Created:	11.03.2025
Author:     jpbbricole/ChatGPT
Remarque:	Commande de LED pour casier de rangement
			Serveur Web
			https://forum.arduino.cc/t/aide-pour-developper-app-telephone-arduino-leds/1362675
14.03.2025	Première version #9
14.03.2025	Indexation des position des LED #9
15.03.2025	Wifi avec DHCP et DNS #9
*/
//------------------------------------- Wifi
#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>

// Configuration réseau wifi
const char* wifiSsid = "xxxxxxxxx";
const char* wifiPassword = "xxxxxxxxx";
const char* serveurHostName = "galien-casiers";

WebServer server(80);

//------------------------------------- LED
#include <Adafruit_NeoPixel.h>
const int ledBusPin = 23; // Connexion du bus des LED
const int ledPosInStrips[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; // Position des LED sur le strips
const int ledNombre = sizeof(ledPosInStrips) / sizeof(ledPosInStrips[0]); // Nombre de LED
int ledOnTempo = 4000; // Durée d'allumage de la LED, si 0 = On/Off
struct ledOffTempoDef {unsigned long tempo; unsigned long millis;}; 

ledOffTempoDef ledOn[] = // Si tempo = 0, On/Off/On/Off ...
{ 
	{ledOnTempo, 0}, {ledOnTempo, 0}, {ledOnTempo, 0}, {0, 0},
	{ledOnTempo, 0}, {ledOnTempo, 0}, {ledOnTempo, 0}, {ledOnTempo, 0},
	{ledOnTempo, 0}, {ledOnTempo, 0}, {ledOnTempo, 0}, {ledOnTempo, 0},
	{ledOnTempo, 0},			 {ledOnTempo, 0}, {ledOnTempo, 0}, {ledOnTempo, 0},
};

const int stripsLedNombre = 24; // Nombre de LED sur le strips
Adafruit_NeoPixel leds(stripsLedNombre, ledBusPin, NEO_GRB + NEO_KHZ800);
enum ledlColorsIndex {ledsColOff, ledsColGreen, ledsColBlue, ledsColRed, ledsColOrange, ledsColYellow, ledsColWithe, ledsColNombre};
uint32_t ledsColors[ledsColNombre];// Valeurs RGB pour les couleurs (Tableau)
const int ledsBrightMax = 25;// LED Luminosité maximum

// Couleurs des cases
const String webColors[16] = {"green", "blue", "red", "orange", "yellow", "white", "blue", "red", "orange", "yellow", "white", "blue", "red", "orange", "yellow", "white"};
const String webColorsPalette[] = {"green", "blue", "red", "orange", "yellow", "white"};
const int webColorNombre = 6; // 6 couleurs dans la palette

void setup()
{
	Serial.begin(115200);

	WiFi.begin(wifiSsid, wifiPassword);
	while (WiFi.status() != WL_CONNECTED) 
	{
		delay(1000);
		Serial.println("Connexion au WiFi...");
	}
	Serial.print("WiFi connected ");
	Serial.println(WiFi.localIP());
	// Attribution hostname
	if (!MDNS.begin(serveurHostName))
	{
		Serial.println("Erreur lors de la configuration mDNS!");
	}
	else
	{
		Serial.print("mDNS OK ");
		Serial.println(serveurHostName);
	}

	server.on("/", wifiHandleRoot);
	server.on("/led", wifiHandleLED);
    server.on("/allOFF", handleAllOFF);
	server.begin();

	signLedInitialisation(); // Démarrage des LED
}

void loop()
{
	server.handleClient();
	
	// Extinction des LED
	for (int l = 0; l < ledNombre; l ++)
	{
		if (ledOn[l].millis != 0) // Si temporisation active
		{
			if (millis() - ledOn[l].millis >= ledOn[l].tempo) // Si temporisation terminée
			{
				ledExtinction(l);
				ledOn[l].millis = 0; // Arrêt de la temporisation
			}
		}
	}
}

//------------------------------------- Serveur Web
void wifiHandleRoot() 
{
    String html = "<html><head><style>";
    html += "body { text-align: center; font-family: Arial; } ";
    html += ".grid { display: grid; grid-template-columns: repeat(4, 80px); grid-gap: 10px; justify-content: center; } ";
    html += ".cell { width: 80px; height: 80px; display: flex; align-items: center; justify-content: center; font-size: 20px; cursor: pointer; transition: background 0.5s; } ";
    html += ".cell:hover { opacity: 0.8; }";
    html += ".off-button { margin-top: 20px; padding: 15px 0; font-size: 18px; background: red; color: white; border: none; cursor: pointer; width: calc(4 * 80px + 30px); }";
    html += ".off-button:hover { background: darkred; }";
    html += "</style></head><body>";
    html += "<h1>galien's casiers couleur</h1><div class='grid'>";
    
    for (int i = 0; i < 16; i++) 
	{
	    html += "<div class='cell' id='cell" + String(i) + "' style='background:" + webColors[i] + "' onclick=\"changeColor(" + String(i) + ", '" + webColors[i] + "')\">" + String(i+1) + "</div>";
    }
    
    html += "</div><br><button class='off-button' onclick=\"location.href='/allOFF'\">OFF</button>";
    
    html += "<script>";
    html += "function changeColor(index, originalColor) {";
	    html += " var cell = document.getElementById('cell' + index);";
	    html += " cell.style.background = 'gray';";
	    html += " setTimeout(function() { cell.style.background = originalColor; }, 1000);";
	    html += " location.href='/led?num=' + index + '&color=' + originalColor;";
    html += "}";
    html += "</script></body></html>";
    
    server.send(200, "text/html", html);
}

void wifiHandleLED()
{
	if (server.hasArg("num") && server.hasArg("color"))
	{
		int caseNum = server.arg("num").toInt();
		String caseColor = server.arg("color");
		if (caseNum >= 0 && caseNum < 16)
		{
			ledAllumage(caseNum, caseColor);
		}
	}
	server.sendHeader("Location", "/");
	server.send(303);
}
void handleAllOFF() 
{
	Serial.println("All LEDs OFF");

	leds.clear();
	leds.show();

	server.sendHeader("Location", "/");
	server.send(303);
}

//------------------------------------- LED
void ledAllumage(int ledNum, String ledCouleur)
{
	Serial.print("Allumage LED " + String(ledNum));
	Serial.print("\tcouleur " + ledCouleur);
	Serial.println("\tLed pos:" + String(ledPosInStrips[ledNum]));	
	
	int couleurIndex = ledColorIndex(ledCouleur) +1;

	if (ledOn[ledNum].tempo > 0) // S'il y a temporisation sur cette LED
	{
		leds.setPixelColor(ledPosInStrips[ledNum], ledsColors[couleurIndex]); // Allumage de la LED
		leds.show();
		ledOn[ledNum].millis = millis(); // Démarrage du chrono
	}
	else // Si LED On/Off
	{
		if (leds.getPixelColor(ledPosInStrips[ledNum]) != 0) // Si LED déjà allumée
		{
			leds.setPixelColor(ledPosInStrips[ledNum], ledsColors[ledsColOff]); // Eteindre la LED
		} 
		else
		{
			leds.setPixelColor(ledPosInStrips[ledNum], ledsColors[couleurIndex]); // Allumer la LED
		}
	leds.show();
	}
}

void ledExtinction(int ledNum)
{
	leds.setPixelColor(ledPosInStrips[ledNum], ledsColors[ledsColOff]); // Extinction de la LED
	leds.show();
}

int ledColorIndex(String color) // Retourne l'index de la couleur en fonction de son nom
{
	for (int c = 0; c < webColorNombre; c ++)
	{
		if (color == webColorsPalette[c]) // Si couleur trouvée dans la palette
		{
			return c;
		}
	}
	return -1; // Couleur pas trouvée
}

void signLedInitialisation() // Initialisation des leds et des couleurs
{
	ledsColors[ledsColOff] = leds.Color(0, 0, 0);
	ledsColors[ledsColRed] = leds.Color(255, 0, 0);     // Définition des couleurs
	ledsColors[ledsColGreen] = leds.Color(0, 255, 0);
	ledsColors[ledsColBlue] = leds.Color(0, 0, 255);
	ledsColors[ledsColYellow] = leds.Color(255, 255, 0);
	ledsColors[ledsColOrange] = leds.Color(255, 165, 0);
	ledsColors[ledsColWithe] = leds.Color(255, 255, 255);

	leds.begin();
	leds.setBrightness(ledsBrightMax);
	leds.clear();
	leds.show();
}

Bonne soirée
jpbbricole

Bravo

Merci!

Bonjour à tous!
Wow!! Quel travail! merci infiniment. Moi qui suis novice, j'ai de quoi cogiter avec tout ça!
Je vais tenter d'avancer sur ce projet grâce à votre aide!
Merci encore!!!

Ce sont justement des leds adressables que j'ai reliées "en cascade" sur le pin 6. Cf : jpeg

Bonsoir galien

N'hésite surtout pas à poser des questions :wink:

Le programme du post#5 tourne sur UNO, par contre, pour celui du post#9, j'ai utilisé un ESP32 Wroom:
image
image

Avec cette carte tu as tout ce qu'il faut pour ton projet dont le wifi.

Bonne soirée
jpbbricole

Je comprends 70% des codes mais de là à les mettre en application...
J'ai un niveau à peine supérieur au "blink", je n'ai aucun projet "en réseau" à mon palmarès (juste un distributeur automatique de croquettes et un système d'arrosage autonome :sweat_smile:, ) pas de connaissance en HTML, etc...
Si je veux tester en pratique votre travail le plus simplement possible avec ESP32 et application web : disons que je possède le circuit pré-monté (leds, etc...), un arduino avec ESP32, une tablette tactile (ou smartphone) et mon réseau wifi domestique. A partir de ces éléments, pouvez-vous m'expliquer, pas à pas, ce que je dois faire pour parvenir à tester cela svp?
Si ce n'est pas trop long pour vous bien sûr...
Merci beaucoup!

Cela ne me dérange absolument pas d'utiliser cette carte là. Mais fonctionne t-elle avec le même IDE qu'arduino? Ce n'est ni un Uno, ni un Méga, ni un Nano, etc... comment paramétrer l'IDE pour la reconnaissance et le téléversement du code?

Sur la tablette, rien.
Charger le programme du post#9 dans l'ESP et y mettre les paramètres propres à ton réseau.

// Configuration réseau wifi
const char* wifiSsid = "xxxxxxxxxxx";
const char* wifiPassword = "xxxxxxxxxxx";
const char* serveurHostName = "galien-casiers";

Puis, depuis depuis ta tablette ou ton smartphone, taper:
http://galien-casiers

A+
jpbbricole

Autant laisser le dhcp affecter une adresse puis ensuite modifier le routeur pour faire une allocation statique pour l’ESP quand on aura repéré son adresse Mac. Comme ça le code reste générique

sous:

Il faut installer ceci:

La suite demain :wink:

Bonne nuit
jpbbricole

Oui, c'est une option, mais je ne connais, pas encore quelles sont les compétences de @galien dans le domaine du réseau et des manipulations d'un routeur.