Need to convert ASCII Dec serial data stored as a string to ASCII characters stored as a string and push to network connection SSID & network password

I have created an app for a project. In this project, the app connects via Bluetooth to Argon and sends network info to, hopefully, initializing it’s network parameters.
I need to convert ASCII Dec serial data stored as a string to ASCII characters stored as a string and push to network connection passing SSID & network password.
I have a string that is this:
83 83 73 68 58 72 111 109 101 78 101 116 119 111 114 107 124 80 87 68 58 49 53 70 69 54 51 67 56 66 52 52 65 54 49 57 50

Which is when sent via Serial.write:
SSID:HomeNetwork|PWD:15FE63C8B44A6192

I need to reconvert this numeric ASCII (83 83 73 68 58 72…) to characters (SSID:H). I can then split this up and store this SSID and network password into variables and then push those to the device’s network connection details.
So the device has the info on it. It is formatted to convert correctly (all decimal number/characters are within ASCII range: 32-122). I am still unable to get a proper conversion. Tried many different ways but still stumped as every attempt to convert either return a number, or an error. Has anyone hit this wall before? If so, please provide your working example or point to where one is… or at minimum, who to do this.

Lastly, eventually, when I have this converted data stored into string variables, I will need to have it initialize or replace network connection info on the Argon, most likely with the device name included. I am looking to have two devices shipped to a customer, including all operating code on each device’s to perform the necessary functions. Since I cannot determine the network info required, I chose to send it via Bluetooth to both devices and then have the device connect to the network. I see no other way for an end user without a complicated procedure.

Many, many, TIA’s as I have been stuck here for over a week.

You can try this

const char demo[] = "83 83 73 68 58 72 111 109 101 78 101 116 119 111 114 107 124 80 87 68 58 49 53 70 69 54 51 67 56 66 52 52 65 54 49 57 50";
char source[sizeof(demo)];
char dest[256];

void loop() {
  int i = 0;
  memcpy(source, demo, sizeof(source)); // copy const to mutable string (strtok will alter source)
                                        // tokenize string at each blank 
  for (char *tok = strtok(source, " "); tok && i < sizeof(dest)-1; tok = strtok(NULL, " ")) {
    dest[i++] = atoi(tok);              // convert substring to numeric value and store away
  };    
  dest[i] = '\0';                       // terminate string
    
  Serial.println(dest);
  delay(1000);
}
2 Likes

Hey Jeff,

there is this library from @Dan-Kouba that allows you to set the Wi-Fi credentials from an app, browser or your own app.

One thing that was discovered during the testing of such library, was that the Argon had to be running, meaning connected to the internet, or the library and settings wouldn’t work.

One thing I noted in this post here is:
The trick is to create a hotspot on your phone (with credentials sensor/sensor123 ), so the Argon can connect and the library can run.

Heads up, then.
Cheers,
Gustavo.

First of all, thanks for the input and time devoted to helping. The above code only prints the first two characters, over and over:
SS
SS
SS

It would not matter as I have mentioned initially, the data is in a string, not a const char array. I need the data converted, eventually, to a string and stored in a string variable such as “SSID:HomeNetwork|PWD:15FE63C8B44A6192”, not a serial as the code above leaves it. I am unaware how to get this converted to ASCII (ABCD) from numeric ASCII (65 66 67 68) and then store that data to a variable (ABCD). The data had initially arrived as Serial, in blocks of 20 chars, terminated. I then concatenated and stored this to a string after removing all termination characters. I could place the data into a newly formed const char array if necessary, from which, I believe, the corrected code above would be seen correctly via Serial in ASCII, but are unable to store this serial printed ASCII (ABCD) into a string variable as ASCII (ABCD). All attempts store as ASCII numeric (“83 83 73 68 58 72 111…”) not (“SSID:Hom…”). Still stuck in the same spot…

Thank you Gustovo (Champion!). This may be the method necessary if all else fails. Thank you very much. This is most helpful.

As for anyone else following this thread:

