-1

Here's the example data I've read in H-Term:

\r\n
\r\n
NO:0015\r\n    
G:   5.97kg\r\n    
T:   0.00kg\r\n    
N:   5.97kg\r\n
\r\n
\r\n

How to save the number including "kg" to each variable (NO, G, T, N)?

  • so you need to save strings? – Jaromanda X Jan 30 '19 at 8:49
  • yes, i need to save that into 4 variable represent each line – El Kazma Jan 30 '19 at 10:11
  • what did you try? – Juraj Jan 30 '19 at 12:39
1

This is an overly broad question which borders "write me the code" - this isn't a coding service - and if anything belongs on StackOverflow since it regards processing. That being said, this question would be flagging as overly broad there too.

I'm providing an answer because I believe it may help others here as it speaks to some fundamental programming concepts. This is not a perfect solution by any means, for example it doesn't take program size into account. See the end of the post for a sketch.

There are two main problems you face:

  1. Model the input data
  2. Process the data

Modeling the input data means structuring your expected input in a meaningful way. Looking at the input data provided, I see this format

  • 0-N chars indicate some metadata delimited by a colon (:)
  • 0-N white space
  • 0-N digits with optional decimal point (0015,5.97)
  • 0-N unit designation (kg)
  • data is delimited with a CRLF (\r\n)

Now think of each line as a packet of information. In that sense, let's create some data structures that describe the data of the packet.

// Types
typedef enum {
  NoType,
  NO,
  G,
  T,
  N,
} Type;

// Units
typedef enum {
  NoUnit,
  uKg, // kilogram
  uG,  // gram
} Unit;

// Values - unions are handy to model variants.
typedef union {
  char str[5];
  float dval;
} Value;

Given the basics above, let's model a Packet:

// the data model for a CRLF delimited packet
typedef struct {
  Type type;
  Value value;
  Unit unit;
  byte valid;
  // metadata
  byte expectUnit; // 1 if a Value has a Unit; 0 otherwise.

} Packet;

OK, so that's a basic model of the input data. How to process? Always break down big problems to the smaller, manageable units to provide a solution.

Now there's a huge number of possibilities to parse/interpret your data packets. I am providing a bare bones, brute force approach that you may learn from, and hopefully improve upon. For example I am not providing much in the way of array overruns which can crash a program.

Here's the main processing function, which takes a string of input data (a line) and produces a Packet. In all I am only going to provide for the Type 'G' and leave the rest to you.

// readPacket processes the input string pszBuffer and fills pPacket accordingly.
//
// pszBuffer should be a string of characters delimited by \r\n.
// pPacket is a pointer to a Packet which is filled by this function.
//
// returns 1 if packet is valid, 0 otherwise.    
byte readPacket(Packet* pPacket, const char* pszBuffer) {
  byte isvalid = 0;
  if (pszBuffer && pPacket) {
    char tmp[10];
    char* psz = (char*)pszBuffer;
    char* pszTmp = 0;
    byte len = 0;
    // initialize packet
    pPacket->type = Type::NoType;
    pPacket->unit = Unit::NoUnit;
    pPacket->valid = 0;
    pPacket->expectUnit = 0;
    // get the Type
    psz = processType(pPacket, psz);
    if (psz) {
      // skip any space characters now.       
      psz = trimLeadingSpaces(psz);
      psz = processValue(pPacket, psz);     
      // if Type handled, check units and finish.
      if (psz) {
        if (pPacket->expectUnit)
          pPacket->valid = processUnit(pPacket, psz);
        else
          pPacket->valid = 1;
      }

    } // Type not found

    isvalid = pPacket->valid;
  }
  return isvalid;
}

OK that's not a lot. The logic is

  • Get the packet Type
  • IFF a packet Type was found, get the packet Value
  • IFF a value was found, get the Unit if required

It's important to breakdown these steps in smaller, manageable functions. First, the code for getting the packet Type.

// processType
// Sets pPacket->type to the type found in psz.  Type is delimited by a colon (:)
// Returns 0 (null) if a known Type wasn't found.
// Otherwise a ptr to the data past colon delimiter.
//
char* processType(Packet* pPacket, const char* psz) {
  // find the colon (:) delimiter and return what it leads to.
  byte len = 0;

  char* ptr = strchr((char*)psz, ':');
  char* pResult = 0;
  if (ptr) {
    // found the colon.
    char buffer[4];
    byte len = (ptr - psz) / sizeof(char); // pointer math to find length.    
    strncpy(buffer, psz, len);
    // null term buff
    buffer[len] = 0;
    // assume succcess; default will rest to fail.      
    pResult = (char*)psz + len + 1; // +1 to skip colon character.
    switch (buffer[0]) {
    case 'G':
      pPacket->type = Type::G;
      pPacket->expectUnit = 1; // unit should be given.
      break;
    default:
      pPacket->type = Type::NoType;
      pResult = 0; // fail, didn't find a known type.
    }
  }

  return pResult;
}

So processType sets up the packet Type and adds information about whether to look for Unit information later.

If processType() found a valid Type, the next step is to get the value based in that type with the function processValue

// Sets pPacket->value according to the packet Type.
// Returns null (0) if type not supported or value not found,
// otherwise a pointer to the remaining characters following the value handled.
char* processValue(Packet* pPacket, const char* psz) {
  char* ptr = 0;
  // value is construed by Type
  switch (pPacket->type) {
  case Type::G:
    ptr = processFloat(pPacket, psz);
    break;
    //
    // add your additonal Type implementations...
    //
  default:
    // invalid, set psz to null.
    ptr = 0;
  }
  return ptr;
}

Great. Now, let's look at a way to get the value for a float:

// Sets pPacket->value.dval 
// Returns 0 (null) if a value wasn't found.
// Otherwise a ptr past the last character of the decimal value.
char* processFloat(Packet* pPacket, const char* psz) {
  const char *float_chars = ".0123456789";
  char* ptr = (char*)psz;
  if (ptr) {
    // scan ptr until we don't find a match.
    byte index = 0;
    while (strchr(float_chars, (char)ptr[index]))
      index++;

    if (index) {
      //  get the float using a temporary buffer.
      //  dangerous! assuming this will not overrun!
      char buf[10];
      strncpy(buf, ptr, index);
      // null term buff
      buf[index] = 0;
      pPacket->value.dval = atof(buf);
      // advance ptr past the decimal characters for return valie.
      ptr += index;
    }
    else ptr = 0; // fail
  }
  return ptr;
}

Finally if all went well, the code should look for the Unit if expected (based on the Type)

// Sets pPacket->unit to the unit given in psz.
// Returns 0 if no unit was found, non-zero otherwise.
byte processUnit(Packet* pPacket, const char* psz) {
  pPacket->unit =  Unit::NoUnit;
  if (psz) {
    switch ((char)*psz) {
    case 'k':
      pPacket->unit = Unit::uKg;
      break;
    case 'g':
      pPacket->unit = Unit::uG;
      break;
    default:
      break; // unit initialized to NoUnit.
    }
  }
  return pPacket->unit != Unit::NoUnit;
}

Here's a GIST of an arduino sketch using the code above.

Good luck, hope this helps. In the future, pose questions regarding processing on StackOverflow.

| improve this answer | |

Not the answer you're looking for? Browse other questions tagged or ask your own question.