Split char array into chunks

I have a long char array I’m getting over UART, and I need to split it into <32byte chunks to use with a WiFi module.

I started on the code a bit, but got lost when determining where to end the final chunk when it comes to a null terminator. This is what I’ve got so far:

      int index = 0;
      int counter = 0;
      while (ctxdata[index] != '\0'){
        if (index * counter > 29){
          counter++;
        }
       index++;
      }
      for (int i = 0; i < counter; i++){
        www.fastrprint(ctxdata); // this function only has a 32byte buffer
      }

Is there perhaps an easier/faster way to do this using std str functions?

Snippets often mean the loss of useful information. This snippet is a case in point.

I can only guess at what library is in use, and what fastrprint(...) does. I can only guess how "ctxdata" is declared.

The fastrprint(...) that I know:

(1) Outputs from program memory, not RAM and not loaded by a UART, AND (2) Breaks data into buffer-sized chunks

... so none of this work is really necessary. Maybe I have the wrong fastrprint(...) ?

If this work was really needed, another array would be needed as well.

If speed is important, the multiply could be replaced by an addition and "index" would be incremented by some amount, say 29. As it stands, the "if" statement cannot work correctly. Try with "index" having a value of, say, three.

Where can we find documentation for fastrprint(...) ?

fastrprint is part of the Adafruit CC3000 library. It’s essentially the same as Stream print except it expects chars ready to spit out over the serial port, so avoids all the overhead of Stream print. It also prints the whole buffer at once instead of 1 at a time (apparently).

I just checked the library and it’s actually limited to about 130 bytes, so I’d need to split the data around that so I can use multiple fastrprint() functions. It is possible to increase the buffer size, but I’m attempting to get all this onto a Mega328, so I’m a bit limited for RAM.

#include <Adafruit_CC3000.h>
#include <ccspi.h>
#include <SPI.h>
#include <string.h>
#include "utility/debug.h"
#include <Timer.h>

Timer timer;

// These are the interrupt and control pins
#define ADAFRUIT_CC3000_IRQ   3  // MUST be an interrupt pin!
// These can be any two pins
#define ADAFRUIT_CC3000_VBAT  5
#define ADAFRUIT_CC3000_CS    10
// Use hardware SPI for the remaining pins
// On an UNO, SCK = 13, MISO = 12, and MOSI = 11
Adafruit_CC3000 cc3000 = Adafruit_CC3000(ADAFRUIT_CC3000_CS, ADAFRUIT_CC3000_IRQ, ADAFRUIT_CC3000_VBAT,
                                         SPI_CLOCK_DIVIDER); // you can change this clock speed

#define WLAN_SSID       "MySSID"           // cannot be longer than 32 characters!
#define WLAN_PASS       "MyPass"
// Security can be WLAN_SEC_UNSEC, WLAN_SEC_WEP, WLAN_SEC_WPA or WLAN_SEC_WPA2
#define WLAN_SECURITY   WLAN_SEC_WPA2

#define IDLE_TIMEOUT_MS  3000      // Amount of time to wait (in milliseconds) with no data 
                                   // received before closing the connection.  If you know the server
                                   // you're accessing is quick to respond, you can reduce this value.

// What page to grab!
#define WEBSITE      "www.website.com"
#define WEBPAGE      "/test.php"

bool WiFiStatus = false;
bool ToggleStatLED = false;
char txdata[328];
bool gettingData = false;


/**************************************************************************/
/*!
    @brief  Sets up the HW and the CC3000 module (called automatically
            on startup)
*/
/**************************************************************************/

uint32_t ip;

void setup(void)
{
  Serial.begin(9600);
  pinMode(6, OUTPUT);

 // Serial.print("Free RAM: "); Serial.println(getFreeRam(), DEC);
  
  /* Initialise the module */
  if (initWifi()){
    //Serial.println(F("WiFi Started!"));
  } else {
    //Serial.println(F("WiFi Start Failed"));
  }
  if (connectWiFi()){
    //Serial.println(F("WiFi Connected!"));
    Serial.println("OK");
    WiFiStatus = true;
  } else {
    //Serial.println(F("WiFi Connect Failed"));
    Serial.println("FAIL");
  }
  ip = 0;
  // Try looking up the website's IP address
  while (ip == 0) {
    if (! cc3000.getHostByName(WEBSITE, &ip)) {
      //Serial.println(F("Couldn't resolve!"));
    }
    delay(500);
  }
  timer.every(10000, checkConnection);
  timer.every(500, dispWifiStat);
}