I am trying to make this as user friendly (idiot proof) a possible and this being the only we to do it, for Particle as a company, seems limiting, at a minimum. Not sure this is the way to go for the average individual. The average IQ in America is ~100. So if you are a person with an average IQ, you are smarter than half the people. Let’s say that is the threshold, 100. Of the remaining 50%, those above 100, how many are elderly? if 50% of those are above 42.85 (life span~73.7 (in 2020) yrs, 0 to age 12 excluded), it would be increasingly difficult, correspondingly with age, to perform a multi-step install. Perhaps the remaining 25% wouldn’t have a problem… but if there were a competing product with an easier setup, that competing product would most likely be not only their recommendation to others, but the product of their future purchases if additional purchases where necessary due to expansion or failure.

I find it difficult to believe that there is not a way to set these variables, from the device itself, when the SSID info data is already on the device itself, such as:

CoreSSID = “newSSID”;
CoreSSIDPW = “new SSODPW”;

I do not see the requirement to generate a hotspot on your phone, for an average user, user friendly. So what’s up Particle?

it's a catch 22 situation. You can't set whatever you want on the device since you need to connect to it to set that, but right there you can't connect if the device is not connected to the internet via Wi-Fi.

What's up? Photon 2 is up! :wink:
In other words, I hope that this will be solved by the Photon 2.

Cheers!

1 Like

First, sorry my code didn't do what you expected :blush:
That was a stupid mistake/typo. In the second strtok() call I had an empty terminator string ("") where it should have contained a blank (" ").
Corrected above - when you try to understand how the provided code works, it'd also be easy to spot the mistake :wink:

But for this

My code leaves the data in a "string variable" (dest) - just not in a "String variable/object" (or std::string for that matter).
In C/C++ a string is nothing other than a series of characters in memory terminated with a zero-character. Even String objects store their string contents in such an array.
But I personally refrain from using String objects wherever possible on these tiny devices as they may lead to heap fragmentation and hence instability over time.

1 Like

@ScruffR you’re the MAN :muscle:
it’s works perfect ! like a charm even on safari :stuck_out_tongue_winking_eye:
And strtok() function is what I was looking for a long long time !
Thanks
Always learning something new :+1:

1 Like

True, I totally get what your saying if there was only a wifi connection but, in this case, I have connected to the device via Bluetooth and transferred over the wifi parameters onto it. So I actually can connect to the device and transfer wifi info it without a network connection or having network setup. My question is relating to after. The after which is the issue. Thus the question “set these variables, from the device itself, when the SSID info data is already on the device itself”.

You mean this? WiFi.getCredentials()

With that you can list all the SSIDs already stored on the device. But not the network password as this would be a security risk.

Or when you want to add a new network: WiFi.setCredentials()

So to answer that

Nothing really, it's just a matter of searching and reading the already available docs :wink:

2 Likes

oh, I misunderstood.
Once the library gets the new credentials from the app, it will set them like @ScruffR mentioned.
Then if you wanted to reset the device (one way to force the device to connect to the new Wi-Fi) you can do this in the callback function like so:

Note: the delay(1000) is optional.

1 Like

That’s the missing info I needed before I even asked!!! Thank you very much gusgongenius.

You're welcome. That was funny.

That is what I was looking for to complete this project step, but still having problems. Please re-read first paragraph.

You mentioned:

I am still at stand still as I do not know how to manipulate the data. I wanted to convert to a string object as ASCII text. I could then split the data (I was going to use indexof) into first SSID, password (via "|" char), then split each of those with the ":" char. I would end up with 4 pieces of data, validate 1st piece of split data to be equal to "SSID" and then take split 2 for the WiFi.setCredentials( split2, split 4)
I cannot seem to access the ASCII info to do this... let alone store for use in "WiFi.setCredentials(ssid, password);". If this was a String object ("SSID:HomeNetwork|PWD:15FE63C8B44A6192"), I don't think I would have a problem doing this. No matter what I try, I get a non-compile error, many times without a reason for this unable to compile error.
As for "heap fragmentation and hence instability over time.", I am only doing this once on this device. Once it is hooked up to the local wifi with internet access, I should not need to run this again unless there was a change to the network credentials. It appears a reboot is necessary after setting the wifi credentials anyway (WiFi.setCredentials(ssid, password)), so this memory would be released.
You code above is functional but data still in numeric format (int), not ASCII text.

