Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.
|
Forum Index : Microcontroller and PC projects : Serial communications
Page 1 of 2 | |||||
Author | Message | ||||
lew247 Guru Joined: 23/12/2015 Location: United KingdomPosts: 1676 |
I'm trying to write code to read a sensor that sends data by serial at 9600 baud but I don't understand some things. I've looked on here and searched Google but was not able to find anything that explained it in simple words I could understand The format of the data I'm trying to decode is If I'm right then shouldn't character 31 start with BM? but it doesn't, it changes This is what it's decoding I'm trying to understand properly so I don't have to keep asking for help This is also getting really frustrating having to take pictures of what I want to say so I can post it as the forum keeps saying it contains illegal ascii characters even when none of the ones I use are over 127 most times |
||||
TassyJim Guru Joined: 07/08/2011 Location: AustraliaPosts: 5903 |
For the checksum, add all the individual bytes together then take the lower 16 bits of the result. It might help if you tell us what the sensor is so we can look at the data sheet. Jim VK7JH MMedit MMBasic Help |
||||
TassyJim Guru Joined: 07/08/2011 Location: AustraliaPosts: 5903 |
This might be what you are after but without and datasheets, I am guessing. OPTION EXPLICIT OPTION DEFAULT NONE dim receivedData$ ' this is the string we received dim integer info(13) ' this is an array that will contain the 13 values we are looking for sub getStuff receivedData$ local integer tempData(32), n, datalength,checksum, check_high, check_low ' copy the string data into a numeric array for n = 1 to len(receivedData$) tempData(n) = asc(mid$(receivedData$,n,1)) next n ' check to see if the starting values are "BM" if tempData(1) = asc("B") and tempData(2) = asc("M") then print "OK so far" ' the next two bytes tell us how long the data packet is. It should alwas be = 28 (2*13 + 2) dataLength = tempData(3)*256+tempData(4) ' tempData(3) should be 0 and tempData(4) = 28 if dataLength = 28 then print "Still looks good" ' now we do a checksum by adding all the bytes and compare them with the checksum that was sent for n = 1 to 30 checksum = checksum + tempData(n) next n check_low = checksum and 255 check_high = (checksum /256) and 255 if check_high = tempData(31) and if check_low = tempData(32) then print "check sum agrees!!!" ' if everything has gone well, it is safe to believe the data ' now we combine the high and low bytes for each of the 13 values for n = 1 to 13 info(n) = tempData(n*2+3)*256+tempData(n*2+4) next n ' print the results for n = 1 to 13 print info(n);","; next n end sub Receive the data and send it to the SUB. Jim VK7JH MMedit MMBasic Help |
||||
lew247 Guru Joined: 23/12/2015 Location: United KingdomPosts: 1676 |
Thanks Jim but I couldn't get it working, even after removing the if after the and if check_low This seems to work but I'm not certain it's returning the correct values I can't figure out how to get the Checksum calculated I know it's probably extremely messy compared to what people who know what they are doing would produce and needs tidying up. Its the PMS5003 air quality sensor Datasheet OPTION EXPLICIT OPTION DEFAULT NONE DIM a$,b$ DIM as Integer c,d,e,f,g,h,i,j,k,l,m,n,o,p,particles_03um2,particles_05um2 DIM as Integer particles_10um2,particles_25um2,particles_50um2,particles_100um2 Open "COM1:9600" As #1 Do a$ = INPUT$(1,#1) b$ = b$ + a$ IF Len(b$) = 32 Then ' valid data packets contain 32 characters decode b$ = "" End IF Loop sub decode IF left$(b$,2) = "BM" then ' Only proceed if probable Valid data is received c = asc(mid$(b$,3)) 'Should be 0 d = asc(mid$(b$,4)) 'Should be 28 if c + d = 28 then ' data length is correct we probably have valid data 'do checksum here from asc(mid$(b$,31)) and asc(mid$(b$,32)) 'IF CHECKSUM = PREVIOUS CHECKSUM DISREGARD READING WE ONLY WANT DATA WITHOUT 2 CONSECUTIVE CHECKSUMS because unit sends data every second but only updates the readings every 2.3 seconds therefore we only want new data to display 'Only need values from Data7 high to Data12 low e = asc(mid$(b$,17)) 'Data7 high f = asc(mid$(b$,18)) 'Data 7 low g = asc(mid$(b$,19)) 'Data 8 high h = asc(mid$(b$,20)) 'Data 8 low i = asc(mid$(b$,21)) 'Data 9 high j = asc(mid$(b$,22)) 'Data 9 low k = asc(mid$(b$,23)) 'Data 10 high l = asc(mid$(b$,24)) 'Data 10 low m = asc(mid$(b$,25)) 'Data 11 high n = asc(mid$(b$,26)) 'Data 11 low o = asc(mid$(b$,27)) 'Data 12 high p = asc(mid$(b$,28)) 'Data 12 low print e,f,g,h,i,j,k,l,m,n,o,p ' print all values to check particles_03um2 = e + f 'value I'm **ASSUMING** we add the values? particles_05um2 = g + h 'value particles_10um2 = i + j 'value particles_25um2 = k + l 'value particles_50um2 = m + n 'value particles_100um2 = o + p 'value print "Particles > 0.3um / 0.1L air: " STR$(particles_03um2) print "Particles > 0.5um / 0.1L air: " STR$(particles_05um2) print "Particles > 1.0um / 0.1L air: " STR$(particles_10um2) print "Particles > 2.5um / 0.1L air: " STR$(particles_25um2) print "Particles > 5.0um / 0.1L air: " STR$(particles_50um2) print "Particles > 10.0 um / 0.1L air: " STR$(particles_100um2) END IF END IF END SUB I found this on how to calculate the checksum but I don't understand it Also this Edited 2021-01-27 00:42 by lew247 |
||||
TassyJim Guru Joined: 07/08/2011 Location: AustraliaPosts: 5903 |
I don't have the same type of sensor so you will have to give me something to work with OPTION EXPLICIT OPTION DEFAULT NONE DIM a$,b$ DIM as Integer c Open "COM1:9600" As #1 Do a$ = INPUT$(1,#1) b$ = b$ + a$ IF Len(b$) = 32 Then ' valid data packets contain 32 characters decode b$ = "" End IF Loop sub decode IF left$(b$,2) = "BM" then ' Only proceed if probable Valid data is received for c = 1 to 32 print asc(mid$(b$,c)) next c endif end sub That will print out the 32 values received. Give me a few sets. Regarding the readings, a PM10 includes all particles less than 10 so it should already include the PM2.5 reading as well. Have a read of this thread. Its about a different sensor but might help you understand them. https://www.thebackshed.com/forum/ViewTopic.php?TID=11922&PID=143341#143341#143335 VK7JH MMedit MMBasic Help |
||||
TassyJim Guru Joined: 07/08/2011 Location: AustraliaPosts: 5903 |
If you have trouble getting the data, it might need to be synchronized to the start of the data stream. If you start reading halfway through, you will never see 'BM' at the start. This change is one way to bring the data stream into alignment. OPTION EXPLICIT OPTION DEFAULT NONE DIM a$,b$ DIM as Integer c Open "COM1:9600" As #1 Do a$ = INPUT$(1,#1) b$ = b$ + a$ if len(b$) > 2 and left$(b$) <>"BM" then b$ = mid$(b$,2) ' synchronise the data stream IF Len(b$) = 32 Then ' valid data packets contain 32 characters decode b$ = "" End IF Loop sub decode IF left$(b$,2) = "BM" then ' Only proceed if probable Valid data is received for c = 1 to 32 print asc(mid$(b$,c)) next c endif end sub VK7JH MMedit MMBasic Help |
||||
lew247 Guru Joined: 23/12/2015 Location: United KingdomPosts: 1676 |
No problem reading the data, and it's confirming the first 2 bytes are correct My issue is it has 32 Bytes and each one of the ones I need consist of 16 bits 8 high and 8 low I don't know if I#m meant to add the ascii values for the high and low bits to get the correct data, or what to do with it for example below offset 12 which is Data 26 (16 bit) Do I simple add the 8 upper and 8 lower bits together to get the byte value? it's ascii values received if that helps Also I really cannot get how to generate the checksum As well as checksum, in binary format (its fairly easy to parse the binary format, but it doesn't come out as pure readable ascii text) Adafruit have a tutorial here but it doesn't tell me in plain english what I need to know |
||||
lizby Guru Joined: 17/05/2016 Location: United StatesPosts: 3015 |
You have to add +all+ bytes--30 in total--2 start bytes, 2 frame bytes, 26 data bytes. PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on fruitoftheshed |
||||
TassyJim Guru Joined: 07/08/2011 Location: AustraliaPosts: 5903 |
You have to compare your calculated checksum with the devices checksum for n = 1 to 30 checksum = checksum + asc(mid$(b$,n)) next n checksum_sent = asc(mid$(b$,31))*256 + asc(mid$(b$,32)) if checksum = checksum_sent then print "Yippee!!!!" to get your values, you multiply the high byte by 256 and add the low byte. VK7JH MMedit MMBasic Help |
||||
TassyJim Guru Joined: 07/08/2011 Location: AustraliaPosts: 5903 |
I took one of the strings shown in your first photo and reconstructed the string. ^@ is zero and ^\ is 28 The rest are as expected ^B = 2, ^D = 4 etc. I didn't try and guess what some of the >128 characters in the first string are. (It would have been easier with some sample data) In Australia, we use PM2.5 and PM10 so I looked it them. Both were 4 which is reasonable for indoors. I haven't studied the smaller particle sizes so will not make any comment on those values. By reading the string from DATA statements instead of the serial port, this is what I ended up with. There is a difference of 256 between the check-sums. 256 is interesting and I would like to see more packets to see if that is a constant error/difference. OPTION EXPLICIT OPTION DEFAULT NONE DIM a$,b$ dim integer cs, css DIM as Integer c,d,e,f,g,h,i,j,k,l,m,n,o,p,particles_03um2,particles_05um2 DIM as Integer particles_10um2,particles_25um2,particles_50um2,particles_100um2 ' Open "COM1:9600" As #1 ' Do ' a$ = INPUT$(1,#1) ' b$ = b$ + a$ ' if len(b$) > 2 and left$(b$) <>"BM" then b$ = mid$(b$,2) ' synchronise the data stream ' IF Len(b$) = 32 Then ' valid data packets contain 32 characters ' decode ' b$ = "" ' End IF ' Loop ' sample data taken from image ^@ = 0 and ^\ = 28 for n = 1 to 32 read p b$ = b$+chr$(p) next n decode print "PM2.5 = ";asc(mid$(b$,7,1))*256+asc(mid$(b$,8,1)) print "PM10 = ";asc(mid$(b$,9,1))*256+asc(mid$(b$,10,1)) end sub decode IF left$(b$,2) = "BM" then ' Only proceed if probable Valid data is received c = asc(mid$(b$,3)) 'Should be 0 d = asc(mid$(b$,4)) 'Should be 28 if c*256 + d = 28 then ' data length is correct we probably have valid data css =asc(mid$(b$,31,1))*256+asc(mid$(b$,32,1)) print "supplied check sum = ";css for n = 1 to 30 cs = cs + asc(mid$(b$,n,1)) next n print "calculated check sum = ";cs print "difference = ";cs-css 'do checksum here from asc(mid$(b$,31)) and asc(mid$(b$,32)) 'IF CHECKSUM = PREVIOUS CHECKSUM DISREGARD READING WE ONLY WANT DATA WITHOUT 2 CONSECUTIVE CHECKSUMS because unit sends data every second but only updates the readings every 2.3 seconds therefore we only want new data to display 'Only need values from Data7 high to Data12 low e = asc(mid$(b$,17)) 'Data7 high f = asc(mid$(b$,18)) 'Data 7 low g = asc(mid$(b$,19)) 'Data 8 high h = asc(mid$(b$,20)) 'Data 8 low i = asc(mid$(b$,21)) 'Data 9 high j = asc(mid$(b$,22)) 'Data 9 low k = asc(mid$(b$,23)) 'Data 10 high l = asc(mid$(b$,24)) 'Data 10 low m = asc(mid$(b$,25)) 'Data 11 high n = asc(mid$(b$,26)) 'Data 11 low o = asc(mid$(b$,27)) 'Data 12 high p = asc(mid$(b$,28)) 'Data 12 low print e,f,g,h,i,j,k,l,m,n,o,p ' print all values to check particles_03um2 = e*256 + f 'value I'm **ASSUMING** we add the values? particles_05um2 = g*256 + h 'value particles_10um2 = i*256 + j 'value particles_25um2 = k*256 + l 'value particles_50um2 = m*256 + n 'value particles_100um2 = o*256 + p 'value print "Particles > 0.3um / 0.1L air: " STR$(particles_03um2) print "Particles > 0.5um / 0.1L air: " STR$(particles_05um2) print "Particles > 1.0um / 0.1L air: " STR$(particles_10um2) print "Particles > 2.5um / 0.1L air: " STR$(particles_25um2) print "Particles > 5.0um / 0.1L air: " STR$(particles_50um2) print "Particles > 10.0 um / 0.1L air: " STR$(particles_100um2) END IF END IF END SUB data 66,77, 0,28,0,2,0,4,0,4,0,2,0,4,0,4 data 2,97 ,0,24,0,28,0,2,0,0,0,0,23,0,2,111 Jim VK7JH MMedit MMBasic Help |
||||
Turbo46 Guru Joined: 24/12/2017 Location: AustraliaPosts: 1593 |
@ Jim, Could there be a problem in reconstructing the string? @ Lew, Maybe you could add something like the code in red below to Jim's code (or your own) so you could supply some usable data? You should be able to paste that into the forum. for n = 1 to 32 read p b$ = b$+chr$(p) next n for n = 1 to 32 print asc(mid$(b$,n,1)); : print ","; next n decode Bill Keep safe. Live long and prosper. |
||||
TassyJim Guru Joined: 07/08/2011 Location: AustraliaPosts: 5903 |
Yes, I had to guess a few bytes but apart from the one high-byte difference, the rest makes sense. That's why I would have liked more data to play with. Jim VK7JH MMedit MMBasic Help |
||||
lew247 Guru Joined: 23/12/2015 Location: United KingdomPosts: 1676 |
Sorry about the time difference and delays. Where did you get the *256? In the datasheet it says Check code=Start character1+Start character2+........+data13Low 8 bits Could that mean add characters 1 - 12 + data 13 low bit? then if it's the same as characters 31 and 32 it's valid? OR does it mean add characters 1 + 2 + data 12 low 8 bits? As the first 4 bytes are always 66,77,0,28 in valid data and I guess I could use that instead of the acual checksum sent to ensure valid data? it's just the fact I can't figure out how the checksum is calculated that's bugging me. I don't "need" all the returned values, it's only to show the air quality in the room here with me so PM2.5 and PM10 are fine I plan on using colours with the numbers to see if I need to turn the air filter on or not, I have asthma and dust particles doesn't help at all. What I would like to do but not sure how is to compare the checksum with the previous checksum and if it's identical then ignore the data and wait for the next read I only want it to update when the data changes. Data as requested below the code This code works but it's for the arduino boolean readPMSdata(Stream *s) { copied from https://how2electronics.com/interfacing-pms5003-air-quality-sensor-arduino/ if (! s->available()) { return false; } // Read a byte at a time until we get to the special '0x42' start-byte if (s->peek() != 0x42) { s->read(); return false; } // Now read all 32 bytes if (s->available() < 32) { return false; } uint8_t buffer[32]; uint16_t sum = 0; s->readBytes(buffer, 32); // get checksum ready for (uint8_t i=0; i<30; i++) { sum += buffer[i]; } /* debugging for (uint8_t i=2; i<32; i++) { Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", "); } Serial.println(); */ // The data comes in endian'd, this solves it so it works on all platforms uint16_t buffer_u16[15]; for (uint8_t i=0; i<15; i++) { buffer_u16[i] = buffer[2 + i*2 + 1]; buffer_u16[i] += (buffer[2 + i*2] << 8); } // put it into a nice struct :) memcpy((void *)&data, (void *)buffer_u16, 30); if (sum != data.checksum) { Serial.println("Checksum failure"); return false; } // success! return true; } data as requested, hopefully it's enough last question do I add the bytes together to get the values? in the last data line that would be 1,3,3,1,3,3,60.165,26,2,0 and 79 is the checksum? Edited 2021-01-27 19:12 by lew247 |
||||
Turbo46 Guru Joined: 24/12/2017 Location: AustraliaPosts: 1593 |
The data consists of 16 bits. The low byte contains the value 0-255. The high byte holds the values of 256 and up. To get the 16 bit value you must multiply the high byte by 256 and add it to the low byte to get the full 16 bit value. That is a bit confusing but I think it does mean to add all of the bytes from Start character1 to the data byte13low to get the checksum which is a 16 bit number. As Jim has done. No. As above you must multiply the high byte by 256 and add it to the low byte to get the 16 bit value. So the checksum is: (2 * 256) + 77 = 589 decimal. Check out Jim's code and try to understand it. I haven't put your data into Jim's program yet. Bill Keep safe. Live long and prosper. |
||||
Turbo46 Guru Joined: 24/12/2017 Location: AustraliaPosts: 1593 |
Addendum, I plugged the last line of your data into Jim's program and it gave the correct result - well the checksum was correct. Bill Keep safe. Live long and prosper. |
||||
lew247 Guru Joined: 23/12/2015 Location: United KingdomPosts: 1676 |
I get what you're saying, that the checksum works BUT it's not the same as comparing it to the checksum sent by the device? The checksum send by the device is asc(mid$(b$,31,1))+asc(mid$(b$,32,1)) Surely the checksum generated should match the checksum sent? in Jim's code if c*256 + d = 28 then ' data length is correct we probably have valid data but C ALWAYS = 0 and ALWAYS = 28 with valid data so doing c*256 will always give the same result In the arduino code there is no multiplying *256 IF I understand this correctly // Now read all 32 bytes if (s->available() < 32) { return false; } Get data fromINPUT$(1,#1) uint8_t buffer[32]; uint16_t sum = 0; s->readBytes(buffer, 32); Read 32 bytes into the buffer // get checksum ready for (uint8_t i=0; i<30; i++) { sum += buffer[i]; } If buffer is less than 30 then loop /* debugging for (uint8_t i=2; i<32; i++) { Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", "); } Serial.println(); */ Print the values (hex??) of the buffer? THIS IS THE BIT I DO NOT UNDERSTAND // The data comes in endian'd, this solves it so it works on all platforms uint16_t buffer_u16[15]; for (uint8_t i=0; i<15; i++) { buffer_u16[i] = buffer[2 + i*2 + 1]; buffer_u16[i] += (buffer[2 + i*2] << 8); } No idea what's going on above - aparently << means shift left by 8 bits but that means nothing to me Edited 2021-01-27 22:25 by lew247 |
||||
Turbo46 Guru Joined: 24/12/2017 Location: AustraliaPosts: 1593 |
The checksum sent by the device is high byte 2, low byte 77. (2 * 256) + 77 = 589 decimal. It does agree with the calculated checksum. Take a 16 bit binary word x = 0000000000000001 and shift it left 8 times and you get 000000010000000. That equals 256. instead of using (x * 256) you could just as easily use (x<<8) which would give the same result. Bill Keep safe. Live long and prosper. |
||||
lew247 Guru Joined: 23/12/2015 Location: United KingdomPosts: 1676 |
Surely this can't be right? If that is the case then with this data 66 77 0 28 0 31 0 43 0 48 0 26 0 38 0 47 22 77 6 77 0 230 0 17 0 6 0 2 151 2 3 226 then the PM10 particle count is data 10* 256 + data 9 which is 48*256+0 = 12288 PM2.5 particle count is data 8* 256 + data 7 which is 43*256+1 = 11088 PM1.0 particle count is data 6* 256 + data 5 which is 31*256+0 = 7936 Those values seem really high? Especially for indoors |
||||
Turbo46 Guru Joined: 24/12/2017 Location: AustraliaPosts: 1593 |
Sorry but I see the data for PM10 as 0, 48. PM 2.5 = 0, 43 PM 1.0 = 0, 31 I don't see where you get your values from. Bill Keep safe. Live long and prosper. |
||||
lew247 Guru Joined: 23/12/2015 Location: United KingdomPosts: 1676 |
The data consists of 16 bits. The low byte contains the value 0-255. The high byte holds the values of 256 and up. To get the 16 bit value you must multiply the high byte by 256 and add it to the low byte to get the full 16 bit value. Bill |
||||
Page 1 of 2 |
Print this page |