How to make an IoT protocol (parsing, answering)?

Hi Colin, thanks for this... I already identified the problem between , and ; ... but even with that mistake corrected, as there is also space between symbols on https://www.w3schools.com/js/js_loop_for.asp, it is impossible to understand from what exactly is coming the real problem.. as there is no debug messages anymore in my nodered
Yesterday, I'be been able to get the splited messages I sent you when I excluded your :

let i;
...
}

I can look forward now

The space after the ; is not important, if that is what you mean, nor is it important round operators such as = or +

Unfortunatly, the result is not ok as for :

[{"id":"38fd6d46.07d862","type":"tab","label":"IWAP02","disabled":false,"info":""},{"id":"4a330da4.5ac7b4","type":"inject","z":"38fd6d46.07d862","name":"TEST IWAP02#","topic":"","payload":"IWAP02,zh_cn,0,7,460,0,9520|3671|13,9520|3672|12,9520|3673|11,9520|3674|10,9520|367 5|9,9520|3676|8,9520|3677|7#","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":114,"y":132.7999906539917,"wires":[["b13eed31.409e9"]]},{"id":"b13eed31.409e9","type":"switch","z":"38fd6d46.07d862","name":"SORT INCOMING","property":"payload","propertyType":"msg","rules":[{"t":"regex","v":"^IWAP02","vt":"str","case":false}],"checkall":"true","repair":false,"outputs":1,"x":381.00000381469727,"y":143.8000030517578,"wires":[["97b7209d.3d45"]]},{"id":"168c17f3.e614a8","type":"comment","z":"38fd6d46.07d862","name":"INCOMING MESSAGES FROM THE WATCH","info":"","x":177.3000030517578,"y":22,"wires":[]},{"id":"e6d39b5d.4017e8","type":"comment","z":"38fd6d46.07d862","name":"OUTGOING MESSAGES TO THE WATCH","info":"","x":184.00000762939453,"y":355.8000297546387,"wires":[]},{"id":"f8900c11.a7614","type":"change","z":"38fd6d46.07d862","name":"IWBP02# ","rules":[{"t":"set","p":"payload","pt":"msg","to":"IWBP02# ","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":97.00000762939453,"y":428.80000591278076,"wires":[["2817006d.81197"]]},{"id":"2817006d.81197","type":"debug","z":"38fd6d46.07d862","name":"outgoing debug","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":864.3000106811523,"y":354.2000045776367,"wires":[]},{"id":"21466e3.35e1792","type":"change","z":"38fd6d46.07d862","name":"IWBP02 LBS ADDRESS","rules":[{"t":"set","p":"payload","pt":"msg","to":"ADDRESS NEEDED","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":152.00000762939453,"y":492.0000057220459,"wires":[["2817006d.81197"]]},{"id":"9cd8efdf.edd82","type":"inject","z":"38fd6d46.07d862","name":"TEST IWAP02_ADDRESS","topic":"","payload":"IWAP02,zh_cn,1,7,460,0,9520|3671|13,9520|3672|12,9520|3673|11,9520|3674|10,9520|367 5|9,9520|3676|8,9520|3677|7,4,1|D8-24-BD-79-FA-1F|59&2|3C-46-D8-6D-CE-01|81&3|0C-4C-39-1A-7C-65|69&4|70-A8-E3-5D-D7-C0|65#","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":143,"y":211.00000381469727,"wires":[["b13eed31.409e9"]]},{"id":"97b7209d.3d45","type":"function","z":"38fd6d46.07d862","name":"split flag 0/1","func":"// flag 0 to split\n//IWAP02,zh_cn,0,7,460,0,9520|3671|13,9520|3672|12,9520|3673|11,9520|3674|10,9520|3675|9,9520|3676|8,9520|3677|7#\nlet answer = {} \nlet elements = msg.payload.split(\",\")  // this gives an array containing the bits\nanswer.command = elements[0]                       // IWAP02\nanswer.languageNotice = elements[1]                // zh_cn\nanswer.flag = elements[2]                          // 0\nnode.warn(`flag is: ${answer.flag}`)               // warning flag\nanswer.baseCount = elements[3]                     // 7\n// you can work out how to put the node.warn statements in yourself\nanswer.mcc = elements[4]                           // 460\nanswer.mnc = elements[5]                           // 7\n// now need to get the bases\nlet i;\nanswer.bases = []\nfor (i=6; i<6+answer.baseCount; i++) {\n  let baseElements = elements[i].split(\"|\")\n  answer.push({lac: baseElements[0], cid: baseElements[1], dbm: baseElements[2]})\n}\n// the rest needs to go in here\nmsg.payload = answer\nreturn msg","outputs":2,"noerr":0,"x":713.3000068664551,"y":144.20001411437988,"wires":[["343e90e6.de5b1"],[]],"outputLabels":[" 0","1"]},{"id":"343e90e6.de5b1","type":"debug","z":"38fd6d46.07d862","name":"incoming debug","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":723.0000114440918,"y":209.00000286102295,"wires":[]}]

The error :

13/02/2019 Ă  17:10:100:
** [node: split flag 0/1]function : (error)**
"TypeError: answer.push is not a function"

I've also lost all the others infos ( command / languageNotice, mcc, mnc, ...) if I'm using :
let i;
answer.bases = []
for (i=6; i<6+answer.baseCount; i++) {
** let baseElements = elements[i].split("|")**
** answer.push({lac: baseElements[0], cid: baseElements[1], dbm: baseElements[2]})**
}

In the code you gave me you also wrote :
// the rest needs to go in here... What dou you mean Colin?

Thanks...

Sorry, that should be answer.bases.push( It pushes the new base onto the end of the array answer.bases
The others won't appear because it failed before it got as far as sending the answer.
When I said the rest goes here I meant you can work out the rest of the elements of that message yourself, now that you have learnt a bit of javascript. It is just more of what has gone before at the start of the function.

Let's try Colin but still errors :

function : (error)

"TypeError: Cannot read property 'split' of undefined"

I forgot to ask :
1/ you splited with ,
2/ you are doing a loop to split with |

Is there still something to repeat as there still , after | in the code ?

The error is because for getting the base count I put
answer.baseCount = elements[3]
but it should be
answer.baseCount = Number(elements[3])
That is because we need the count as a number 7 not the letter 7 so we can add 6 to it to do the test.
While looking at that I see that I think you have an error in your inject strings, you have a space in the middle that should be a comma.

The initial split is on comma because comma splits it into
IWAP02
zh_cn
0
7
460
0
9520|3671|13
9520|3672|12
and so on.
Then the split using | splits 9520|3671|13 into the three separate parts.

Thanks!!! You are a genius!

Hi Colin, coming back to Node-Red, for split command

You wrote previously... "then I suggest doing it after the function node. You can use msg.payload.flag to switch on". As usually, I understand what is the process (if flag = 0 then => output 1, if flag = 1 then => output 2)
But do you mean this command needs to be included in the present function node or have I to use in another node...?

// flag 0 to split
//IWAP02,zh_cn,0,7,460,0,9520|3671|13,9520|3672|12,9520|3673|11,9520|3674|10,9520|3675|9,9520|3676|8,9520|3677|7#
let answer = {}
let elements = msg.payload.split(",") // this gives an array containing the bits
answer.command = elements[0] // IWAP02
answer.languageNotice = elements[1] // zh_cn
answer.flag = elements[2] // 0
node.warn(flag is: ${answer.flag})
// warning flag
answer.baseCount = Number(elements[3]) // 7
// you can work out how to put the node.warn statements in yourself
answer.mcc = elements[4] // 460
answer.mnc = elements[5] // 7
// now need to get the bases
let i;
answer.bases = []
for (i=6; i<6+answer.baseCount; i++) {
let baseElements = elements[i].split("|")
answer.bases.push({lac: baseElements[0], cid: baseElements[1], dbm: baseElements[2]})
}
// the rest needs to go in here
msg.payload = answer
return msg

Add a Switch node after the function node. The switch node has the flad in msg.payload.flag, so you can switch if msg.payload.flag = 0 then o/p 1, if msg.payload.flag = 1 then o/p 2

Thks Colin!

There is still an important job on IWAP02 if the answer "flag = 1". But I need more infos about it.

So ... let's go for IWAP01 !!! :slight_smile: Here, it is the same same idea as IWAP02 ... but no need to setup the watch with the answer...

// flag A (valid)
//IWAP01080524A2232.9806N11404.9355E000.1061830323.8706000908000102,460,0,9520,3671#

where : IWAP01 = Command ; 080524 = 24th May 2008
“A”shows valid data,”V”shows invalid data and will call additional LBS data (same idea as in IWAP02 with flag = 0 / flag = 1) ; 2232.9806N11404.9355E = 22 degrees north latitude 32.9806 point ,114 degrees east longitude 04.9355 points ; 000.1 = moving speed ; 061830 = GMT 06:18:30 ; 323.87 = direction angle as 323.87° ; 060 = GSM signal ; 009 = number of satellites ; 080 = battery level ; 0 = remaining space , 01 = fortification state ; 02 = working mode ; 460 =mcc ; 0 =mnc ; 9520 = lac ; 3671 = cid

If state in GPS package is V (for invalid datas) or latitude and longitude are 0000.0000N00000.0000E, it will call additional LBS data (same idea as IWAP02)

// flag V (invalid)
//IWAP01080524V0000.0000N00000.0000E000.1061830323.8706000908000102,460,0,9520,3671,Home|74-DE-2B-44-88-8C|97&Home1|74-DE-2B-44-88-8C|97&Home2|74-DE-2B-44-88-8C|97&Home3|74-DE-2B-44-88-8C|97#

where : Home = SSID ; 74-DE-2B-44-88-8C = MAC address ; 97 = signal strength. Variables are separated by“|”, wifi information can be multiple sets and are separated by “&”.

I think I can use a part of your code for IWAP02 but I really don't know for the first splits...

That message is much more difficult to split up. Whoever designed that
Are all the numbers up to the first comma always the same number of digits? If so we can do it by extracting the right number of digits.

What do you mean by
"but no need to setup the watch with the answer..."

Of course, it will be always the same number of digits. The problem is just to convert 2232.9806N11404.9355E in decimal... but i found a code for this and I don't know how to integrate inside :

[{"id":"9f3e71b9.bc689","type":"tab","label":"GPS_TextTODecimal","disabled":true,"info":""},{"id":"3de636fc.7cbd1a","type":"debug","z":"9f3e71b9.bc689","name":"","active":true,"console":"false","complete":"false","x":830.0000114440918,"y":192.00000190734863,"wires":[]},{"id":"5dd83314.546c0c","type":"function","z":"9f3e71b9.bc689","name":"Convert NMEA to decimal","func":"//Example of data received:\n//GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,\n\nmessage0 = {};\nmessage1 = {};\nmessage2 = {};\nmessage3 = {};\nmessage4 = {};\nmessage5 = {};\nmessage6 = {};\nmessage7 = {};\nmessage8 = {};\nmessage9 = {};\nmessage10 = {};\nmessage11 = {};\nvar symblat = \"\";\nvar symblong = \"\";\n\n\nmsg.string = msg.payload.split(\",\");\n\n//Find if latitude is south and add minus\nif (msg.string[4] == \"S\") {\nsymblat = \"-\";\n} else {\n\n}\n\n//Find if longitude is west and add minus\nif (msg.string[6] == \"W\") {\nsymblong = \"-\";\n} else {\n\n}\n\nvar nvals = msg.payload.match(/,(\\d+)(\\d\\d\\.\\d+),(N|S)/);\nvar evals = msg.payload.match(/,(\\d+)(\\d\\d\\.\\d+),(E|W)/);\nmsg.payload = {\n    name:\"Position test\",\n    lat: symblat + (+nvals[1] + nvals[2]/60).toFixed(4),\n    lon: symblong + (+evals[1] + evals[2]/60).toFixed(4)\n}\n\nreturn [msg];","outputs":"1","noerr":0,"x":532,"y":191.00000190734863,"wires":[["3de636fc.7cbd1a"]]},{"id":"6bba3817.9c61a8","type":"inject","z":"9f3e71b9.bc689","name":"31.426391,S,64.191494,W","topic":"","payload":"GPRMC,161935.00,A,3125.58347,S,06411.48964,W,0.030,,221217,,,A*66","payloadType":"str","repeat":"","crontab":"","once":false,"x":181,"y":111.00000190734863,"wires":[["5dd83314.546c0c"]]},{"id":"a303b32b.20425","type":"inject","z":"9f3e71b9.bc689","name":"46.671974°N 71.734865°W","topic":"","payload":"$GPRMC,161935.00,A,4640.31845,N,07144.09187,W,0.030,,221217,,,A*66","payloadType":"str","repeat":"","crontab":"","once":false,"x":195,"y":201.00000190734863,"wires":[["5dd83314.546c0c"]]},{"id":"1810a5a2.3a852a","type":"comment","z":"9f3e71b9.bc689","name":"Córdoba, Argentine","info":"","x":160,"y":69.00000190734863,"wires":[]},{"id":"1389f752.21eea9","type":"comment","z":"9f3e71b9.bc689","name":"Donnacona, Canada","info":"","x":160,"y":157.00000190734863,"wires":[]},{"id":"b9895164.66836","type":"comment","z":"9f3e71b9.bc689","name":"Tokyo, Japon","info":"","x":141,"y":250.00000190734863,"wires":[]},{"id":"5495a5fd.d6181c","type":"inject","z":"9f3e71b9.bc689","name":"46.671974°N 71.734865°W","topic":"","payload":"$GPRMC,161935.00,A,3541.22155,N,13941.30142,E,0.030,,221217,,,A*66","payloadType":"str","repeat":"","crontab":"","once":false,"x":196,"y":295.00000190734863,"wires":[["5dd83314.546c0c"]]},{"id":"a335669c.b093c8","type":"comment","z":"9f3e71b9.bc689","name":"Sydney, Australie","info":"","x":147,"y":337.00000190734863,"wires":[]},{"id":"d3930048.93dea","type":"inject","z":"9f3e71b9.bc689","name":"33.879585°S 151.205577°E","topic":"","payload":"$GPRMC,161935.00,A,3352.7751,S,15112.33463,E,0.030,,221217,,,A*66","payloadType":"str","repeat":"","crontab":"","once":false,"x":196,"y":376.00000190734863,"wires":[["5dd83314.546c0c"]]}]

"What do you mean by
"but no need to setup the watch with the answer...""
= just told there is no particular answer including datas to produce here...

Sorry, still don't understand

Sorry, it is not important for the actual node... I just said that the answer for IWAP01 is the same whatever flag = A or flag = V It will be always IWBP01#

in contrary of the answer to write for IWAP02 / when flag = 1... Because, in this case, the answer is the biggest problem to solve compared to the others ...

Ok, understood. I haven't got any more time this evening, will try to have a look at decoding the lat/lon tomorrow.
If you wanted to make a start you could split it on commas initially, then in elements[0] will be the long string at the begining. This needs to be split using the substring function. That needs to be called multiple times to fetch each of the sections one at a time.

That initial part looks similar to NMEA (but isn't) so the flow you pointed at won't work as is...

2232.9806N11404.9355E

is 22 degress 32.9806 minutes North 114 degrees 04.9355 minutes East
so to get decimal degress you need

latitude = 22 + (32.9806/60) and longitude = 114 + (04.9355/60) 

and make them negative if S (if not N) and W (if not E)

Thanks dceejay...

So, the story starts like this ... but does not work correctly, because I suppose the substring have to be included somewhere... :slight_smile:

// flag A
//IWAP01080524A2232.9806N11404.9355E000.1061830323.8706000908000102,460,0,9520,3671#
// flag V
//IWAP01080524V0000.0000N00000.0000E000.1061830323.8706000908000102,460,0,9520,3671,Home|74-DE-2B-44-88-8C|97&Home1|74-DE-2B-44-88-8C|97&Home2|74-DE-2B-44-88-8C|97&Home3|74-DE-2B-44-88-8C|97#

let answer = {}
let elements = msg.payload.split(",")
answer.part1 = elements[0] // IWAP01080524A2232.9806N11404.9355E000.1061830323.8706000908000102
answer.mcc = elements[1] // 460
answer.mnc = elements[2] // 0
answer.lac = elements[3] // 9520
answer.cid = elements[4] // 3671

//answer.flag = elements[2]
//node.warn(flag is: ${answer.flag})
// warning flag
let i;
answer.bases = []
for (i=1; i<1+answer.cid; i++) {
let baseElements = elements[i].split("|")
answer.bases.push({lac: baseElements[0], cid: baseElements[1], dbm: baseElements[2]})
}
return msg

Well I have made a start on decoding the first section

[{"id":"cf907e47.07511","type":"inject","z":"bae6d5e2.bea09","name":"Invalid IWAP01","topic":"","payload":"IWAP01190215V0000.0000N00000.0000E000.1061830323.8706000908000102,460,0,9520,3671,Home|74-DE-2B-44-88-8C|97&Home1|74-DE-2B-44-88-8C|97&Home2|74-DE-2B-44-88-8C|97&Home3|74-DE-2B-44-88-8C|97#","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":124.5,"y":221,"wires":[["4ecc3207.8feb4c"]]},{"id":"4ecc3207.8feb4c","type":"function","z":"bae6d5e2.bea09","name":"Split IWAP01","func":"let answer = {}\nlet elements = msg.payload.split(\",\")  // this gives an array containing the bits\n// elements[0] is the long string starting IWAP01\nlet dateStr = elements[0].substr(6,6)        // eg \"190215\" 15th Feb 2019\nanswer.date = new Date(Number(dateStr.substr(0,2))+2000, Number(dateStr.substr(2,2))-1, dateStr.substr(4,2))\nanswer.valid = (elements[0].substr(12,1) === \"A\")   // true or false\nif (answer.valid) {\n    let degreesN = Number(elements[0].substr(13,2))\n    let minutesN = Number(elements[0].substr(15,7))\n    let ns = elements[0].substr(22,1)      // N or S\n    let degreesE = Number(elements[0].substr(23,3))\n    let minutesE = Number(elements[0].substr(26,7))\n    let ew = elements[0].substr(33,1)      // E or W\n    node.warn(`${degreesN}  ${minutesN}  ${ns}  ${degreesE}  ${minutesE} ${ew}`)\n    answer.latitude = degreesN + minutesN/60\n    if (ns === \"S\") answer.latitude = -answer.latitude\n    answer.longitude = degreesE + minutesE/60\n    if (ew === \"W\") answer.longitude = -answer.longitude\n} else {\n    answer.latitude = 0\n    answer.longitude = 0\n}\n\nmsg.payload = answer\nreturn msg","outputs":1,"noerr":0,"x":310.5,"y":162,"wires":[["c5195512.cf66e"]]},{"id":"c5195512.cf66e","type":"debug","z":"bae6d5e2.bea09","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":489.5,"y":161,"wires":[]},{"id":"9d29ac9c.9ba0f","type":"inject","z":"bae6d5e2.bea09","name":"Valid IWAP01","topic":"","payload":"IWAP01190215A2232.9806N11404.9355E000.1061830323.8706000908000102,460,0,9520,3671#","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":116,"y":124,"wires":[["4ecc3207.8feb4c"]]}]