… as in this line:
" ```
dest[i++] = atoi(tok); // convert substring to numeric value and store away

I am sure it is my inexperience with C. As I mentioned, I have this is a string:

"83 83 73 68 58 72 111 109 101 78 101 116 119 111 114 107 124 80 87 68 58 49 53 70 69 54 51 67 56 66 52 52 65 54 49 57 50"

I was going to write my own function to do this. In PHP, for example, it would be something like this:

$testRef = "SSID:HomeNetwork|PWD:15FE63C8B44A6192";
$testData = "83 83 73 68 58 72 111 109 101 78 101 116 119 111 114 107 124 80 87 68 58 49 53 70 69 54 51 67 56 66 52 52 65 54 49 57 50";

$convertedText = convertToASCII($testData);

echo $convertedText . "<br>" . $testRef;

function convertToASCII($rawdata){
	$output = "";
	$asciiText = setCapsArr();
	$data = explode(" ", $rawdata);
	for($x = 0; $x < count($data); $x++){
		$output .= $asciiText[$data[$x]];
	}
	return $output;
}
function setCapsArr($casing){
	$caps = array();
	$caps[33] = "!"; $caps[35] = "#"; $caps[36] = "$"; $caps[37] = "%";
	$caps[38] = "&"; $caps[42] = "*"; $caps[64] = "@"; $caps[136] = "^";
	$caps[58] = ":"; $caps[124] = "|";
	$capDataRaw = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	$capData = str_split($capDataRaw);
	$smallDataRaw = strtolower($capDataRaw);
	$smallData = str_split($smallDataRaw);
	$start = 65; $y = 0;
	for($x = $start; $x < count($capData) + $start; $x++){
		$caps[$x] = $capData[$y]; $y++;
	}
	$start = 97; $y = 0;
	for($x = $start; $x < count($capData) + $start; $x++){
		$caps[$x] = $smallData[$y];	$y++;
	}
	$start = 48; $y = 0;
	for($x = $start; $x < 10 + $start; $x++){
		$caps[$x] = $y;	$y++;
	}
	return $caps;
}

which outputs this with a validation reference string:

SSID:HomeNetwork|PWD:15FE63C8B44A6192
SSID:HomeNetwork|PWD:15FE63C8B44A6192

…but, attempting this, converted to C, also gave compile errors.

Nope, my code above leaves you with a perfectly fine string.

With the info contained in the first part of the solution you can also solve this

  // code from above to get the converted data into "dest"
  char* dmy;
  char* SSID;
  char* PWD;
  dmy  = strtok(dest, ":|"); // first token "SSID" but don't keep  
  SSID = strtok(NULL, ":|"); // second token to store SSID
  dmy  = strtok(NULL, ":|"); // third token prefix "PWD" but don't keep
  PWD  = strtok(NULL, ":|"); // fourth token to store PWD

  Serial.printlnf("%s / %s", SSID, PWD);

And this is what the combined code could would then print

SSID:HomeNetwork|PWD:15FE63C8B44A6192
HomeNetwork / 15FE63C8B44A6192

Alternatively you could use strchr(dest, ':') to locate a colon in a string.
Another alternative would be to look out for 58 (':') and 124 ('|') inside the for() loop and instead of filling dest[] with the entire string feed the respective substrings into their respective character arrays directly.

1 Like

Thanks for your reply. You certainly know what you are doing! I appreciate the ‘training wheels’ as I apparently need more education in this area.
Fantastic split… yet… I still needed to validate first split. Best if I validated 1st and 3rd.
So changed to:

  char* SSID;
  char* PWD;
  char* IDVal; 
  char* PWVal;

  IDVal = strtok(dest, ":|"); // first token "SSID" need to validate
  SSID  = strtok(NULL, ":|"); // second token to store SSID
  PWVal = strtok(NULL, ":|"); // third token "PWD" should also validate
  PWD   = strtok(NULL, ":|"); // fourth token to store PWD
    
  Serial.printlnf("%s / %s / %s / %s", SSID, PWD, tik, tok);
  // I get: HomeNetwork / 15FE63C8B44A6192 / SSID / PWD
  delay(1000);
  if((IDVal=="SSID")&&(PWVal=="PWD")){
    WiFi.setCredentials(SSID, PWD);
  }

