Emptying the buffer

Hi everyone,

I'm currently trying to send a message wireless from an Arduino Uno (emitter) to an Arduino Nano. I'm using Python and Pyserial to do so, since I need to get the message through several algorithms before the transmission step.

When it comes to this step, things do not work anymore. As you'll see, I separated my message in 8-bytes-long strings which I send one after another from the Uno to the Nano. I expect them all to be send to the Nano before I ask it to read them (since the 'for' loop should end before the reading instruction below), and only then, I ask to read what has been received by the Nano.

In the same time, the Nano should wait for an end-of-line character to be received to read the buffer (see code below). However, one of the algorithms I use tells me the variable "recu" is actually empty. Following the Serial Input Basics Tutorial, I think the problem comes from the buffer being read too quickly, and therefore the Nano reading 'empty' slots. Can you help me solving this, if this is the problem ?

What also disturbs me, is that if I add a 'time.sleep(5)' just before the reading instruction, each character sent from the Nano to the Serial Monitor is delayed from 5 seconds, which means the reading instructions works only for one character and goes to the next instruction (every 5 seconds delay means a new execution of the 'while True' loop).

The relevant part of the codes follow :
Python pySerial :

while True : 
        for i in range(len(bits)):                                      
                ArduinoUno.write(bytes(bits[i], 'utf-8'))       
        time.sleep(5)
        recu = str(ArduinoNano.read())                                   
        print(recu)

Receiver (Nano) :

#include <SPI.h>
#include <RF24.h>

#define pinCE   7             // On associe la broche "CE" du NRF24L01 à la sortie digitale D7 de l'arduino
#define pinCSN  8             // On associe la broche "CSN" du NRF24L01 à la sortie digitale D8 de l'arduino
#define tunnel  "PIPE1"       // On définit le "nom de tunnel" (5 caractères) à travers lequel on va recevoir les données de l'émetteur

RF24 radio(pinCE, pinCSN);    // Instanciation du NRF24L01

const byte adresse[6] = tunnel;       // Mise au format "byte array" du nom du tunnel  
char message[32];
boolean newData = false;
const byte numChars = 32;
char receivedChars[numChars];
char add = '\0';

void setup() {
  // Initialisation du port série (pour afficher les infos reçues, sur le "Moniteur Série" de l'IDE Arduino)
  Serial.begin(9600);
  Serial.println("Récepteur NRF24L01");
  Serial.println("");

  // Partie NRF24
  radio.begin();                      // Initialisation du module NRF24
  radio.openReadingPipe(0, adresse);  // Ouverture du tunnel en LECTURE, avec le "nom" qu'on lui a donné
  radio.setPALevel(RF24_PA_MIN);      // Sélection d'un niveau "MINIMAL" pour communiquer (pas besoin d'une forte puissance, pour nos essais)
  radio.startListening();             // Démarrage de l'écoute du NRF24 (signifiant qu'on va recevoir, et non émettre quoi que ce soit, ici)
}
void loop() {
    //recvWithEndMarker();
    
    if (radio.available()) {        // On vérifie si un message est en attente de lecture
      radio.read(&message, sizeof(message));// Si oui, on le charge dans la variable "message"
      newData = true;
    }
    showNewData();
  }
  

Emitter :

#include <SPI.h>
#include <RF24.h>

#define pinCE   7             // On associe la broche "CE" du NRF24L01 à la sortie digitale D7 de l'arduino
#define pinCSN  8             // On associe la broche "CSN" du NRF24L01 à la sortie digitale D8 de l'arduino
#define tunnel  "PIPE1"       // On définit un "nom de tunnel" (5 caractères), pour pouvoir communiquer d'un NRF24 à l'autre

RF24 radio(pinCE, pinCSN);    // Instanciation du NRF24L01

const byte adresse[6] = tunnel;               // Mise au format "byte array" du nom du tunnel
                    // Message à transmettre à l'autre NRF24 (32 caractères maxi, avec cette librairie)

const byte numChars = 32;
char receivedChars[numChars];   // an array to store the received data

boolean newData = false;

void setup() {
    Serial.begin(9600);
    Serial.println("<Arduino is ready>");
    radio.begin();                      // Initialisation du module NRF24
    radio.openWritingPipe(adresse);     // Ouverture du tunnel en ÉCRITURE, avec le "nom" qu'on lui a donné
    radio.setPALevel(RF24_PA_MIN);      // Sélection d'un niveau "MINIMAL" pour communiquer (pas besoin d'une forte puissance, pour nos essais)
    radio.stopListening();              // Arrêt de l'écoute du NRF24 (signifiant qu'on va émettre, et non recevoir, ici)

}
void loop() {
    recvWithEndMarker();
    sendNewData();
}

void recvWithEndMarker() {
    static byte ndx = 0;
    char endMarker = '\n';
    char rc;
    
    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();

        if (rc != endMarker) {
            receivedChars[ndx] = rc;
            ndx++;
            if (ndx >= numChars) {
                ndx = numChars - 1;
            }
        }
        else {
            receivedChars[ndx] = '\0'; // terminate the string
            ndx = 0;
            newData = true;
        }
    }
}

void showNewData() {
    if (newData == true) {
        Serial.print("This just in ... ");
        Serial.println(receivedChars);
        newData = false;
    }
}

void sendNewData() {
  if (newData == true){
    radio.write(&receivedChars, sizeof(receivedChars));     // Envoi de notre message
    delay(1000);   
  }
}

Could you please help me locate the problem and understand what I did wrong ?
Thank you all !

Where is Python running?

On your emitter, sendNewData() never sets the variable newData back to false so it will remain true forever after the first message is received. It does get reset in showNewData() but you never call that function.