void loop(void)
{
  timer.update();
  if (Serial.available() > 0){
    char serialdata[3];
    delay(5);
    Serial.readBytesUntil('?', serialdata, 3);
    //while (Serial.available()>0) char c = Serial.read();
    // Return IP address
    if (strncmp(serialdata, "IP", 2) == 0){
        uint32_t ipAddress, netmask, gateway, dhcpserv, dnsserv;
        if(!cc3000.getIPAddress(&ipAddress, &netmask, &gateway, &dhcpserv, &dnsserv))
        {
          Serial.println(F("FAIL"));
        }else{
          cc3000.printIPdotsRev(ipAddress); Serial.println();
        }
        while (Serial.available()>0) char c = Serial.read();
    }
    // Check WiFi status. 0 = disconnected, 1 = scanning, 2 = connecting, 3 = connected
    if (strncmp(serialdata, "ST", 2) == 0){
      Serial.println(cc3000.getStatus());
      while (Serial.available()>0) char c = Serial.read();
    }
    if (strncmp(serialdata, "TX", 2) == 0){
      memset(txdata, 0, sizeof(txdata));
      Serial.readBytesUntil('\r', txdata, 328);
      while (Serial.available()>0) char c = Serial.read();
      Serial.print(txdata);
      if (POSTData()){
          Serial.println("OK");
        } else{
          Serial.println("FAIL");
        }
    }
  }
}


/**************************************************************************/
/*!
    @brief  Tries to read the IP address and other connection details
*/
/**************************************************************************/
bool displayConnectionDetails(void)
{
  uint32_t ipAddress, netmask, gateway, dhcpserv, dnsserv;
  
  if(!cc3000.getIPAddress(&ipAddress, &netmask, &gateway, &dhcpserv, &dnsserv))
  {
    Serial.println(F("Unable to retrieve the IP Address!\r\n"));
    return false;
  }
  else
  {
    Serial.print(F("\nIP Addr: ")); cc3000.printIPdotsRev(ipAddress);
    Serial.print(F("\nNetmask: ")); cc3000.printIPdotsRev(netmask);
    Serial.print(F("\nGateway: ")); cc3000.printIPdotsRev(gateway);
    Serial.print(F("\nDHCPsrv: ")); cc3000.printIPdotsRev(dhcpserv);
    Serial.print(F("\nDNSserv: ")); cc3000.printIPdotsRev(dnsserv);
    Serial.println();
    return true;
  }
}

void checkConnection(){
  //Serial.println(F("Checking connection"));
  if (cc3000.getStatus() == 0){
    WiFiStatus = false;
    //Serial.println(F("Attempting reconnect"));
     if(!connectWiFi()){
      //Serial.println(F("WiFi connect failed"));
    }
  }
  if (cc3000.getStatus() == 3){
    WiFiStatus = true;
    //Serial.println(F("WiFi OK"));
  }
}

bool connectWiFi(){
  // Optional SSID scan
  // listSSIDResults();
  
  if (!cc3000.connectToAP(WLAN_SSID, WLAN_PASS, WLAN_SECURITY, 1)) {
      return false;
  }
  while (!cc3000.checkDHCP())
  {
    delay(100); // ToDo: Insert a DHCP timeout!
  }  
  while (cc3000.getStatus() != 3){
    delay(100);
  }
  return true;
}

void dispWifiStat(){
  if (WiFiStatus){
    if (ToggleStatLED){
      digitalWrite(6, LOW);
      ToggleStatLED = false;
    }else{
      digitalWrite(6, HIGH);
      ToggleStatLED = true;
    }
  } else {
    digitalWrite(6, LOW);
  }
}

bool initWifi(){
  if (!cc3000.begin())
  {
      return false;
  }
  return true;
}

