ultrasonic sensor JSN-SR04T V2.0 utilisation en mode 2 (RX-TX)

Bonjour à tous,

toujours curieux dans la découverte des modules, j’ai commandé deux modules JSN-SR04T (voir PJ) qui pourraient être directement utilisables sur un véhicule parce qu’ils sont étanches. En lisant la datasheet (en PJ), je découvre que parmi les 3 modes d’utilisation possibles (mode 1 par défaut), le module, à la condition de lui adjoindre une résistance de 47KOhms peut “faire le job” de manière autonome et se contente (en gros) de renvoyer la mesure de la distance à l’obstacle. Après avoir modifié un module, je constate effectivement que dès la mise en tension, les bips d’emission sont très réguliers (pas comme avec Trig et Echo). Je lis par ailleurs que ce mode permettrait un meilleur niveau de fiabilité que le mode 1.

Je cherche (longtemps) un programme dédié (cf plus bas), parce que vraiment je n’ai pas encore le niveau de faire autrement. Je n’en trouve qu’un. Las, cet exemple traite de 4 capteurs placés en série (via SerialSoftware). Ne disposant que d’un module modifié, je n’arrive pas à exploiter ce code. (rien sur le moniteur série) J’ai essayé beaucoup de choses (//!) mais ne comprenant pas la fonction de toutes les lignes, forcement, autant essayer de jouer au loto. Je passe sur le fait d’avoir bien sur changé les N° de pins et autres choses de ce niveau.
Ma demande est simple, l’un de vous dispose t-il d’un code de départ du JSN SR04T en mode 2? ou bien, comment simplement (//?) modifier ce code pour en attendre quelque chose de constructif. Vous allez penser, c’est comme 98% des posts, c’est pour faire le job à sa place. Ben là, un peu oui, parce que tout ce que j’essaie en ce moment merde prodigieusement!

Bien à vous tous,

#include <SPI.h>
#include <SoftwareSerial.h>

// Sensors pins

#define S1_TX_Pin 16
#define S2_TX_Pin 16
#define S3_TX_Pin 16
#define S4_TX_Pin 16

#define BUZZER_VCC_Pin 4
#define BUZZER_IO_Pin 5

#define S1_RX_Pin 6
#define S1_VCC_Pin 7

#define S2_RX_Pin 8
#define S2_VCC_Pin 9

// TODO set other ports as for 4!
#define S3_RX_Pin 14
#define S3_VCC_Pin 15


// led pin (13) is left out
#define S4_RX_Pin 14
#define S4_VCC_Pin 15

SoftwareSerial sensor1(S1_RX_Pin, S1_TX_Pin);
SoftwareSerial sensor2(S2_RX_Pin, S2_TX_Pin);
// S3 pins TODO (MOSI/MISO pins are needed for SPI)
SoftwareSerial sensor3(S4_RX_Pin, S4_TX_Pin);
SoftwareSerial sensor4(S4_RX_Pin, S4_TX_Pin);

// print measured  values to seriraloutput in cm
boolean printSerial = false;

boolean measuring = true;

// last measured values in cm (0xFF sensor N/A or not  working)
byte lastMeasure[] = {0, 0, 0, 0};
const byte sensorOffline = 0xFF;
 
//temp variables for measure function
byte startByte = 0, mmLow = 0, mmHigh = 0, sumByte = 0;
unsigned int chkSum = 0; // calculated checksum
unsigned int mm = 0; //measured mm.


void setup() {
  if (printSerial) {
    Serial.begin(9600);
    Serial.println("started...");
  }
  pinMode(S1_TX_Pin, OUTPUT);
  pinMode(S2_TX_Pin, OUTPUT);

  pinMode(S1_RX_Pin, INPUT);
  pinMode(S1_VCC_Pin, OUTPUT);

  pinMode(S2_RX_Pin, INPUT);
  pinMode(S2_VCC_Pin, OUTPUT);

pinMode(S3_RX_Pin, INPUT);
pinMode(S3_VCC_Pin, OUTPUT);

  pinMode(S4_RX_Pin, INPUT);
  pinMode(S4_VCC_Pin, OUTPUT);

  // set the data rate for the SoftwareSerial port
  sensor1.begin(9600);
  sensor2.begin(9600);
  sensor3.begin(9600);
  sensor4.begin(9600);

  delay(1000); // wait 1 sec - for new version upload!

  digitalWrite(S1_VCC_Pin, LOW);
  digitalWrite(S2_VCC_Pin, LOW);
  digitalWrite(S3_VCC_Pin, LOW);
  digitalWrite(S4_VCC_Pin, LOW);

  lastMeasure[0] = checkSensors(sensor1, S1_VCC_Pin);
  lastMeasure[1] = checkSensors(sensor2, S2_VCC_Pin);
  lastMeasure[2] = checkSensors(sensor3, S3_VCC_Pin);
  lastMeasure[3] = checkSensors(sensor4, S4_VCC_Pin);
  if (printSerial) {
    Serial.println("sensors checked. ");
  }

  pinMode(BUZZER_VCC_Pin, OUTPUT);

  // SPI slave initialization 
  // (arduino is the slave,  RPi is the master)
  
  // have to send on master in, *slave out*
  pinMode(MISO, OUTPUT);
  // turn on SPI in slave mode
  SPCR |= bit(SPE);
  // turn on interrupts
  SPCR |= bit(SPIE);  
}

// SPI interrupt routine (should be minimum possible)
ISR (SPI_STC_vect)
{
  unsigned long c = SPDR; // value from the RPI (32 bit!)

  // if value is 0xFFFF_FFFF -> send all measured data!


  // if the value is between 0x00001 .. 0x00004
  // then return the measured distance for that sensor.
  if (c == 0xA0) {
   // command used to switch debug (serial print on/off)
    printSerial = !printSerial;
    SPDR = 0xAB;
  } else  if (c == 0xA1) {
   // command used to switch measuring on and off
    measuring = !measuring;
    SPDR = 0xAC;
  } else if (c >= 1 && c <= 4)  { // sensors 1 -4
       // c == 1 is the first sensor!
      byte sc = c-1; 
      if (lastMeasure[sc] == sensorOffline) {
        SPDR = 0xFA;
      }else {
        SPDR = 0xFF & lastMeasure[sc];
      }
  } else {
    // the recieved command is not between 1 and nr of sensors
    // 1 -> first sensor! (not starting with 0
     SPDR = 0xAA;
  }
}

void loop() {

  unsigned int s1cm = 0;
  unsigned int mincm = 100;
  if (measuring) {
    for (byte sc = 0; sc <= 3; sc++) {
      if (lastMeasure[sc] != -1) {
        if ( sc == 0) {
          s1cm = measure(sensor1, S1_VCC_Pin);
        } else if ( sc == 1) {
          s1cm = measure(sensor2, S2_VCC_Pin);
        } else if ( sc == 2) {
          s1cm = measure(sensor3, S3_VCC_Pin);
        } else if ( sc == 3) {
          s1cm = measure(sensor4, S4_VCC_Pin);
        }
        if (s1cm != lastMeasure[sc]) {
          lastMeasure[sc] = s1cm;
        }
        // delay(50);
        if (s1cm < mincm) {
          mincm = s1cm;
        }
      }
    }
  }else {
    lastMeasure[0] = 0;
    lastMeasure[1] = 0;
    lastMeasure[2] = 0;
    lastMeasure[3] = 0;
  }
  delay(1000);
  if (printSerial) {
    // print measurements result
    for (byte sc = 0; sc <= 3; sc++) {
      Serial.print(lastMeasure[sc]);
      Serial.print(" ");
    }
    Serial.println(" ");
  }
/*
  unsigned long v4 = (unsigned long) lastMeasure[3];
  unsigned long v3 = (unsigned long) lastMeasure[2] << 8;
  unsigned long v2 = (unsigned long) lastMeasure[1] << 16;
  unsigned long v1 = (unsigned long) lastMeasure[0] << 24;
  measured = v1 | v2 | v3 | v4;
*/

}

// check if the sensor is not returning a 0xFF within one second deactivate sensor
byte checkSensors(SoftwareSerial &sensor, byte vccPin) {
  digitalWrite(vccPin, HIGH);
  sensor.listen();
  byte count = 0;
  byte startByte = 0;
  do {
    startByte = sensor.read();
    delay(100);
    count++;
  } while ( startByte != 0xFF && count < 10);
  digitalWrite(vccPin, LOW);

  if (count >= 10) {
    return sensorOffline;
  }
  return 0;
}

byte measure(SoftwareSerial &sensor, byte vccPin) {
  digitalWrite(vccPin, HIGH);

  sensor.listen();
  // todo eventually 3 measurements with avg?
  delay(50);
  do {
    byte count = 0;
    do {
      startByte = sensor.read();
      count++;
    } while ( startByte != 0xFF && count <= 10);
    if (count >= 10) {
      digitalWrite(vccPin, LOW);
      return 1;
    }
    // wait till the next 3 bytes are available
    while (sensor.available() < 3) {
      // approx needed for the next 3 bytes
      // delayMicroseconds(10);
    }
    mmHigh = sensor.read();
    mmLow = sensor.read();
    sumByte = sensor.read();

    chkSum =  0xFF + mmHigh + mmLow;
    if (lowByte(chkSum) == sumByte) {
      mm = (mmHigh << 8) + mmLow;
    } else {
      mm = 0;
    }
  } while (mm == 0);
  digitalWrite(vccPin, LOW);
  return mm / 10;
}

module_img.jpg

JSN-SR04T-2.0.pdf (349 KB)

la doc dit:

Mode 2: R27 = 47K is the welding 47K resistance. The pattern is described below

Serial output format for the TTL level, that: 100MS module for the cycle of automatic transmission
The value of the distance, in mm. Serial baud rate: 9600, n, 8,1.
Module power recognition, directly into the work mode, the module to conduct a distance every 100ms range,
And outputs one frame from the pin TX with four 8-bit data. The frame format is: 0XFF + H_DATA + L_DATA + SUM

  1. 0XFF: for a frame to start the data, used to judge;
  2. H_DATA: the upper 8 bits of the distance data;
  3. L_DATA: the lower 8 bits of the distance data;
  4. SUM: data and, for the effect of its 0XFF + H_DATA + L_DATA = SUM (only low 8 ) Note: H_DATA and L_DATA synthesize 16-bit data, that is, the distance in millimeters.
    Description: The module outputs the nearest distance value in the dead zone. If the module does not measure data or is out of range Measured output 0.
    LED indicator, LED non-power indicator, the module connected to work after the light, then the module is in Working state.

on comprend donc que sur le port série on va recevoir des messages composés de la façon suivante

un marqueur de début de trame: 0xFF
deux octets qui sont la distance en mm, le premier octet étant l’octet de poids fort
un checksum sur 1 octet qui est la somme de 0XFF + H_DATA + L_DATA

donc voilà, suffit de bien écouter le port série, vous pouvez jeter un oeil à mon petit tuto sur le sujet

je ferais un truc comme cela, tapé ici donc absolument pas sûr que ça compile ni ne fonctionne

// #include <SoftwareSerial.h>
// SoftwareSerial SR04TSerial(2, 3); // RX, TX
#define SR04TSerial Serial1

struct __attribute__ ((__packed__)) distance_t {
  uint8_t distanceMSB;
  uint8_t distanceLSB;
} distance ;

const uint8_t marqueurDeDebut = 0xFF;

boolean lireDistance(distance_t &mesure, bool forceStart = false)
{
  static uint8_t indexOctet = 0;
  static bool marqueurRecu = false;
  bool mesureOK = false;
  int lecture; // sera un int à -1 si rien à lire 
  uint8_t octetRecu;

  if (forceStart) marqueurRecu = false;

  lecture = SR04TSerial.read();
  if (lecture != -1) { // -1 veut dire rien à lire
    octetRecu = (uint8_t) lecture;
    if (!marqueurRecu) {
      marqueurRecu = (octetRecu == marqueurDeDebut);
      indexOctet = 0;
    } else {
      // on a déjà reçu le marqueur de début
      switch (indexOctet) {
        case 0:
          mesure.distanceMSB = octetRecu;
          indexOctet = 1;
          break;
        case 1:
          mesure.distanceLSB = octetRecu;
          indexOctet = 2;
          break;
        case 2:
          if (((uint8_t) (mesure.distanceMSB + mesure.distanceLSB + 0xFF)) == octetRecu) {
            // CRC est correct
            mesureOK = true;
          } else {
            // erreur de réception, on recommence
            marqueurRecu = false;
          }
          break;
        default: // ne dervait pas arriver
          break;
      }
    }
  }
  return mesureOK;
}

void setup() {
  Serial.begin(115200);
  SR04TSerial.begin(9600, SERIAL_8N1); // 9600, n, 8, 1
  lireDistance(distance, true); // initialise la lecture de distance
}

void loop()
{
  if (lireDistance(distance))  {
    uint16_t mm = ((uint16_t) distance.distanceMSB) << 88 | distance.distanceLSB;
    Serial.print(F("distance en mm = "));
    Serial.println(mm);
  }
}

J-M-L, vous êtes partout!

Vous tapez dans le mille, la doc que j'ai lue met en lumière s'il le fallait mon piètre niveau de connaissance! Je vais décortiquer tout ça, et je reviens... Dans tous les cas je vous tiens au courant.
Merci,
L.G

J’ai légèrement modifié le code pour tester en tout premier si on avait reçu -1 à la lecture du port série pour ne pas avoir de souci avec le fait que 0xFF pourrait être vu comme -1 aussi alors que c’est le marqueur de début et différentier une int d’un byte

Bonjour,

merci pour vos conseils et pour les excellents tutos de "dégrossissage" que vous partagez. Une seule conclusion à cette affaire, je n'ai définitivement pas le niveau. Je compte vraiment sur mon assiduité "dans la durée" pour y arriver. Des années que je rêvais d'Eeprom et de microcontroleur (en fait, depuis qu'on m'avait refusé l'entrée en BTS dans les années 80.
Bref, revenons sur ce sujet. à l'origine, un programme générique qui renvoi trop 10 à 20% de mesures "o" qui sont des distances erronées. Après avoir creusé votre code, sans grand succès, puis après avoir dégradé les pistes de mon circuit (les soudures sur les composants de surface...), j'ai continué à chercher par ailleurs. Sur le forum anglais, j'ai trouvé de nombreuses discutions désappointées sur la V2.0 de ce module, jusqu'à trouver une solution qui, avec le module non modifié, apporte une solution que je n'ai pas encore mis en défaut.

La datasheet indique que le trigger doit être fait au moins toutes les 10 ms. De fait, les exemples choisissent ce rythme de travail, qui ne prend pas en compte (j'imagine) le fait que la loop elle-même prend du temps. Donc, de manière redondante, certaines mesures reviennent à zéro parce que inexploitées. Il suffit d'augmenter de délais pour que toutes les mesures deviennent cohérentes.

Ceci résout donc mon problème de fiabilité, et de fait provoque l'extinction de mon problème.
Merci encore une fois JML d'avoir partagé et bossé sur le sujet.
Je place ci-après le-dit code. La valeur de 20ms apparait après le commentaire // Send a 10uS high to trigger (ceci pour les encore plus débutants que moi)

// note : le delay minimal de 10ms cause de nombreux echos à "0". 
//il suffit d'augmenter ce delai.
#define ECHOPIN 2// Pin to receive echo pulse
#define TRIGPIN 3// Pin to send trigger pulse
void setup(){
  Serial.begin(9600);
  pinMode(ECHOPIN, INPUT);
  pinMode(TRIGPIN, OUTPUT);
  digitalWrite(ECHOPIN, HIGH);
}
void loop(){
  digitalWrite(TRIGPIN, LOW); // Set the trigger pin to low for 2uS
  delayMicroseconds(2);
  digitalWrite(TRIGPIN, HIGH); // Send a 10uS high to trigger ranging
  delayMicroseconds(20);
  digitalWrite(TRIGPIN, LOW); // Send pin low again
  int distance = pulseIn(ECHOPIN, HIGH,26000); // Read in times pulse
  distance= distance/58;
  Serial.print(distance);
  Serial.println("   cm");                   
  delay(50);// Wait 50mS before next ranging
}