I do not see this in the Uno code.

Without reference to Python, can you get the Uno and the Nano communicating as you want with input from either the Serial Monitor, or from data defined in the code.

Can you write code where an array of 8 byte strings is sent from the Uno and they are read and printed by the Nano?

Hi,
Thanks for you answers, I apoplogize for answering so late.

On my computer, on which both Arduinos are plugged in while the message is sent.

Because it is done in the Python code :

saisissez ou collez du code ici

I did not try recently, but I did it before trying to use Python, to check and understand how the hardware works.

shwoNewData() is called at the end of the receiver's loop !?

I just noticed the most important part of the receiver's code has not been pasted on my first message. I do it below. It is the function which makes the Nano wait for a specific character before emptying the buffer :

void recvWithEndMarker() {
    static byte ndx = 0;
    char endMarker = "\n" ;
    char rc;
    
    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();

        if (rc != endMarker) {
            receivedChars[ndx] = rc;
            ndx++;
            if (ndx >= numChars) {
                ndx = numChars - 1;
            }
        }
        else {
            receivedChars[ndx] = '\0'; // terminate the string
            ndx = 0;
            newData = true;
        }
    }
}

up ?

Up what?

Did you answer all the questions above?

We need to see 100% of the code, including the Python part

Ok, here is the Python part :

import serial
import CRC
import numpy as np

a = '0110010010111000110'
CRC.Codage_Canal(a)
print(CRC.Codage_Canal(a))

ArduinoUno = serial.Serial(port="COM3", baudrate=9600, bytesize=8, timeout=2, stopbits=serial.STOPBITS_ONE)
ArduinoNano = serial.Serial(port="COM4", baudrate=9600, bytesize=8, timeout=2, stopbits=serial.STOPBITS_ONE)

file = open('message.txt', 'r')
message = file.read() 
file.close()
print(message)

messagecode = CRC.Codage_Canal(message)
print(messagecode)
                                                                        
bits = []

Generateur = np.array([1, 0, 1, 1])


for i in range(len(messagecode)//8 + 1) :                               
        chaine = '' 
        chaine += messagecode[8*i:8*i+8]
        bits.append(chaine)

while True : 
        for i in range(len(bits)):                                      
                ArduinoUno.write(bytes(bits[i], 'utf-8'))                

        recu = str(ArduinoNano.read())                                   
        recu2 = recu[2:-1]
        print(recu2)

        decode = CRC.Decodage_Canal(recu, Generateur)                              
        ecrire = True                                                   
        
        compteur = 0
        while compteur < len(decode) :                                  
                if decode[compteur] == 0 :
                        compteur += 1
                else :                                                 
                        ecrire = False
                        break
        if ecrire :                                                     
                fichier = open('reception.txt', 'a')
                fichier.write(str(recu))
        else :
                fichier2 = open('erreurs', 'a')                        
                fichier2.write(str(recu))        

I think I answered the other questions.

Another bug just appeared, since he finds out a ' character somewhere in recu, and I don't understand where it comes from. The file only stores a string with "0" and "1".

Have you looked into this?

recu = str(ArduinoNano.read())

What is the Nano sending to Python?
You are only reading one byte.

I answered to this, saying showNewData() is called in the receiver's loop

The Nano is expected to send to Python what it has received from the Uno through the wireless transmission. The result is the same with read_all() and read_until(ending_character).

Currently, it prints b'\r and then a bug appears, which states there is ' character somewhere in "recu". This is particularly weird because recu is set to be a str, not a byte type, and moreover I don't understand where that apostrophe could come from (I'm searching in the Arduino sketches currently, but couldn't find).

Is it ?

May be I’m not looking at the right place

sendNewData != showNewData

One of those functions resets the newData variable, you call the other one in your emitter (Uno) code.

That comes from the emitter's code.
The receiver's loop is there :

void loop() {
    //recvWithEndMarker();
    
    if (radio.available()) {        // On vérifie si un message est en attente de lecture
      radio.read(&message, sizeof(message));// Si oui, on le charge dans la variable "message"
      newData = true;
    }
    showNewData();
  }
  

The code you reference comes from your receiver. I was referring to the emitter code. Is this correct:

PC --> Uno (emitter) --> Nano (receiver)

Yes, that is correct.
To be exact, then there is the step Nano --> PC but I think you had understood that.

However, I don't understand why showNewData should be called in the emitter code, since I don't want to show the data inside the emitter, but the ones inside the receiver.

You don't have to call that function, just set newData back to false so it will again receive input from the PC

Hi,
The problem stands.
In the python code, the variable "recu" which stores what has been received by the Nano, is just empty. And I don't understand why.

I tried, once again, to use the serial monitor of the Arduino IDE to send a simple string message, and everything works well, it is correctly received by the Nano, and printed on the Serial Monitor again.

Is this a correct assessment of the problem:

Message begins (Serial Monitor) --> Uno --> Nano --> Serial Monitor == Works
Message begins (Serial Monitor) --> Uno --> Nano --> Python code == receives nothing

Are these two separate PCs?

Did you do this test with the original posted code above or has it been modified? If so, please post the code used for this test or else everyone will just be guessing.

The test was done with the same Arduino sketches as I posted, if I ever use another one, I'll post it.
The assesment of the problem is correct for the first line. The second is correct but to be very accurate, I'll add :

Message --> Python code --> Uno --> Nano --> Python code == receives nothing

You can see in my first post that I use the command line ArduinoUno.write(bytes(bits[i], 'utf-8')) to send the message to the Uno. I think it's what you meant, but this way it's reallly clear for everyone.
The receives nothing means the variable declared with recu = str(ArduinoNano.read()) is an empty string whereas I need it to contain the message I want to receive.