-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)?

closed as unclear what you're asking by VE7JRO, sempaiscuba, gre_gor, Juraj, per1234 Jan 31 at 10:31

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • so you need to save strings? – Jaromanda X Jan 30 at 8:49
  • yes, i need to save that into 4 variable represent each line – El Kazma Jan 30 at 10:11
  • what did you try? – Juraj Jan 30 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.

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