I see 2 problems with your code:
You wrote in the comments, that the JSON file is huge. The Uno has only 2kB of RAM and you are trying to lead the complete file into RAM at once. Such a JSON file from Spotify will easily exceed the 2kB (in fact you have even less, because the Arduino framework and SoftwareSerial also use some of the RAM). So you just cannot handle the complete file at once with an Uno.
You use readString()
. This function is sure easy, but also has some pitfalls. It uses the String
class, which utilizes dynamic memory allocation. It allocates a bigger character array in RAM, if the current array is not big enough. That means, that you would need up to 2 versions of your String fitting into RAM while receiving, leaving only about 1kB. Also it will read from the SoftwareSerial interface, until a timeout is hit (1s by default I think). For building a robust communication between the two devices you should read until you reach a special character, for example via readStringUntil()
. That special character is often the newline character '\n'
.
Possible solution: First we should talk about the limited RAM problem. As stated above you cannot handle the full file at once. You need to handle it in chunks. Whatever you want to do with the data in the end, the Uno will need some time handling the current chunk of data. So you need to pause the transmission of data at the ESP, until the Uno is ready to receive more. Here you can go two ways:
- Send the data in chunks of predefined size (small enough for the Uno to hold at once in its RAM) and wait a predefined time between each chunk, that is long enough for the Uno to handle every data it might have gotten in that chunk. This is the easiest to program, but will necessarily take more execution time.
- Let the Uno signal to the ESP, that its buffer is full, for example by sending a special character to the ESP. The ESP should then stop sending, until it gets the OK from the Uno to proceed the transmission. To send without loosing data the Uno should probably also send the number of bytes received in that chunk. That way the ESP knows exactly, where in the data String the Uno is currently. This is a bit more difficult, though still easy enough and robust.
To handle the sending in chunks I would use the write(const uint8_t *buffer, size_t size)
method of SoftwareSerial (actually this function comes from the Print
class, from which SoftwareSerial
inherits). The buffer is the pointer to the first element of the current chunk in the char
array with the data. You can get the char
array via spotData.c_str()
and you can get the pointer to the n-th element of it via &(spotData.c_str()[n])
. The second parameter is the size of the chunk in bytes. This needs to be small enough, that the data will fit into the RAM of the UNO, together with all the RAM needed for processing the data.
On the Uno I would then do the reading bytewise. Something like this:
char data[BUFFER_SIZE]="";
int pos=0;
void loop(){
if(ardSerial.available()){
data[pos] = ardSerial.read();
if(pos == BUFFER_SIZE - 1){
// Handle chunk of data
pos = 0; // reset position to reuse buffer
} else {
pos++;
}
}
}
I have left out some parts to only demonstrate the principle. Also you need to define BUFFER_SIZE
yourself, depending on the available RAM.
Obviously this does not implement the second option with the pause signal, which is more complex.
Apart from sending and receiving you will need to think about how to process the chunks of data. This depends largely on what exactly you want to do with that data. Just sending them via Serial is easy. Interpreting the data in chunks is more difficult.