writing BLE data into SD card

I tried to use the bluetooth module to search for beacons(set up by myself).
And I want to store the received data into SD card.
Initially, I wrote the buffer into SD card, but SD card reads"OOKOK+OK+DOK+DIOK+DISOK+DISCOK+DISCSOOKOK+OK+DOK+DIOK+DISOK+DISCOK+DISCS", the useful data are missed.
Then I wrote the character directly into SD card, but the SD card reads"-1-1-1-1..."

here's my code:

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

SoftwareSerial ble(7,8);
File myFile;
unsigned char buffer[128];
int count=0;
char c;

void setup() {
 Serial.begin(9600);
 ble.begin(9600);
 if (ble.isListening())
 {
  Serial.println("BLE is listening!");
  }
  while (!Serial) {
    ; 
  }
  Serial.print("Initializing SD card...");
  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
  myFile=SD.open("test.txt",FILE_WRITE);
  if(myFile){
    Serial.println("file opened");
  }else{
    Serial.println("file open failed");
  }
   ble.write("AT+DISC?");
}

void loop() {
  if(ble.available()){
    while(ble.available())
    {
      buffer[count++]=ble.read();
      if(count==128)break;
       Serial.write(buffer,count);
       c=Serial.read();
       myFile.print(c,DEC);
    }
    myFile.close();
     for (int i=0; i<count;i++)
    { buffer[i]=NULL;}
  count=0;}
 
  }

Please tell me if you have suggestions or what I have done wrong. thank you.

    while(ble.available())
    {
      buffer[count++]=ble.read();
      if(count==128)break;
       Serial.write(buffer,count);
       c=Serial.read();
       myFile.print(c,DEC);
    }

what's the deal here with c=Serial.read(); thingy here?
I thought what you wanted to write to the SD card was what you had read from the ble... that's in the buffer... why on earth would it be suddenly available on the Serial Port? (you sent it, it's gone somewhere, it's not coming back to you :)) )

so this would seem more appropriate

    while(ble.available())
    {
      int r = ble.read();
      if (r != -1) { // -1 means error
          buffer[count++]=(byte) r; // we add it into the buffer
          Serial.write(buffer,count); // we blast the buffer to the console
          Serial.println(); // we go to next line so that you can see the string being built
          myFile.print((char) r); // we write the byte as a char in the file
          if(count >= 128) break; // if we got enough we stop
      }
    }

(typed here untested)

but once you have emptied (quickly) the incoming buffer you close the file.. so when the loop loops and you get more data, your file write fails... --> this is not the proper way to handle Serial
(see Serial Input Basics)

what do you really expect from an AT+DISC?

The response of sending "AT+DISC?" once should be like this:

OK+DISCSOK+DIS1:FDF8C2F78340OK+RSSI:-055
OK+NAME:P6
OK+DIS1:E1ECCE1BEAFDOK+RSSI:-038
OK+NAME:P2
OK+DIS1:D928FBEB438AOK+RSSI:-041
OK+NAME:P8
OK+DIS1:D7BC73C73758OK+RSSI:-032
OK+NAME:P10
OK+DIS1:F67750FA4782OK+RSSI:-037
OK+NAME:P9
OK+DIS1:E9B20E663589OK+RSSI:-042
OK+NAME:P1
OK+DISCE

what I want to do is when opening the switch, the command is sent continuously, and the data received are stored into sd card continuously.

Well at the moment you only send it in the setup...

See my comments above why you fail

I have made some changes, ideally, it should get one response each time the switch is on. But I can only get correct response for 2-3 times, then it starts to show "error opening file"
here is my code:

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

SoftwareSerial ble(7,8);
String message="";
String endmarker="DISCE";
const int chipSelect=4;
boolean ok=true;
boolean next=true;
File myFile;
const int SwitchPin=2;
byte val=0;
const int ledPin=5;


void setup() {
  Serial.begin(9600);
  ble.begin(9600);
  if(ble.isListening()){
    Serial.println("ble is listening");
  }
if(SD.begin(chipSelect)){
  Serial.println("card initialized");
}
pinMode(SwitchPin,INPUT);
pinMode(ledPin,OUTPUT);
Serial.println(millis());
}

void loop(){
  val=digitalRead(SwitchPin);
  if(val== HIGH && next==true){
    ble.write("AT+DISC?");
    next=false;
  }
  if(val== HIGH && ok==true){
     BluetoothLowEnergy();
     digitalWrite(ledPin,HIGH);
  }
  if(val==LOW){
    digitalWrite(ledPin,LOW);
    next=true;
  }
}


void BluetoothLowEnergy(){
 
 if(ble.available()>0){
 while(ble.available()>0){
  char c= ble.read();
  message=message+c;
  if(c=='\n'){
    myFile=SD.open("test.txt",FILE_WRITE);
    Serial.print(message);
    if(myFile){myFile.print(message);}
    else{Serial.println("error opening file");}
    message="";
    myFile.flush();
  }else if(message.endsWith(endmarker)){
    Serial.println(message);Serial.println("------END");
    if(myFile){myFile.println(message);myFile.println("------END");}
    else{Serial.println("error opening file");}
    message="";
    Serial.println(millis());
    delay(300);
    myFile.close();
    }
   }
  }
}

You have not read what I told you to read on how to manage Serial input. Don’t try to have synchronous code to manage an asynchronous protocol... will fail you and clearly it does

Your code should have clear states - one is WAITING (waiting for the switchpin), in that state you only read the switch

when the switch gets ticked then you send the AT command and open the file, write some sort of header if needed and change the state to GATHERING_DATA

In that state you call a c functionrecvWithEndMarkers() you will have learnt to write by reading the serial tutorial I pointed out above, Call and Check against being ‘\n’ for line end and test out the newData flag in a similar way the tutorial was testing receiving A line

If you have the flag set, compare the incoming line with strcmp() or strstr() to see if it is (or contains) “OK+DISCE\r\n” or whatever the last line sent by the module is

If you don’t get “OK+DISCE” then it’s a line describing an answer (you might want to parse it to extract the meaningful data), so add the line buffer you got to the file, change newData to false , and go back just waiting for next line.
If it is “OK+DISCE” then write some sort of trailer in the file and close the file, change newData to false and change the state to WAITING

The cycle will start again - if the switch is still triggered you’ll issue the command again and the gathering will continue.

This type of code structure is what is called a state machine, you should read about this. Your loop basically starts with a

switch(state) {
   case WAITING:
      ....
      break;

   case GATHERING_DATA:
      ....
      break;
}
// here your code can do other things as long as it’s nit tying up resources for too long

To be more robust then some sort of timeout could also be implemented if staying too long in the GSTHERING_DATA state with no new input and not receiving the end string. Then my need to discard what you saved from that session in the file, so maybe making a note of where you start writing that header to be able to delete from there in case of timeout

1 Like

how should I declare state, waiting and gathering_data?
I have typed int waiting=0; int gathering_data=1; int state=0;
but it can't work

enum sate_t :byte {WAITING, GATHERING_DATA, UNKNOWN} state;[/tt]