The ‘if’ statement does not validate to TRUE yet Serial.printlnf shows SSID & PWD
removing the if statement and the “WiFi.setCredentials(SSID, PWD);” does not work.
I most certainly need to study C. This is the last piece of this puzzle, TIA.

1 Like

C-strings cannot be tested for equality with the == operator as it would only check whether the pointers are pointing to the same location in memory.
Since both strings are not stored at the same location (i.e. the literals are stored in flash while the variables reside in RAM) the check has to fail - always.

To compare a string character by character you’d use strcmp()

So your check should rather look like this

  if (!strcmp(IDVal, "SSID") && !strcmp(PWVAL, "PWD"))

Side-note on the use of String:
I prefer using the C legacy functions to keep me conscious about what’s actually going on as close to the hardware as possible. Sure String objects make stuff somewhat easier but they also keep you “ignorant” about what’s going on behind the scenes (and also their overhead).
Once you know how the basics work you can also understand the more abstract stuff better - which is (mostly) not true the other way round tho’.
Black-boxes are useful but don’t help a lot once they don’t behave as expected :wink:
I like to know what’s going on inside such black-box to make an informed decision whether the benefits outweigh the cost for my use case.


full code to directly parse into substrings
enum TOKENS {
    PREFIX_SSID = 0,
    SSID,
    PREFIX_PWD,
    PWD
};
const char demo[] = "83 83 73 68 58 72 111 109 101 78 101 116 119 111 114 107 124 80 87 68 58 49 53 70 69 54 51 67 56 66 52 52 65 54 49 57 50";
char source[sizeof(demo)];
char data[4][64];

void loop() {
  int  i = 0;
  int  s = 0;
  char c = 0;
  memcpy(source, demo, sizeof(source)); // copy const to mutable string (strtok will alter source)
                                        // tokenize string at each blank 
  for (char *tok = strtok(source, " "); tok && i < sizeof(data[s])-1; tok = strtok(NULL, " ")) {
    c = atoi(tok);                      // convert substring to numeric value and store away
    if (c == ':' || c == '|') {
      data[s++][i] = '\0';
      i = 0;
      continue;
    }
    data[s][i++] = c; 
  };    
  data[s][i] = '\0';                   // terminate string

  for (s = 0; s < 4; s++)
    Serial.printf("%s ", data[s]);
  Serial.println();
  
  if (!strcmp(data[PREFIX_SSID], "SSID") && !strcmp(data[PREFIX_PWD], "PWD"))
    Serial.printlnf("setCredentials(\"%s\", \"%s\")", data[SSID], data[PWD]);
  delay(1000);
}

Here is another tidbit to try and marvel about :wink:

  const char dest[] = "SSID:HomeNetwork|PWD:15FE63C8B44A6192";
  char ssid[32];
  char pwd[32];
  if (2 == sscanf(dest, "SSID:%31[^|:]|PWD:%31[^|:]", ssid, pwd)) 
    Serial.printlnf("sscanfed setCredentials(\"%s\", \"%s\")", ssid, pwd);

sscanf() parses the data against a template and returns how good the incoming data was matched. Here we check whether the incoming data produced exactly two valid strings (^ not containing '|' or ':' - but no more than 31 characters).

4 Likes

Fantastic!! Thank you for your input (and output) on this matter. This was the hump I was having trouble getting over. I have not yet had a chance implement this, but can hardly wait to do so. I was unaware that you could put a function inside of a Serial.printlnf function, and have it execute it… that will help immensely. I did have a question regarding the parameters of the setCredentials. I see that additional parameters can be utilized such as WPA2, WPA, or WEP. If this information is not provided, is the Argon scripting intelligent enough to recognize which one of these is required? … or does it default to one of these. Most smart devices I have configured, such as plugs or lights, did not require this additional network parameter. Looks like I need to buy you dinner.