Hello world ! Vous dites que vous voulez brancher votre vieux capteur SFR05 à un ESP et programmer le tout en langage C ou Python ? Vous êtes tombés sur le bon site ! UwU
Matériel :
- 1 ESP32 (j’utiliserai un Lora32 avec ecran OLED)
- 3 Résistances (environ 470 ohms)
- 1 Capteur SRF05
Vous suivez ce petit tuto à vos risques et périls. Je ne saurais tenu être responsable de la dégradation directe ou indirecte de votre matériel quel qu’il soit 😉 beup
Sommaire
1 – Introduction
Le capteur à ultrasons SRF05 est un capteur de distance unidirectionnel 5V bon marché (environ 4€). Equipé de deux broches, la 1e active l’émetteur tandis que la 2e renvoie l’état du récepteur.
Ce capteur est-il simple d’apparence ? Oui !
Ce capteur est-il finalement compliqué à utiliser ? Oui !
Ce capteur est-il excellent pour apprendre la programmation ? Oui !
Vaut-il mieux acheter un capteur 5 fois plus cher, mais d’usage plus simple et moins facile à perturber ? Tout dépend de votre budget ! :p
Théorie
La théorie est simple. Equipé 1, voire 2 modules à ultrasons. Le capteur mesure le temps T pris par l’onde envoyée par un des modules pour être reçue par le second. S’il n’y a qu’un seul module, ce dernier se charge de réaliser les 2 actions.
Connaissant la vitesse de propagation du son dans l’air (340 m/s), il ne nous suffit que d’une simple multiplication pour avoir la distance aller-retour. Une division par 2 donne la distance D entre sonar et objet !
D = \dfrac{T\times 340 m/s}{2}C’est beau la théorie ;’).
Réalité
Première chose : la vitesse de propagation du son dans l’air n’est pas fixe. Heureusement, pour la majorité des applications robotiques qui se résume à la détection d’obstacles proches ou éloignés, cette variation peut être négligée. Les causes de la variation sont :
- Humidité de l’air
- Vitesse de l’air
- Pression atmosphérique
- Température de l’air
Si votre capteur (au hasard le SRF05) ne les prend pas en compte, il vous faudra corriger la valeur obtenue si vous voulez une mesure précise. Notez que dans certaines applications, cette variation est une aubaine. Les Girouettes basés sur 4 sonars utilisent la différence de mesure renvoyé par nos 4 sonars pour déterminer la direction du vent.
Seconde chose : La précision du sonar. Plusieurs choses peuvent en plus perturber la mesure du sonar. Certaines peuvent être compensées, d’autres non :
- L’angle d’émission (souvent > 10°, tous les objets dans cet angle seront vus)
- La forme de l’obstacle (un obstale mince ou de biais ne sera pas détecté)
- La distance avec l’obstacle (un obstacle ne sera pas vu si trop éloigné, et la mesure sera faussée si trop proche)
- La tension d’alimentation (le SRF05 accepte une tension de 3.3V, avec un fonctionnement dégradé)
- Les erreurs de quantification (pas encore évalué)
- Les ondes électromagnétiques
Troisième chose : Les perturbations extérieures. Elles peuvent être d’origine életromagnétiques ou provenir d’autres sources ultrasonores. Pour limiter leurs actions, le SRF05 :
- Se limite à une bande de fréquence sonore fixe
- Envoie 8 impulsions et attend de toutes les recevoir
Ces deux actions suffisent à rejeter ce genre de perturbation. Cependant, si un autre SRF05 (ou similaire) proche émet dans votre direction, vous ne pouvez rien faire d’autre que remarquer sa présence si vous n’émettez pas. Utiliser ce capteur sur un robot n’est pas une bonne idée si d’autres robots sont proches.
2 – Caractéristiques
Soyez patient, cet article / partie sera bientôt prêt ! Faites moi savoir votre impatience via les commentaires 😉
Broches
Le SRF possède 5 broches. 4 sont utiles :
- Alimentation
- Vcc : Alimentation 5V
- GND : Masse
- Broches IO
- Trig : Broche d’entrée du capteur – Lancement de la mesure de distance
- Echo : Broche de sortie du capteur – Retour de la mesure de distance
Alimentation : Le capteur est conçu pour fonctionner sous 5V. Néanmoins, il est possible de l’alimenter en 3,3V, ce qui entrainera une légère baisse des performances.
Broches IO : La tension de travail des broches est liée à celle de l’alimentation. Or les tensions sur les connexions entre le capteur et le microcontrôleur (en général 5V pour les Arduino et 3,3V pour les ESP) doivent correspondre. En effet, une tension supérieure à celle de travail sur une broche peut entrainer sa dégradation. Deux solutions sont possibles :
- Utiliser un pont diviseur de tension sur les broches susceptibles de recevoir une tension trop importante
- Utiliser la même alimentation le capteur et le microcontrôleur
3 – Préparation
Circuit électrique
Alimentation : Les deux schémas présentés ci-dessous sont supposés alimentés via USB. Si vous voulez les alimenter à partir d’une source externe. Utilisez une source 5V et branchez la borne + sur VIN et la broche – sur GND
Résistances de protection : Vous avez remarqué les résistances en plus du pont diviseur de tension ? Par défaut, je pose toujours une résistance sur les broches d’entrée / sortie, ce afin de les protéger d’une mauvaise manipulation (branchement et / ou programmation). Je ne les enlève que si elles perturbent le circuit. Je vous invite à toujours faire comme moi 🙂
4 – Mise en marche
Vous pouvez retrouver ci-dessous 3 groupes de programmes écrits en C ou en Python. Vous pouvez utiliser des ESP32 pour les deux premiers groupes (il suffit juste de désactiver les fonctions liés à l’écran) :
- Machine états : La fonction permettant la gestion du capteur est programmé de manière à être non bloquante et selon une logique de machine à d’états (C : sonar_update et P : receiveThread). La distance mesurée est imprimée sur le port série chaque 0,5s.
- Interruptions : Le fonctionnement est similaire au programme précédent, à ceci près qu’on utilise des interruptions pour gérer la réception des données sur la broche ECHO. Ainsi, la mesure ne sera pas affectée si vous ajoutez des fonctions bloquantes ou des delay à ce programme.
- IoT : Ici je vous propose d’utiliser 2 Lora32, un pour recueillir, puis émettre via son module sans fil Lora la distance mesurée. L’autre Lora reçoit la valeur, puis l’affiche.
Vous noterez que la gestion du capteur se fait à travers une fonction non bloquante contrairement à la majorité des programmes que vous trouverez sur internet. La prise de mesure de la distance pouvant prendre jusqu’à 30ms par le capteur, il serait dommage d’empécher à l’ESP de réaliser d’autres actions entre temps 😉
Il se peut que la mesure de distance soit faussée, dans ce cas, modifiez la valeur « 58 » la ligne
#define PIN_ECHO 12
#define PIN_TRIG 13
long temps = 0;
byte etape = 0;
long distance = 0;
void setup()
{
pinMode(PIN_TRIG, OUTPUT);
pinMode(PIN_ECHO, INPUT);
Serial.begin(115200);
}
void loop()
{
sonar_update();
}
void sonar_update()
{
switch(etape)
{
case 0: digitalWrite(PIN_TRIG, 1);
temps = micros();
etape++;
break;
case 1: if(micros() - temps > 20)
{
digitalWrite(PIN_TRIG, 0);
etape++;
temps = micros();
}
break;
case 2: if(digitalRead(PIN_ECHO) == 1)
{
etape++;
temps = micros();
}
else if(micros() - temps > 30000)
{
etape = 0;
distance = -1;
}
break;
case 3: if(digitalRead(PIN_ECHO) == 0)
{
distance = micros() - temps;
distance = (distance * 10) / 58;
etape++;
}
else if(micros() - temps > 30000)
{
etape = 0;
distance = -1;
}
break;
case 4: Serial.print("distance (mm) : ");
Serial.println(distance);
etape++;
temps = micros();
break;
case 5: if(micros() - temps > 500000)
{
etape=0;
}
break;
default:etape = 0;
break;
}
}#define PIN_ECHO 12
#define PIN_TRIG 13
long temps = 0;
long temps_int = 0;
byte etape = 0;
long distance = 0;
bool distance_new = 0;
void setup()
{
pinMode(PIN_TRIG, OUTPUT);
pinMode(PIN_ECHO, INPUT);
Serial.begin(115200);
}
void loop()
{
sonar_update();
}
void echo_up()
{
temps_int = micros();
attachInterrupt(PIN_ECHO, echo_down, FALLING);
}
void echo_down()
{
distance = micros() - temps_int;
distance_new = 1;
detachInterrupt(PIN_ECHO);
}
void sonar_update()
{
switch(etape)
{
case 0: digitalWrite(PIN_TRIG, 1);
temps = micros();
etape++;
break;
case 1: if(micros() - temps > 20)
{
distance_new = 0;
attachInterrupt(PIN_ECHO, echo_up, RISING);
digitalWrite(PIN_TRIG, 0);
etape++;
temps = micros();
}
break;
case 2: if(distance_new == 1)
{
etape++;
distance = (distance * 10) / 58;
}
else if(micros() - temps > 60000)
{
etape = 0;
distance = -1;
detachInterrupt(PIN_ECHO);
}
break;
case 3: Serial.print("distance (mm) : ");
Serial.println(distance);
etape++;
temps = micros();
break;
case 4: if(micros() - temps > 500000)
{
etape=0;
}
break;
default:etape = 0;
break;
}
}from uPySensors.ssd1306_i2c import Display
from sx127x import SX127x
from machine import Pin
import _thread
import time
display = Display()
display.show_text_wrap("Hi Moon")
pin_echo = Pin(12, Pin.IN)
pin_trig = Pin(13, Pin.OUT)
temps = 0
state = 0
distance = 0
def receiveThread():
state = 0
while True:
if state == 0 :
pin_trig.value(1)
time.sleep_us(100)
pin_trig.value(0)
state += 1
temps = time.ticks_us()
elif state == 1 :
if pin_echo.value() == 1 :
state += 1
temps = time.ticks_us()
elif (time.ticks_us() - temps) > 30000 :
state = 0
display.show_text_wrap("My bad")
elif state == 2 :
if pin_echo.value() == 0 :
state += 1
distance = (time.ticks_us() - temps) / 58
temps = time.ticks_ms()
print(distance)
display.show_text_wrap(str(distance))
elif (time.ticks_us() - temps) > 30000 :
state = 0
display.show_text_wrap("My bad")
elif state == 3 :
if (time.ticks_ms() - temps) > 500 :
state = 0
#print(time.ticks_us())
else :
state = 0
_thread.start_new_thread(receiveThread, ())from uPySensors.ssd1306_i2c import Display
from sx127x import SX127x
from machine import Pin
import _thread
import time
display = Display()
display.show_text_wrap("Hi Moon")
pin_echo = Pin(12, Pin.IN)
pin_trig = Pin(13, Pin.OUT)
temps = 0
temps_int = 0
state = 0
distance = 0
distance_new = 0
def echo_up(p) :
global temps_int
temps_int = time.ticks_us()
pin_echo.irq(trigger=Pin.IRQ_FALLING, handler=echo_down)
def echo_down(p) :
global distance
global distance_new
distance = time.ticks_us() - temps_int
distance_new = 1
pin_echo.irq(trigger=Pin.IRQ_FALLING, handler=None)
def receiveThread():
global state
global distance
global distance_new
while True:
if state == 0 :
distance_new = 0
pin_echo.irq(trigger=Pin.IRQ_RISING, handler=echo_up)
pin_trig.value(1)
time.sleep_us(100)
pin_trig.value(0)
state += 1
temps = time.ticks_ms()
elif state == 1 :
if distance_new == 1 :
state += 1
temps = time.ticks_ms()
distance = distance / 58
print(distance)
display.show_text_wrap(str(distance))
elif (time.ticks_ms() - temps) > 60 :
state += 1
temps = time.ticks_ms()
display.show_text_wrap("My bad")
pin_echo.irq(trigger=Pin.IRQ_FALLING, handler=None)
elif state == 2 :
if (time.ticks_ms() - temps) > 500 :
state = 0
else :
state = 0
_thread.start_new_thread(receiveThread, ())from uPySensors.ssd1306_i2c import Display
from controller_esp32 import ESP32Controller
from sx127x import SX127x
from machine import Pin
import config_lora
import _thread
import time
display = Display()
display.show_text_wrap("Hi Moon")
controller = ESP32Controller()
lora = controller.add_transceiver(SX127x(name = 'LoRa'),
pin_id_ss = ESP32Controller.PIN_ID_FOR_LORA_SS,
pin_id_RxDone = ESP32Controller.PIN_ID_FOR_LORA_DIO0)
pin_echo = Pin(12, Pin.IN)
pin_trig = Pin(13, Pin.OUT)
temps = 0
temps_int = 0
state = 0
distance = 0
distance_new = 0
def echo_up(p) :
global temps_int
temps_int = time.ticks_us()
pin_echo.irq(trigger=Pin.IRQ_FALLING, handler=echo_down)
def echo_down(p) :
global distance
global distance_new
distance = time.ticks_us() - temps_int
distance_new = 1
pin_echo.irq(trigger=Pin.IRQ_FALLING, handler=None)
def receiveThread():
global state
global distance
global distance_new
while True:
if state == 0 :
distance_new = 0
pin_echo.irq(trigger=Pin.IRQ_RISING, handler=echo_up)
pin_trig.value(1)
time.sleep_us(100)
pin_trig.value(0)
state += 1
temps = time.ticks_ms()
elif state == 1 :
if distance_new == 1 :
state += 1
temps = time.ticks_ms()
distance = distance / 58
display.show_text_wrap(str(distance))
lora.println(str(distance))
elif (time.ticks_ms() - temps) > 60 :
state += 1
temps = time.ticks_ms()
display.show_text_wrap("My bad")
lora.println("My bad")
pin_echo.irq(trigger=Pin.IRQ_FALLING, handler=None)
elif state == 2 :
if (time.ticks_ms() - temps) > 500 :
state = 0
#print(time.ticks_us())
else :
state = 0
_thread.start_new_thread(receiveThread, ())import config_lora
import _thread
from sx127x import SX127x
from uPySensors.ssd1306_i2c import Display
from controller_esp32 import ESP32Controller
print("Hello World")
# Variables Lora
controller = ESP32Controller()
lora = controller.add_transceiver(SX127x(name = 'LoRa'),
pin_id_ss = ESP32Controller.PIN_ID_FOR_LORA_SS,
pin_id_RxDone = ESP32Controller.PIN_ID_FOR_LORA_DIO0)
# Fonction Lora
def receiveThread():
display = Display()
print("LoRa Receiver")
display.show_text_wrap("LoRa Receiver")
while True:
if lora.receivedPacket():
lora.blink_led()
try:
payload = lora.read_payload()
display.show_text_wrap("{0} RSSI: {1}".format(payload.decode(), lora.packetRssi()))
print("*** Received message ***\n{}".format(payload.decode()))
except Exception as e:
print(e)
display.show_text("RSSI: {}\n".format(lora.packetRssi()), 10, 10)
print("with RSSI: {}\n".format(lora.packetRssi))
# Lancement Lora
_thread.start_new_thread(receiveThread, ())5 – Sources
Vous êtes arrivés jusque la ? Bravo ! Vous voulez une transmission de la mesure du capteur vie BT ou WiFi (au lieu du Lora) ? Dites le moi dans les commentaires !