bool POSTData(){
  boolean returnval = false;
  if (cc3000.getStatus() != 3){
    returnval = false;
  } else {
    char txlen[4];
    String txlen2 = String(strlen(txdata));
    txlen2.toCharArray(txlen, 4);
    //Serial.println(txlen);
    //Serial.println(txdata);
    Adafruit_CC3000_Client www = cc3000.connectTCP(ip, 80);
    if (www.connected()) {
      Serial.println(F("Connected"));
      www.fastrprint(F("POST "));
      www.fastrprint(WEBPAGE);
      www.fastrprint(F(" HTTP/1.1\r\n"));
      www.fastrprint(F("Host: ")); www.fastrprint(WEBSITE); www.fastrprint(F("\r\n"));
      www.fastrprint(F("Accept: */*\r\n"));
      www.fastrprint(F("Content-Length: ")); www.fastrprint(txlen); www.fastrprint(F("\r\n"));
      www.fastrprint(F("Content-Type: application/x-www-form-urlencoded\r\n\r\n"));
      Serial.println(F("Sent header"));
      www.fastrprint(txdata); //<------ Can only send ~130bytes at a time
      Serial.println(F("Sent data"));
      /*int index = 0;
      int counter = 0;
      while (ctxdata[index] != '\0'){
        if (index * counter > 29){
          counter++;
        }
      }
      for (int i = 0; i < counter; i++){
        www.fastrprint(ctxdata);
      }*/
    } else {   
      Serial.println(F("Failed connect")); 
      returnval = false;
    }
  
    //Serial.println(F("-------------------------------------"));
    
    /* Read data until either the connection is closed, or the idle timeout is reached. */ 
    unsigned long lastRead = millis();
    while (www.connected() && (millis() - lastRead < IDLE_TIMEOUT_MS)) {
      while (www.available()) {
        char c[64];
        memset(c, 0, sizeof(c));
        www.readBytesUntil('\n',c, 64 );  
        if (strncmp("RXOK", c, 4) == 0){
          //Serial.println("Received OK");
          while (www.available()) {
            char t = www.read();
          }
          returnval = true;
        }
        lastRead = millis();
      }
    }
    www.close();
    return returnval;
    //Serial.println(F("-------------------------------------"));
  }
}

The code has similar functionality to the AT command format. Data is sent using TX?datahere
It is then read into txdata until it hits the CR at the end of the data. The issue is this string can be quite long, and variable length, so I need to figure out how to split it dynamically to fit into the fastrprint() buffer.

It is then read into txdata until it hits the CR at the end of the data.

"It is read..."? What the heck is it? Read from where? You don't "read into txdata" You WRITE into txdata.

Where is ctxtdata declared?

PaulS: "It is read..."? What the heck is it? Read from where? You don't "read into txdata" You WRITE into txdata.

Where is ctxtdata declared?

Read in from serial .. I thought I made that pretty clear. Regardless, the code is irrelevant, I'm simply asking the best way to go about splitting a char array into smaller chunks. It doesn't matter what the variables are called, where they come from, or where they're going, not hard to change variable names later.

I'm simply asking the best way to go about splitting a char array into smaller chunks

The best way depends on what the char array holds. Are the contents delimited in any way, perhaps by a comma ? If not, could they be delimited which would make splitting it up easier,

What you be wrong with

for (byte n = 0; n < 32; n++) {
  // etc
}

Whether or not you find a clever function at the bottom (below all the turtles) that is what must happen.

…R

Things:
It doesn’t matter what the variables are called, where they come from, or where they’re going, not hard to change variable names later.

I feel your frustration(!)

What you have is a generic problem.
Have a code snippet containing a generic solution.
Not claiming it to be the best.

/*
   THIS IS GENERIC CODE, WHICH WILL NOT COMPILE DIRECTLY IN THE ARDUINO IDE 
*/

//do something with a chunk of stringData
void printChunk(char* string) {
    std::cout << string << "\n";
}

//chunk data into strings and pass to action function
//return number of chunks
uint16_t chunkedStringActionL(char* ptrData, uint16_t dataLength, uint16_t chunkLength, void(*action)(char*)) {
    char chunkData[chunkLength +1];
    chunkData[chunkLength] = 0;                             //terminate chunk buffer
    
    uint16_t chunks = dataLength / chunkLength;             //complete chunks
    uint16_t lastChunkLength = dataLength % chunkLength;    //partial or empty

    ptrData[dataLength] = 0;                                //prevent buffer overrun
    
    if(chunks) {
        for(uint16_t i =0; i < chunks; i++) {
            memcpy(chunkData, ptrData, chunkLength);
            ptrData += chunkLength;
            action(chunkData);
        }
        if(lastChunkLength) {
            memcpy(chunkData, ptrData, lastChunkLength +1);      //copy terminator from source
            action(chunkData);
            chunks++;
        }
    }
    return chunks;
}


#define DATA_LENGTH 16
#define CHUNK_LENGTH 3

int main(int argc, const char * argv[]) {
    char data[DATA_LENGTH +1] = "0123456789ABCDEF";
    data[DATA_LENGTH] = 0;
    
    std::cout << data << "\n";
    std::cout << "strLen data = " << strlen(data) << "\n";
    
    chunkedStringActionL(data, strlen(data), CHUNK_LENGTH, printChunk);
    
    return 0;
}

Things:
not hard to change variable names later.

That is indeed true.

And the compiler doesn’t care what they are called.

However it is also true that it is easier to get a program to work properly if you use meanignful names to help your brain (rather than the compiler) to understand what is happening (or not happening).

And you may be surprised to learn that meaningful variable names make it easier for people trying to help you who are not as familiar with your program as you are.

…R