From 9dda5f1f229f7672e5dd0fbec36c63f3d28a2b58 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 15 Sep 2023 13:41:30 -0400 Subject: [PATCH 01/99] Update pm25_simpletest.py This adds a heading row for tiles. it also records a second time value for when the second sensor run. Make sure to test this on the sensors, as this is untested for now so it may have some marginal errors. --- PMS plantower/pm25_simpletest.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/PMS plantower/pm25_simpletest.py b/PMS plantower/pm25_simpletest.py index 55d9712..bad8c5a 100644 --- a/PMS plantower/pm25_simpletest.py +++ b/PMS plantower/pm25_simpletest.py @@ -15,7 +15,7 @@ # Kaleb Nails, nailsk@my.erau.edu # Marc Compere, comperem@erau.edu # created : 10 Feb 2023 -# modified: 06 Apr 2023 +# modified: 15 Sep 2023 """ Example sketch to connect to PM2.5 sensor with either I2C or UART. @@ -72,6 +72,14 @@ print("Found PM2.5 sensor, reading data...") +#adds heading columns +#Add Labels to the top of the file TEST this works +titleStr = ',Date Label, Dates (YMD), Sensor 1, pm10 standard, pm25 standard, pm100 standard, pm10 env, pm25 env, pm100 env,particles 03um, particles 05um, particles 10um, particles 25um, particles 50um, particles 100um, Dates (YMD), Sensor 2, pm10 standard, pm25 standard, pm100 standard, pm10 env, pm25 env, pm100 env,particles 03um, particles 05um, particles 10um, particles 25um, particles 50um, particles 100um' +file.write(titleStr +"\n") +file.flush() + + + while True: time.sleep(1) @@ -103,7 +111,7 @@ print("Particles > 5.0um / 0.1L air:", aqdata["particles 50um"]) print("Particles > 10 um / 0.1L air:", aqdata["particles 100um"]) print("---------------------------------------") - + dateStr1 =', Date:, {0}'.format(datetime.now()) # while False: time.sleep(1) @@ -137,7 +145,7 @@ print("Particles2 > 10 um / 0.1L air:", aqdata2["particles 100um"]) print("---------------------------------------") - dateStr =', Date:, {0}'.format(datetime.now()) + dateStr2 =', Date:, {0}'.format(datetime.now()) #dataStr1 = ', Data1:, {0}, {1}, {2}, {3}, {4}, {5}'.format(aqdata["particles 03um"],aqdata["particles 05um"],aqdata["particles 10um"],aqdata["particles 25um"],aqdata["particles 50um"],aqdata["particles 100um"]) #dataStr2 = ', Data2:, {0}, {1}, {2}, {3}, {4}, {5}'.format(aqdata2["particles 03um"],aqdata2["particles 05um"],aqdata2["particles 10um"],aqdata2["particles 25um"],aqdata2["particles 50um"],aqdata2["particles 100um"]) @@ -172,7 +180,7 @@ aqdata2["particles 25um"], \ aqdata2["particles 50um"], \ aqdata2["particles 100um"]) - file.write(dateStr + dataStr1 + dataStr2 + "\n") + file.write(dateStr1 + dataStr1 + dateStr2 + dataStr2 +"\n") file.flush() From 504920084ade558f2e3ea7da12035011cf2f247e Mon Sep 17 00:00:00 2001 From: erik Date: Fri, 15 Sep 2023 14:23:56 -0400 Subject: [PATCH 02/99] This commit is to add Alphasense python code to repo branch 1. --- Alphasense/OPC_Simple_v2.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Alphasense/OPC_Simple_v2.py diff --git a/Alphasense/OPC_Simple_v2.py b/Alphasense/OPC_Simple_v2.py new file mode 100644 index 0000000..1401049 --- /dev/null +++ b/Alphasense/OPC_Simple_v2.py @@ -0,0 +1,25 @@ +from time import sleep +from usbiss.spi import SPI +import opcng as opc + +spi = SPI('/dev/ttyACM0') +spi.mode = 1 +spi.max_speed_hz = 500000 +spi.lsbfirst = False + +dev = opc.detect(spi) + +print(f'device information: {dev.info()}') +print(f'serial: {dev.serial()}') +print(f'firmware version: {dev.serial()}') + +# power on fan and laser +dev.on() + +for i in range(10): + # query particle mass readings + sleep(1) + print(dev.pm()) + +# power off fan and laser +dev.off() \ No newline at end of file From 0da7b0dd6ffec58e6ef65d05597871094fcadac5 Mon Sep 17 00:00:00 2001 From: KalebNails Date: Fri, 6 Oct 2023 14:02:19 -0400 Subject: [PATCH 03/99] Found the function that pulls from all the bins --- Alphasense/OPC_Simple_v2.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Alphasense/OPC_Simple_v2.py b/Alphasense/OPC_Simple_v2.py index 1401049..7f0bbe8 100644 --- a/Alphasense/OPC_Simple_v2.py +++ b/Alphasense/OPC_Simple_v2.py @@ -19,7 +19,8 @@ for i in range(10): # query particle mass readings sleep(1) - print(dev.pm()) + #print(dev.pm()) + print(dev.histogram()) # power off fan and laser -dev.off() \ No newline at end of file +dev.off() From 1557fde220bb1a3fc56ada630ec2bba499f0dd7f Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 6 Oct 2023 14:35:16 -0400 Subject: [PATCH 04/99] Update README.md --- Alphasense/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Alphasense/README.md b/Alphasense/README.md index 8b13789..5f08850 100644 --- a/Alphasense/README.md +++ b/Alphasense/README.md @@ -1 +1 @@ - +This is code to read the alphasense through USB. There is also code to read it through the aurduino if thats possible. From 7e5ac58cbd006ba9571bcf215c177f78baa9e1e4 Mon Sep 17 00:00:00 2001 From: KalebNails Date: Fri, 6 Oct 2023 14:42:43 -0400 Subject: [PATCH 05/99] added the bin labels --- Alphasense/OPC_Simple_v2.py | 46 +++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/Alphasense/OPC_Simple_v2.py b/Alphasense/OPC_Simple_v2.py index 7f0bbe8..b4e0824 100644 --- a/Alphasense/OPC_Simple_v2.py +++ b/Alphasense/OPC_Simple_v2.py @@ -24,3 +24,49 @@ # power off fan and laser dev.off() + + + +#This is what the bins apparently are: +#_OPC_N3_HISTOGRAM_MODEL = [['Bin 0', 'H'], +# ['Bin 1', 'H'], +# ['Bin 2', 'H'], +# ['Bin 3', 'H'], +# ['Bin 4', 'H'], +# ['Bin 5', 'H'], +# ['Bin 6', 'H'], +# ['Bin 7', 'H'], +# ['Bin 8', 'H'], +# ['Bin 9', 'H'], +# ['Bin 10', 'H'], +# ['Bin 11', 'H'], +# ['Bin 12', 'H'], +# ['Bin 13', 'H'], +# ['Bin 14', 'H'], +# ['Bin 15', 'H'], +# ['Bin 16', 'H'], +# ['Bin 17', 'H'], +# ['Bin 18', 'H'], +# ['Bin 19', 'H'], +# ['Bin 20', 'H'], +# ['Bin 21', 'H'], +# ['Bin 22', 'H'], +# ['Bin 23', 'H'], +# ['Bin1 MToF', 'B'], +# ['Bin3 MToF', 'B'], +# ['Bin5 MToF', 'B'], +# ['Bin7 MToF', 'B'], +# ['Sampling Period', 'H'], +# ['SFR', 'H'], +# ['Temperature', 'H'], +# ['Relative humidity', 'H'], +# ['PM1', 'f'], +# ['PM2.5', 'f'], +# ['PM10', 'f'], +# ['#RejectGlitch', 'H'], +# ['#RejectLongTOF', 'H'], +# ['#RejectRatio', 'H'], +# ['#RejectOutOfRange', 'H'], +# ['Fan rev count', 'H'], +# ['Laser status', 'H'], +# ['Checksum', 'H']] From afe3cab5982ea880fdecb770d8939cb3b8461047 Mon Sep 17 00:00:00 2001 From: KalebNails Date: Fri, 6 Oct 2023 15:22:20 -0400 Subject: [PATCH 06/99] moving it to another computer, this version does not work --- Alphasense/OPC_Simple_v2.py | 40 ++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/Alphasense/OPC_Simple_v2.py b/Alphasense/OPC_Simple_v2.py index b4e0824..ad76654 100644 --- a/Alphasense/OPC_Simple_v2.py +++ b/Alphasense/OPC_Simple_v2.py @@ -1,6 +1,16 @@ from time import sleep from usbiss.spi import SPI import opcng as opc +from datetime import datetime # datetime.now() + + +fname = '{0}_pm25_simplest_CSV.csv'.format(datetime.now().strftime("%Y_%m_%d__%H_%M_%S") ) +file = open(fname,'w') +titleStr = ',Date Label, Dates (YMD), Sensor 1, pm10 standard, pm25 standard, pm100 standard, pm10 env, pm25 env, pm100 env,particles 03um, particles 05um, particles 10um, particles 25um, particles 50um, particles 100um, Dates (YMD), Sensor 2, pm10 standard, pm25 standard, pm100 standard, pm10 env, pm25 env, pm100 env,particles 03um, particles 05um, particles 10um, particles 25um, particles 50um, particles 100um' +file.write(titleStr +"\n") +file.flush() + + spi = SPI('/dev/ttyACM0') spi.mode = 1 @@ -13,17 +23,29 @@ print(f'serial: {dev.serial()}') print(f'firmware version: {dev.serial()}') -# power on fan and laser -dev.on() -for i in range(10): - # query particle mass readings - sleep(1) - #print(dev.pm()) - print(dev.histogram()) +while True: + + try: + # power on fan and laser + dev.on() + + recorded_data = dev.histogram() + #print(dev.histogram()) + + # power off fan and laser + dev.off() + + except RuntimeError: + print("Unable to read from sensor, retrying...") + continue + + + dataStr1 = ', {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}'.format(recorded_data['Bin0'], + dateStr =', Date:, {0}'.format(datetime.now()) + file.write(dateStr + dataStr + "\n") + file.flush() -# power off fan and laser -dev.off() From af987d36c22c497da0b74e58963e87f8c7cd4259 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 6 Oct 2023 15:58:55 -0400 Subject: [PATCH 07/99] Update OPC_Simple_v2.py Added the fully formatted dataStr, needs to be tested! --- Alphasense/OPC_Simple_v2.py | 53 ++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/Alphasense/OPC_Simple_v2.py b/Alphasense/OPC_Simple_v2.py index ad76654..ffb3950 100644 --- a/Alphasense/OPC_Simple_v2.py +++ b/Alphasense/OPC_Simple_v2.py @@ -41,14 +41,61 @@ continue - dataStr1 = ', {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}'.format(recorded_data['Bin0'], + # dataStr1 = ', {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}'.format(recorded_data['Bin0'], + # dateStr =', Date:, {0}'.format(datetime.now()) + + + dataStr = ', {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, {17}, {18}, {19}, {20}, {21}, {22}, {23}, {24}, {25}, {26}, {27}, {28}, {29}, {30}, {31}, {32}, {33}, {34}, {35}, {36}, {37}, {38}, {39}, {40}, {41}, {42}, {43}, {44}, {45}, {46}, {47}, {48}, {49}, {50}, {51}, {52}, {53}'.format( + recorded_data.get('Bin 0', ''), + recorded_data.get('Bin 1', ''), + recorded_data.get('Bin 2', ''), + recorded_data.get('Bin 3', ''), + recorded_data.get('Bin 4', ''), + recorded_data.get('Bin 5', ''), + recorded_data.get('Bin 6', ''), + recorded_data.get('Bin 7', ''), + recorded_data.get('Bin 8', ''), + recorded_data.get('Bin 9', ''), + recorded_data.get('Bin 10', ''), + recorded_data.get('Bin 11', ''), + recorded_data.get('Bin 12', ''), + recorded_data.get('Bin 13', ''), + recorded_data.get('Bin 14', ''), + recorded_data.get('Bin 15', ''), + recorded_data.get('Bin 16', ''), + recorded_data.get('Bin 17', ''), + recorded_data.get('Bin 18', ''), + recorded_data.get('Bin 19', ''), + recorded_data.get('Bin 20', ''), + recorded_data.get('Bin 21', ''), + recorded_data.get('Bin 22', ''), + recorded_data.get('Bin 23', ''), + recorded_data.get('Bin1 MToF', ''), + recorded_data.get('Bin3 MToF', ''), + recorded_data.get('Bin5 MToF', ''), + recorded_data.get('Bin7 MToF', ''), + recorded_data.get('Sampling Period', ''), + recorded_data.get('SFR', ''), + recorded_data.get('Temperature', ''), + recorded_data.get('Relative humidity', ''), + recorded_data.get('PM1', ''), + recorded_data.get('PM2.5', ''), + recorded_data.get('PM10', ''), + recorded_data.get('#RejectGlitch', ''), + recorded_data.get('#RejectLongTOF', ''), + recorded_data.get('#RejectRatio', ''), + recorded_data.get('#RejectOutOfRange', ''), + recorded_data.get('Fan rev count', ''), + recorded_data.get('Laser status', ''), + recorded_data.get('Checksum', '') + ) + dateStr =', Date:, {0}'.format(datetime.now()) + file.write(dateStr + dataStr + "\n") file.flush() - - #This is what the bins apparently are: #_OPC_N3_HISTOGRAM_MODEL = [['Bin 0', 'H'], # ['Bin 1', 'H'], From ad30bc6793e79b34f7585cd172017489fb7aae67 Mon Sep 17 00:00:00 2001 From: KalebNails Date: Fri, 6 Oct 2023 16:36:51 -0400 Subject: [PATCH 08/99] debugged the code --- Alphasense/OPC_Simple_v2.py | 124 ++++++------------------------------ 1 file changed, 18 insertions(+), 106 deletions(-) diff --git a/Alphasense/OPC_Simple_v2.py b/Alphasense/OPC_Simple_v2.py index ffb3950..c398ef7 100644 --- a/Alphasense/OPC_Simple_v2.py +++ b/Alphasense/OPC_Simple_v2.py @@ -1,141 +1,53 @@ from time import sleep from usbiss.spi import SPI import opcng as opc +spi = SPI('/dev/ttyACM0') +spi.mode = 1 +spi.max_speed_hz = 500000 +spi.lsbfirst = False + from datetime import datetime # datetime.now() fname = '{0}_pm25_simplest_CSV.csv'.format(datetime.now().strftime("%Y_%m_%d__%H_%M_%S") ) file = open(fname,'w') titleStr = ',Date Label, Dates (YMD), Sensor 1, pm10 standard, pm25 standard, pm100 standard, pm10 env, pm25 env, pm100 env,particles 03um, particles 05um, particles 10um, particles 25um, particles 50um, particles 100um, Dates (YMD), Sensor 2, pm10 standard, pm25 standard, pm100 standard, pm10 env, pm25 env, pm100 env,particles 03um, particles 05um, particles 10um, particles 25um, particles 50um, particles 100um' -file.write(titleStr +"\n") +#file.write(titleStr +"\n") file.flush() -spi = SPI('/dev/ttyACM0') -spi.mode = 1 -spi.max_speed_hz = 500000 -spi.lsbfirst = False -dev = opc.detect(spi) +dev = opc.detect(spi) print(f'device information: {dev.info()}') print(f'serial: {dev.serial()}') print(f'firmware version: {dev.serial()}') +# power on fan and laser +dev.on() while True: - try: - # power on fan and laser - dev.on() - - recorded_data = dev.histogram() + # query particle mass readings + sleep(1) + #print(dev.pm()) #print(dev.histogram()) - - # power off fan and laser - dev.off() + recorded_data = dev.histogram() + print(recorded_data) except RuntimeError: print("Unable to read from sensor, retrying...") continue - # dataStr1 = ', {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}'.format(recorded_data['Bin0'], - # dateStr =', Date:, {0}'.format(datetime.now()) - - - dataStr = ', {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, {17}, {18}, {19}, {20}, {21}, {22}, {23}, {24}, {25}, {26}, {27}, {28}, {29}, {30}, {31}, {32}, {33}, {34}, {35}, {36}, {37}, {38}, {39}, {40}, {41}, {42}, {43}, {44}, {45}, {46}, {47}, {48}, {49}, {50}, {51}, {52}, {53}'.format( - recorded_data.get('Bin 0', ''), - recorded_data.get('Bin 1', ''), - recorded_data.get('Bin 2', ''), - recorded_data.get('Bin 3', ''), - recorded_data.get('Bin 4', ''), - recorded_data.get('Bin 5', ''), - recorded_data.get('Bin 6', ''), - recorded_data.get('Bin 7', ''), - recorded_data.get('Bin 8', ''), - recorded_data.get('Bin 9', ''), - recorded_data.get('Bin 10', ''), - recorded_data.get('Bin 11', ''), - recorded_data.get('Bin 12', ''), - recorded_data.get('Bin 13', ''), - recorded_data.get('Bin 14', ''), - recorded_data.get('Bin 15', ''), - recorded_data.get('Bin 16', ''), - recorded_data.get('Bin 17', ''), - recorded_data.get('Bin 18', ''), - recorded_data.get('Bin 19', ''), - recorded_data.get('Bin 20', ''), - recorded_data.get('Bin 21', ''), - recorded_data.get('Bin 22', ''), - recorded_data.get('Bin 23', ''), - recorded_data.get('Bin1 MToF', ''), - recorded_data.get('Bin3 MToF', ''), - recorded_data.get('Bin5 MToF', ''), - recorded_data.get('Bin7 MToF', ''), - recorded_data.get('Sampling Period', ''), - recorded_data.get('SFR', ''), - recorded_data.get('Temperature', ''), - recorded_data.get('Relative humidity', ''), - recorded_data.get('PM1', ''), - recorded_data.get('PM2.5', ''), - recorded_data.get('PM10', ''), - recorded_data.get('#RejectGlitch', ''), - recorded_data.get('#RejectLongTOF', ''), - recorded_data.get('#RejectRatio', ''), - recorded_data.get('#RejectOutOfRange', ''), - recorded_data.get('Fan rev count', ''), - recorded_data.get('Laser status', ''), - recorded_data.get('Checksum', '') - ) + + dataStr = ', {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, {17}, {18}, {19}, {20}, {21}, {22}, {23}, {24}, {25}, {26}, {27}, {28}, {29}, {30}, {31}, {32}, {33}, {34}, {35}, {36}, {37}, {38}, {39}, {40}, {41}'.format(recorded_data.get('Bin 0', ''),recorded_data.get('Bin 1', ''),recorded_data.get('Bin 2', ''),recorded_data.get('Bin 3', ''),recorded_data.get('Bin 4', ''),recorded_data.get('Bin 5', ''),recorded_data.get('Bin 6', ''),recorded_data.get('Bin 7', ''),recorded_data.get('Bin 8', ''),recorded_data.get('Bin 9', ''),recorded_data.get('Bin 10', ''),recorded_data.get('Bin 11', ''),recorded_data.get('Bin 12', ''),recorded_data.get('Bin 13', ''),recorded_data.get('Bin 14', ''),recorded_data.get('Bin 15', ''),recorded_data.get('Bin 16', ''),recorded_data.get('Bin 17', ''),recorded_data.get('Bin 18', ''),recorded_data.get('Bin 19', ''),recorded_data.get('Bin 20', ''),recorded_data.get('Bin 21', ''),recorded_data.get('Bin 22', ''),recorded_data.get('Bin 23', ''),recorded_data.get('Bin1 MToF', ''),recorded_data.get('Bin3 MToF', ''),recorded_data.get('Bin5 MToF', ''),recorded_data.get('Bin7 MToF', ''),recorded_data.get('Sampling Period', ''),recorded_data.get('SFR', ''),recorded_data.get('Temperature', ''),recorded_data.get('Relative humidity', ''),recorded_data.get('PM1', ''),recorded_data.get('PM2.5', ''),recorded_data.get('PM10', ''),recorded_data.get('#RejectGlitch', ''),recorded_data.get('#RejectLongTOF', ''),recorded_data.get('#RejectRatio', ''),recorded_data.get('#RejectOutOfRange', ''),recorded_data.get('Fan rev count', ''),recorded_data.get('Laser status', ''),recorded_data.get('Checksum', '')) dateStr =', Date:, {0}'.format(datetime.now()) file.write(dateStr + dataStr + "\n") file.flush() - -#This is what the bins apparently are: -#_OPC_N3_HISTOGRAM_MODEL = [['Bin 0', 'H'], -# ['Bin 1', 'H'], -# ['Bin 2', 'H'], -# ['Bin 3', 'H'], -# ['Bin 4', 'H'], -# ['Bin 5', 'H'], -# ['Bin 6', 'H'], -# ['Bin 7', 'H'], -# ['Bin 8', 'H'], -# ['Bin 9', 'H'], -# ['Bin 10', 'H'], -# ['Bin 11', 'H'], -# ['Bin 12', 'H'], -# ['Bin 13', 'H'], -# ['Bin 14', 'H'], -# ['Bin 15', 'H'], -# ['Bin 16', 'H'], -# ['Bin 17', 'H'], -# ['Bin 18', 'H'], -# ['Bin 19', 'H'], -# ['Bin 20', 'H'], -# ['Bin 21', 'H'], -# ['Bin 22', 'H'], -# ['Bin 23', 'H'], -# ['Bin1 MToF', 'B'], -# ['Bin3 MToF', 'B'], -# ['Bin5 MToF', 'B'], -# ['Bin7 MToF', 'B'], -# ['Sampling Period', 'H'], -# ['SFR', 'H'], -# ['Temperature', 'H'], -# ['Relative humidity', 'H'], -# ['PM1', 'f'], -# ['PM2.5', 'f'], -# ['PM10', 'f'], -# ['#RejectGlitch', 'H'], -# ['#RejectLongTOF', 'H'], -# ['#RejectRatio', 'H'], -# ['#RejectOutOfRange', 'H'], -# ['Fan rev count', 'H'], -# ['Laser status', 'H'], -# ['Checksum', 'H']] +# power off fan and laser +#dev.off() From 8d2e864fb5bce2177e5335a42a15622fe5dffa6b Mon Sep 17 00:00:00 2001 From: KalebNails Date: Fri, 6 Oct 2023 17:06:14 -0400 Subject: [PATCH 09/99] This adds a Ctrl + C function that turns off the sensor when the code is interrupted. Also adjusted the headings so that they match. --- Alphasense/OPC_Simple_v2.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/Alphasense/OPC_Simple_v2.py b/Alphasense/OPC_Simple_v2.py index c398ef7..6157aef 100644 --- a/Alphasense/OPC_Simple_v2.py +++ b/Alphasense/OPC_Simple_v2.py @@ -1,21 +1,37 @@ -from time import sleep +#Created by: Kaleb Nails, Erik Liebergall +#Date: 10/6/2023 +#from time import sleep from usbiss.spi import SPI import opcng as opc +import os +import time +import signal +import subprocess +from datetime import datetime # datetime.now() + +#This exits one directory at a time so its on the local computer so you dont get csvs on your repository +os.chdir("..") +os.chdir("..") + spi = SPI('/dev/ttyACM0') spi.mode = 1 spi.max_speed_hz = 500000 spi.lsbfirst = False -from datetime import datetime # datetime.now() - fname = '{0}_pm25_simplest_CSV.csv'.format(datetime.now().strftime("%Y_%m_%d__%H_%M_%S") ) file = open(fname,'w') -titleStr = ',Date Label, Dates (YMD), Sensor 1, pm10 standard, pm25 standard, pm100 standard, pm10 env, pm25 env, pm100 env,particles 03um, particles 05um, particles 10um, particles 25um, particles 50um, particles 100um, Dates (YMD), Sensor 2, pm10 standard, pm25 standard, pm100 standard, pm10 env, pm25 env, pm100 env,particles 03um, particles 05um, particles 10um, particles 25um, particles 50um, particles 100um' -#file.write(titleStr +"\n") +titleStr = ',Date Label, Dates (YMD), Bin 0, Bin 1, Bin 2, Bin 3, Bin 4, Bin 5, Bin 6, Bin 7, Bin 8, Bin 9, Bin 10, Bin 11, Bin 12, Bin 13, Bin 14, Bin 15, Bin 16, Bin 17, Bin 18, Bin 19, Bin 20, Bin 21, Bin 22, Bin 23, Bin1 MToF,Bin3 MToF,Bin5 MToF,Bin7 MToF,Sampling Period,SFR,Temperature,Relative humidity,PM1,PM2.5,PM10,#RejectGlitch,#RejectLongTOF,#RejectRatio,#RejectOutOfRange,Fan rev count,Laser status,Checksum' +file.write(titleStr +"\n") file.flush() +#This is to hand interuptions and turn the alphasense +def handle_interrupt(signal, frame): + print("Ctrl+C pressed. Performing cleanup or other actions...") + dev.off() + exit(0) # Terminate the script gracefully +signal.signal(signal.SIGINT, handle_interrupt) @@ -30,7 +46,7 @@ while True: try: # query particle mass readings - sleep(1) + time.sleep(1) #print(dev.pm()) #print(dev.histogram()) recorded_data = dev.histogram() From 61f0d9b7312faa80609051f41f5f533bc3264364 Mon Sep 17 00:00:00 2001 From: KalebNails Date: Fri, 6 Oct 2023 17:21:05 -0400 Subject: [PATCH 10/99] This has added the serial code of the alphasense sensor to the first column of the csv. --- Alphasense/OPC_Simple_v2.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Alphasense/OPC_Simple_v2.py b/Alphasense/OPC_Simple_v2.py index 6157aef..ace2420 100644 --- a/Alphasense/OPC_Simple_v2.py +++ b/Alphasense/OPC_Simple_v2.py @@ -21,7 +21,7 @@ fname = '{0}_pm25_simplest_CSV.csv'.format(datetime.now().strftime("%Y_%m_%d__%H_%M_%S") ) file = open(fname,'w') -titleStr = ',Date Label, Dates (YMD), Bin 0, Bin 1, Bin 2, Bin 3, Bin 4, Bin 5, Bin 6, Bin 7, Bin 8, Bin 9, Bin 10, Bin 11, Bin 12, Bin 13, Bin 14, Bin 15, Bin 16, Bin 17, Bin 18, Bin 19, Bin 20, Bin 21, Bin 22, Bin 23, Bin1 MToF,Bin3 MToF,Bin5 MToF,Bin7 MToF,Sampling Period,SFR,Temperature,Relative humidity,PM1,PM2.5,PM10,#RejectGlitch,#RejectLongTOF,#RejectRatio,#RejectOutOfRange,Fan rev count,Laser status,Checksum' +titleStr = 'Serial Number,Date Label, Dates (YMD), Bin 0, Bin 1, Bin 2, Bin 3, Bin 4, Bin 5, Bin 6, Bin 7, Bin 8, Bin 9, Bin 10, Bin 11, Bin 12, Bin 13, Bin 14, Bin 15, Bin 16, Bin 17, Bin 18, Bin 19, Bin 20, Bin 21, Bin 22, Bin 23, Bin1 MToF,Bin3 MToF,Bin5 MToF,Bin7 MToF,Sampling Period,SFR,Temperature,Relative humidity,PM1,PM2.5,PM10,#RejectGlitch,#RejectLongTOF,#RejectRatio,#RejectOutOfRange,Fan rev count,Laser status,Checksum' file.write(titleStr +"\n") file.flush() @@ -38,6 +38,7 @@ def handle_interrupt(signal, frame): dev = opc.detect(spi) print(f'device information: {dev.info()}') print(f'serial: {dev.serial()}') +SerialNumberStr = str(dev.serial()) print(f'firmware version: {dev.serial()}') # power on fan and laser @@ -62,7 +63,8 @@ def handle_interrupt(signal, frame): dateStr =', Date:, {0}'.format(datetime.now()) - file.write(dateStr + dataStr + "\n") + print("\n" + SerialNumberStr + dateStr + dataStr + "\n") + file.write(SerialNumberStr + dateStr + dataStr + "\n") file.flush() # power off fan and laser From 6ac4f03b1d6ed4a1f946da3cdfd3e4601666aaec Mon Sep 17 00:00:00 2001 From: KalebNails Date: Fri, 6 Oct 2023 17:35:25 -0400 Subject: [PATCH 11/99] Fixed the spacing in the Serial name, and also changed the name of the csv and also added the serial number to the name of the csv --- Alphasense/OPC_Simple_v2.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/Alphasense/OPC_Simple_v2.py b/Alphasense/OPC_Simple_v2.py index ace2420..1c4f43f 100644 --- a/Alphasense/OPC_Simple_v2.py +++ b/Alphasense/OPC_Simple_v2.py @@ -19,11 +19,7 @@ spi.lsbfirst = False -fname = '{0}_pm25_simplest_CSV.csv'.format(datetime.now().strftime("%Y_%m_%d__%H_%M_%S") ) -file = open(fname,'w') -titleStr = 'Serial Number,Date Label, Dates (YMD), Bin 0, Bin 1, Bin 2, Bin 3, Bin 4, Bin 5, Bin 6, Bin 7, Bin 8, Bin 9, Bin 10, Bin 11, Bin 12, Bin 13, Bin 14, Bin 15, Bin 16, Bin 17, Bin 18, Bin 19, Bin 20, Bin 21, Bin 22, Bin 23, Bin1 MToF,Bin3 MToF,Bin5 MToF,Bin7 MToF,Sampling Period,SFR,Temperature,Relative humidity,PM1,PM2.5,PM10,#RejectGlitch,#RejectLongTOF,#RejectRatio,#RejectOutOfRange,Fan rev count,Laser status,Checksum' -file.write(titleStr +"\n") -file.flush() + #This is to hand interuptions and turn the alphasense def handle_interrupt(signal, frame): @@ -34,14 +30,29 @@ def handle_interrupt(signal, frame): signal.signal(signal.SIGINT, handle_interrupt) - +#This detects which model of opc and prints the relevent data dev = opc.detect(spi) print(f'device information: {dev.info()}') print(f'serial: {dev.serial()}') + +#this just formats the serial number string to look better SerialNumberStr = str(dev.serial()) +SerialNumberStr = SerialNumberStr.replace("N3","N3-") +SerialNumberStr = SerialNumberStr.replace(" ","") + print(f'firmware version: {dev.serial()}') # power on fan and laser + + +fname = '{0}_Alphasense_{1}.csv'.format(datetime.now().strftime("%Y_%m_%d__%H_%M_%S"), SerialNumberStr) +file = open(fname,'w') +titleStr = 'Serial Number,Date Label, Dates (YMD), Bin 0, Bin 1, Bin 2, Bin 3, Bin 4, Bin 5, Bin 6, Bin 7, Bin 8, Bin 9, Bin 10, Bin 11, Bin 12, Bin 13, Bin 14, Bin 15, Bin 16, Bin 17, Bin 18, Bin 19, Bin 20, Bin 21, Bin 22, Bin 23, Bin1 MToF,Bin3 MToF,Bin5 MToF,Bin7 MToF,Sampling Period,SFR,Temperature,Relative humidity,PM1,PM2.5,PM10,#RejectGlitch,#RejectLongTOF,#RejectRatio,#RejectOutOfRange,Fan rev count,Laser status,Checksum' +file.write(titleStr +"\n") +file.flush() + + + dev.on() while True: From e5d184ac13fe5d666a0e66c5767d0ff5047d3d24 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 11 Oct 2023 14:49:20 -0400 Subject: [PATCH 12/99] Create README.md --- Sensirion/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 Sensirion/README.md diff --git a/Sensirion/README.md b/Sensirion/README.md new file mode 100644 index 0000000..e633d5d --- /dev/null +++ b/Sensirion/README.md @@ -0,0 +1,13 @@ +# Sensirion Sensor # + +## UART VS I2C ## +The sensor can record using both uart and I2C, for this we will be using uart + + +## Reference Links ## +Data-sheet: https://cdn.sparkfun.com/assets/4/e/e/f/8/Sensirion_PM_Sensors_Datasheet_SPS30.pdf +https://github.com/binh-bk/Sensirion_SPS30/blob/master/sps30.py +https://binh-bk.github.io/Sensirion_SPS30/ + +## Hardware Interface & Ports ## +![image](https://github.com/MOVEUAS/Sensor_Code/assets/117048000/1d0e7864-0a3f-4848-bdcf-2cc4c035eff0) From 7354b495f04d8411a2a1bea3bd8235af24eeedb1 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 11 Oct 2023 15:04:22 -0400 Subject: [PATCH 13/99] Update README.md --- Sensirion/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sensirion/README.md b/Sensirion/README.md index e633d5d..18f047d 100644 --- a/Sensirion/README.md +++ b/Sensirion/README.md @@ -3,10 +3,15 @@ ## UART VS I2C ## The sensor can record using both uart and I2C, for this we will be using uart +## sps30.py ## +This is a file that comes from https://github.com/binh-bk/Sensirion_SPS30/blob/master/sps30.py and is used to collect sps30 data. + ## Reference Links ## Data-sheet: https://cdn.sparkfun.com/assets/4/e/e/f/8/Sensirion_PM_Sensors_Datasheet_SPS30.pdf + https://github.com/binh-bk/Sensirion_SPS30/blob/master/sps30.py + https://binh-bk.github.io/Sensirion_SPS30/ ## Hardware Interface & Ports ## From ffb367b36e829db300f64e4623789779d76572a1 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 11 Oct 2023 15:04:27 -0400 Subject: [PATCH 14/99] Update pm25_simpletest.py Changed the device ID from 'ttyUSB1' to 'ttyUSB0', also commented out second device from code to test on drone pi. --- PMS plantower/pm25_simpletest.py | 98 ++++++++++++++++---------------- 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/PMS plantower/pm25_simpletest.py b/PMS plantower/pm25_simpletest.py index bad8c5a..60914de 100644 --- a/PMS plantower/pm25_simpletest.py +++ b/PMS plantower/pm25_simpletest.py @@ -15,7 +15,7 @@ # Kaleb Nails, nailsk@my.erau.edu # Marc Compere, comperem@erau.edu # created : 10 Feb 2023 -# modified: 15 Sep 2023 +# modified: 11 Oct 2023 """ Example sketch to connect to PM2.5 sensor with either I2C or UART. @@ -52,16 +52,16 @@ # For use with USB-to-serial cable: import serial -dev1='/dev/ttyUSB1' -dev2='/dev/ttyUSB2' +dev1='/dev/ttyUSB0' +# dev2='/dev/ttyUSB2' uart = serial.Serial(dev1, baudrate=9600, timeout=0.25) -uart2 = serial.Serial(dev2, baudrate=9600, timeout=0.25) +# uart2 = serial.Serial(dev2, baudrate=9600, timeout=0.25) # Connect to a PM2.5 sensor over UART from adafruit_pm25.uart import PM25_UART pm25 = PM25_UART(uart, reset_pin) -pm25_2 = PM25_UART(uart2, reset_pin) +# pm25_2 = PM25_UART(uart2, reset_pin) # Create library object, use 'slow' 100KHz frequency! #i2c = busio.I2C(board.SCL, board.SDA, frequency=100000) @@ -116,36 +116,36 @@ # while False: time.sleep(1) - try: - aqdata2 = pm25_2.read() - # print(aqdata) - except RuntimeError: - print("Unable to read from sensor, retrying...") - continue - - print() - print("Concentration Units (standard)") - print("---------------------------------------") - print( - "PM 1.0: %d\tPM2.5: %d\tPM10: %d" - % (aqdata2["pm10 standard"], aqdata2["pm25 standard"], aqdata2["pm100 standard"]) - ) - print("Concentration Units (environmental)") - print("---------------------------------------") - print( - "PM 1.0: %d\tPM2.5: %d\tPM10: %d" - % (aqdata2["pm10 env"], aqdata2["pm25 env"], aqdata2["pm100 env"]) - ) - print("---------------------------------------") - print("Particles2 > 0.3um / 0.1L air:", aqdata2["particles 03um"]) - print("Particles2 > 0.5um / 0.1L air:", aqdata2["particles 05um"]) - print("Particles2 > 1.0um / 0.1L air:", aqdata2["particles 10um"]) - print("Particles2 > 2.5um / 0.1L air:", aqdata2["particles 25um"]) - print("Particles2 > 5.0um / 0.1L air:", aqdata2["particles 50um"]) - print("Particles2 > 10 um / 0.1L air:", aqdata2["particles 100um"]) - print("---------------------------------------") + # try: + # aqdata2 = pm25_2.read() + # # print(aqdata) + # except RuntimeError: + # print("Unable to read from sensor, retrying...") + # continue + + # print() + # print("Concentration Units (standard)") + # print("---------------------------------------") + # print( + # "PM 1.0: %d\tPM2.5: %d\tPM10: %d" + # % (aqdata2["pm10 standard"], aqdata2["pm25 standard"], aqdata2["pm100 standard"]) + # ) + # print("Concentration Units (environmental)") + # print("---------------------------------------") + # print( + # "PM 1.0: %d\tPM2.5: %d\tPM10: %d" + # % (aqdata2["pm10 env"], aqdata2["pm25 env"], aqdata2["pm100 env"]) + # ) + # print("---------------------------------------") + # print("Particles2 > 0.3um / 0.1L air:", aqdata2["particles 03um"]) + # print("Particles2 > 0.5um / 0.1L air:", aqdata2["particles 05um"]) + # print("Particles2 > 1.0um / 0.1L air:", aqdata2["particles 10um"]) + # print("Particles2 > 2.5um / 0.1L air:", aqdata2["particles 25um"]) + # print("Particles2 > 5.0um / 0.1L air:", aqdata2["particles 50um"]) + # print("Particles2 > 10 um / 0.1L air:", aqdata2["particles 100um"]) + # print("---------------------------------------") - dateStr2 =', Date:, {0}'.format(datetime.now()) + # dateStr2 =', Date:, {0}'.format(datetime.now()) #dataStr1 = ', Data1:, {0}, {1}, {2}, {3}, {4}, {5}'.format(aqdata["particles 03um"],aqdata["particles 05um"],aqdata["particles 10um"],aqdata["particles 25um"],aqdata["particles 50um"],aqdata["particles 100um"]) #dataStr2 = ', Data2:, {0}, {1}, {2}, {3}, {4}, {5}'.format(aqdata2["particles 03um"],aqdata2["particles 05um"],aqdata2["particles 10um"],aqdata2["particles 25um"],aqdata2["particles 50um"],aqdata2["particles 100um"]) @@ -165,22 +165,24 @@ aqdata["particles 50um"], \ aqdata["particles 100um"]) - dataStr2 = ', Data2:, {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}'.format( \ - aqdata2["pm10 standard"], \ - aqdata2["pm25 standard"], \ - aqdata2["pm100 standard"], \ + # dataStr2 = ', Data2:, {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}'.format( \ + # aqdata2["pm10 standard"], \ + # aqdata2["pm25 standard"], \ + # aqdata2["pm100 standard"], \ - aqdata2["pm10 env"], \ - aqdata2["pm25 env"], \ - aqdata2["pm100 env"], \ + # aqdata2["pm10 env"], \ + # aqdata2["pm25 env"], \ + # aqdata2["pm100 env"], \ - aqdata2["particles 03um"], \ - aqdata2["particles 05um"], \ - aqdata2["particles 10um"], \ - aqdata2["particles 25um"], \ - aqdata2["particles 50um"], \ - aqdata2["particles 100um"]) - file.write(dateStr1 + dataStr1 + dateStr2 + dataStr2 +"\n") + # aqdata2["particles 03um"], \ + # aqdata2["particles 05um"], \ + # aqdata2["particles 10um"], \ + # aqdata2["particles 25um"], \ + # aqdata2["particles 50um"], \ + # aqdata2["particles 100um"]) + # file.write(dateStr1 + dataStr1 + dateStr2 + dataStr2 +"\n") + + file.write(dateStr1 + dataStr1 + "\n") file.flush() From 9c1691eca47da9f5ee4a6d46c8ae7925d5db208f Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 11 Oct 2023 15:05:02 -0400 Subject: [PATCH 15/99] Create sps30.py --- Sensirion/sps30.py | 235 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 Sensirion/sps30.py diff --git a/Sensirion/sps30.py b/Sensirion/sps30.py new file mode 100644 index 0000000..e94b681 --- /dev/null +++ b/Sensirion/sps30.py @@ -0,0 +1,235 @@ +""" +Binh Nguyen, June 14, 2020 +- A fork from Szymon Jakubiak (2018) +- additional feature: + 1. push data to MQTT server + 2. log file to CSV format (both of them are optional) + 3. run multiple SPS30 with USB hub (quality check SPS30 sensors) +""" + +import serial, struct, time +import subprocess +from operator import invert +import os +import json +import socket +import paho.mqtt.publish as publish + +# set localtime +os.environ['TZ'] = 'Asia/Ho_Chi_Minh' +time.tzset() + +# MQTT host, users +mqtt = '192.168.1.100' # change this +topic = 'sensor/sps30' # and this +auth = {'username': 'mqtt_user', 'password': 'mqtt_password'} # and these two + + +def get_usb(): + ''' + - list all devices connected to USB port + - make sure no other devices rather SDS011 sensors connected + ''' + + + try: + with subprocess.Popen(['ls /dev/ttyUSB*'], shell=True, stdout=subprocess.PIPE) as f: + usbs = f.stdout.read().decode('utf-8') + usbs = usbs.split('\n') + usbs = [usb for usb in usbs if len(usb) > 3] + except Exception as e: + print('No USB available') + return usbs + +def time_(): return int(time.time()) + + +def datetime_(): + return time.strftime('%x %X', time.localtime()) + + +def host_folder(): + """designate a folder to save data""" + this_month_folder = time.strftime('%b%Y') + basedir = os.path.abspath(os.path.dirname(__file__)) + # basedir = '/'.join(basedir.split('/')[: -1]) # make one level up + all_dirs = [d for d in os.listdir(basedir) if os.path.isdir(d)] + if len(all_dirs) == 0 or this_month_folder not in all_dirs: + os.makedirs(this_month_folder) + print('created: {}'.format(this_month_folder)) + return os.path.join(basedir, this_month_folder) + + +def internet_ready(): + '''check if internet connection is ready''' + + try: + _ = socket.create_connection((mqtt, 1883), 3) + return True + except Exception as e: + print("Error {}".format(e)) + return False + + +def record_data(data): + '''save data to CSV file''' + id_ = data.split(',')[0] + id_ = f'{id_}' + filename = os.path.join(host_folder(), f'{id_}.csv') + with open(filename, 'a+') as f: + f.write(f'{data}\n') + print(data) + return None + + +def push_mqtt_server(data): + '''push data to MQTT server''' + + header = ['sensor','time','PM1','PM25','PM4','PM10','b0305', + 'b031','b0325','b034','b0310','tsize'] + data = data.split(',') + if len(header) == len(data): + print(f'Process for MQTT {data}') + payload = dict(zip(header,data)) + payload['type'] = 'json' + print(f'MQTT: {payload}') + payload = json.dumps(payload) + try: + if internet_ready(): + publish.single(topic, payload, hostname=mqtt, auth=auth) + + except Exception as e: + print('Error: {}'.format(e)) + pass + return None + + + +class SPS30: + NAME = 'SPS30' + WARMUP = 20 # seconds + + def __init__(self, port, save_data=True, push_mqtt=False, INTERVAL=60): + self.port = port + self.interval = INTERVAL + self.warmup = SPS30.WARMUP + self.save_data = save_data + self.push_mqtt = push_mqtt + self.name = SPS30.NAME + self.lastSample = 0 + self.fanOn = 0 + self.is_started = False + self.ser = serial.Serial(self.port, baudrate=115200, stopbits=1, parity="N", timeout=2) + + def __str__(self): + return f'{self.port}, {self.name}, {self.fanOn}, {self.lastSample}' + + + def start(self): + self.ser.write([0x7E, 0x00, 0x00, 0x02, 0x01, 0x03, 0xF9, 0x7E]) + + def stop(self): + self.ser.write([0x7E, 0x00, 0x01, 0x00, 0xFE, 0x7E]) + + def read_values(self): + self.ser.flushInput() + # Ask for data + self.ser.write([0x7E, 0x00, 0x03, 0x00, 0xFC, 0x7E]) + toRead = self.ser.inWaiting() + # Wait for full response + # (may be changed for looking for the stop byte 0x7E) + while toRead < 47: + + toRead = self.ser.inWaiting() + print(f'Wait: {toRead}') + time.sleep(1) + raw = self.ser.read(toRead) + + # Reverse byte-stuffing + if b'\x7D\x5E' in raw: + raw = raw.replace(b'\x7D\x5E', b'\x7E') + if b'\x7D\x5D' in raw: + raw = raw.replace(b'\x7D\x5D', b'\x7D') + if b'\x7D\x31' in raw: + raw = raw.replace(b'\x7D\x31', b'\x11') + if b'\x7D\x33' in raw: + raw = raw.replace(b'\x7D\x33', b'\x13') + + # Discard header and tail + rawData = raw[5:-2] + + try: + data = struct.unpack(">ffffffffff", rawData) + except struct.error: + data = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) + return data + + def read_serial_number(self): + self.ser.flushInput() + self.ser.write([0x7E, 0x00, 0xD0, 0x01, 0x03, 0x2B, 0x7E]) + toRead = self.ser.inWaiting() + while toRead < 7: #24 + toRead = self.ser.inWaiting() + print(f'Wait: {toRead}') + time.sleep(1) + raw = self.ser.read(toRead) + + # Reverse byte-stuffing + if b'\x7D\x5E' in raw: + raw = raw.replace(b'\x7D\x5E', b'\x7E') + if b'\x7D\x5D' in raw: + raw = raw.replace(b'\x7D\x5D', b'\x7D') + if b'\x7D\x31' in raw: + raw = raw.replace(b'\x7D\x31', b'\x11') + if b'\x7D\x33' in raw: + raw = raw.replace(b'\x7D\x33', b'\x13') + + # Discard header, tail and decode + serial_number = raw[5:-3].decode('ascii') + return serial_number + + def run_query(self): + if time_() - self.lastSample >= self.interval: + if not self.is_started: + self.start() + self.fanOn = time_() + self.is_started = True + if self.name == SPS30.NAME: + name_ = self.read_serial_number() + if len(name_) >0: + self.name = f'SPS_{name_}' + if time_() - self.fanOn >= self.warmup: + output = self.read_values() + sensorData = "" + for val in output: + sensorData += "{0:.2f},".format(val) + + output = ','.join([self.name, datetime_(),sensorData[:-1]]) + self.lastSample = time_() + if self.is_started: + self.stop() + self.is_started = False + if self.save_data: + record_data(output) + if self.push_mqtt: + push_mqtt_server(output) + else: + time.sleep(1) + return None + + def close_port(self): + self.ser.close() + + +if __name__ == '__main__': + # s1 = SPS30(port='/dev/ttyUSB0', push_mqtt=True) + usbs = get_usb() + print(usbs) + process = list() + for port in usbs: + p = SPS30(port=port, push_mqtt=False) + process.append(p) + print('Starting') + while True: + for p in process: + p.run_query() From d1bb47caca9496a93f77d369c2799d931d158c5b Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 11 Oct 2023 15:06:20 -0400 Subject: [PATCH 16/99] Create exampleReadSPS30.py --- Sensirion/exampleReadSPS30.py | 45 +++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 Sensirion/exampleReadSPS30.py diff --git a/Sensirion/exampleReadSPS30.py b/Sensirion/exampleReadSPS30.py new file mode 100644 index 0000000..5d921e3 --- /dev/null +++ b/Sensirion/exampleReadSPS30.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# +# UART interface to Sparkfun SPS-30: sparkfun.com/products/15103 +# documented here: https://binh-bk.github.io/Sensirion_SPS30/ +# https://github.com/binh-bk/Sensirion_SPS30 +# +# output format (is probably) documented in 4.3 Measurement Output Formats +# at datasheet: https://cdn.sparkfun.com/assets/4/e/e/f/8/Sensirion_PM_Sensors_Datasheet_SPS30.pdf +# +# needs paho-mqtt: +# install with: pip3 install paho-mqtt +# +# Erik Liebergall, lieberge@my.erau.edu +# Marc Compere, comperem@erau.edu +# created : 02 Feb 2023 +# modified: 02 Feb 2023 + +from sps30 import get_usb +from sps30 import SPS30 + +device='/dev/ttyUSB0' +#device='/dev/ttyUSB1' + +p = SPS30(port=device, push_mqtt=True) + +docStr=[] +docStr.append('Mass Concentration PM1.0 (µg/m³)') +docStr.append('Mass Concentration PM2.5 (µg/m³)') +docStr.append('Mass Concentration PM4.0 (µg/m³)') +docStr.append('Mass Concentration PM10.0 (µg/m³)') +docStr.append('Number Concentration PM0.5 [#/cm³]') +docStr.append('Number Concentration PM1.0 [#/cm³]') +docStr.append('Number Concentration PM2.5 [#/cm³]') +docStr.append('Number Concentration PM4.0 [#/cm³]') +docStr.append('Number Concentration PM10.0 [#/cm³]') +docStr.append('Typical Particle Size [µm]') + +for i in range(len(docStr)): + print('{0}, '.format(docStr[i]), end='') + +while True: + out=p.read_values() + for i in range(len(out)): + print('{0}, '.format(out[i]), end='') + print('') From c9bd000d607aecabcffa053b855c78b907b4b384 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 11 Oct 2023 15:13:01 -0400 Subject: [PATCH 17/99] Update README.md --- Sensirion/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sensirion/README.md b/Sensirion/README.md index 18f047d..db0a48d 100644 --- a/Sensirion/README.md +++ b/Sensirion/README.md @@ -6,6 +6,8 @@ The sensor can record using both uart and I2C, for this we will be using uart ## sps30.py ## This is a file that comes from https://github.com/binh-bk/Sensirion_SPS30/blob/master/sps30.py and is used to collect sps30 data. +# exampleReadSPS30.py # +This is a simple examle on how to read the Sensirion sensor ## Reference Links ## Data-sheet: https://cdn.sparkfun.com/assets/4/e/e/f/8/Sensirion_PM_Sensors_Datasheet_SPS30.pdf From 9554c66c743c01df1a2664f42183e8f6957bbc11 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 11 Oct 2023 15:14:18 -0400 Subject: [PATCH 18/99] Update README.md --- Sensirion/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sensirion/README.md b/Sensirion/README.md index db0a48d..8288b16 100644 --- a/Sensirion/README.md +++ b/Sensirion/README.md @@ -6,7 +6,7 @@ The sensor can record using both uart and I2C, for this we will be using uart ## sps30.py ## This is a file that comes from https://github.com/binh-bk/Sensirion_SPS30/blob/master/sps30.py and is used to collect sps30 data. -# exampleReadSPS30.py # +## exampleReadSPS30.py ## This is a simple examle on how to read the Sensirion sensor ## Reference Links ## From 93c9fa816fa533fe635e32eca5c82a6d9304fd1d Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 11 Oct 2023 15:28:21 -0400 Subject: [PATCH 19/99] Update pm25_simpletest.py Added 'os.chdir("..")' to store CSV's to local repository instead of current GitHub branch --- PMS plantower/pm25_simpletest.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/PMS plantower/pm25_simpletest.py b/PMS plantower/pm25_simpletest.py index 60914de..32f57b3 100644 --- a/PMS plantower/pm25_simpletest.py +++ b/PMS plantower/pm25_simpletest.py @@ -29,6 +29,9 @@ from digitalio import DigitalInOut, Direction, Pull #from adafruit_pm25.i2c import PM25_I2C +# go back 2 directories to store CSV's +os.chdir("..") +os.chdir("..") reset_pin = None # If you have a GPIO, its not a bad idea to connect it to the RESET pin From 1e4dc670d5288036d08591fade6498a89c7c5351 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 11 Oct 2023 15:31:01 -0400 Subject: [PATCH 20/99] Create SPS30_Senirion_Run.py --- Sensirion/SPS30_Senirion_Run.py | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Sensirion/SPS30_Senirion_Run.py diff --git a/Sensirion/SPS30_Senirion_Run.py b/Sensirion/SPS30_Senirion_Run.py new file mode 100644 index 0000000..a034af5 --- /dev/null +++ b/Sensirion/SPS30_Senirion_Run.py @@ -0,0 +1,2 @@ +#Kaleb Nails +#10/11/2023 From 13b9ecb308bc60cfa813fc3c4bcee27d9f94789f Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 11 Oct 2023 15:31:52 -0400 Subject: [PATCH 21/99] Update pm25_simpletest.py Added "import os" --- PMS plantower/pm25_simpletest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/PMS plantower/pm25_simpletest.py b/PMS plantower/pm25_simpletest.py index 32f57b3..cc11836 100644 --- a/PMS plantower/pm25_simpletest.py +++ b/PMS plantower/pm25_simpletest.py @@ -25,6 +25,7 @@ import time import board import busio +import os from datetime import datetime # datetime.now() from digitalio import DigitalInOut, Direction, Pull #from adafruit_pm25.i2c import PM25_I2C From ee23072c96d3dc6be1945f5fa8adad14aa6c2a6b Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 11 Oct 2023 15:37:35 -0400 Subject: [PATCH 22/99] Update README.md --- Sensirion/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sensirion/README.md b/Sensirion/README.md index 8288b16..23dd0de 100644 --- a/Sensirion/README.md +++ b/Sensirion/README.md @@ -18,3 +18,6 @@ https://binh-bk.github.io/Sensirion_SPS30/ ## Hardware Interface & Ports ## ![image](https://github.com/MOVEUAS/Sensor_Code/assets/117048000/1d0e7864-0a3f-4848-bdcf-2cc4c035eff0) + + +NOTE FOR OUR USB TO UART, white is the reciever & green is the transmitter. From ca0716593790a5e77c40a8ebf5afb77b54aea800 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 11 Oct 2023 16:48:41 -0400 Subject: [PATCH 23/99] Update README.md --- Sensirion/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Sensirion/README.md b/Sensirion/README.md index 23dd0de..089d6e7 100644 --- a/Sensirion/README.md +++ b/Sensirion/README.md @@ -21,3 +21,12 @@ https://binh-bk.github.io/Sensirion_SPS30/ NOTE FOR OUR USB TO UART, white is the reciever & green is the transmitter. + + +also note: +USB - SENSOR: +red - red +black -black +white - purple +green -white +nothing - green From 91a69a7e40a64f4cc06a39cce7b39821a0bff81e Mon Sep 17 00:00:00 2001 From: KalebNails Date: Wed, 11 Oct 2023 16:57:51 -0400 Subject: [PATCH 24/99] updated sensirion code so that it may work, still run into occascional issues with it. --- Sensirion/__pycache__/sps30.cpython-310.pyc | Bin 0 -> 6775 bytes Sensirion/exampleReadSPS30.py | 8 +++++++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 Sensirion/__pycache__/sps30.cpython-310.pyc diff --git a/Sensirion/__pycache__/sps30.cpython-310.pyc b/Sensirion/__pycache__/sps30.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..646f5e56874f53bbfe81f6459a1db2cf3bdc476a GIT binary patch literal 6775 zcmdT|+ix7#d7t~v&MwI%b)$71J5cJiCEg@Aj;bnFbZKl@v})2;w2m_v?iq52+}&B8 zIZKA)UBNBorbX+zO^UvxE|x`$#Q#Hq0)6UZpAY)hhk`)S7XuAy#8Q9X8QvuWeeEvh zJNL_c=lA`-?@Tf@Ls8Z|?u zX3bQoRkM(qO}pjP96j$_s1;P3TXR)ftQA%2wMvGjYprsvjGk6hSRP{bbuBUn!&|6l zPOZcWwGrmBBJ=*ns8v{rl^<)hQ8vVek&dwuRzZ4%G*+-~@@ zOWlpAeaU~T+m8I{tC#$lshKJ7vVYTGO!$hw$di^omu<9?w*OYR(p`wd760X#sp;3N z=oB&*i#TbAO@A>8MVCj{Jb!w^?{w3K&q5LUBJuD2{`|b3Mtm*ezUR+O_|0U=UyPd( zHQt(gpB8F`!hd-o5e+|C^hG0T`5}+|q?1ox_53RnKJT{uR<|kQ4u;O%pSv>Uug4hi z-rV>7Mt8w~d9@ohW3l1a8&Q2_r$-vK(}bs0Z=29ETh*l#x3LqERc@wnIQfGlxYdY* zdr*Zm=4;u|^mk__rhohOiRp>ysi~}-kDp9C>6Iy2Y^@3rbnz~7NM$==nyx3DRSoG< zbvtZD(%r4C(~`SC6B&Z=Ej;PZAhKzpEd!oQ04j0@Ge0#ytMs*=wrmcpEp1>kYfJCz z4>V?5nx$=;y4H55weqd>kSUdUqTTRdMe2+|=|mP@qm@e>SyTHquP}NsO2jVgqx(G7o*) zX(MVjrIQMlbVX)&#p31HXQl4dVc>f|133%N9G>(n#1i!Pq1V?1G_k2OZN=hv%fB>M z3H16lodPgmHlsGyFeav?*-aO|(D(=%zItV{k+h=8m9QBtOwOsj2DcIxseH@<^c)Bi zoek+-Cmq$3R_DfVVeFT*le1cOW+@_sEKFQ)CiSqH-auxzUsjpTbr-IMaWhoA>YVA|{j;k3uFL7S*v0=%fRH%i^7%)FWjY((drMEvlx} z7o~+|Q>jPlU2;*OISM;lBK4%>5JusoKBk|2>KPT?GKZgx>6Y$(Y&_xT(Rc{7C~KrL z1==z)LKFHx-vY$uv@i5o=+MOa(h_m&;JTzut`@hcCAfrgN*XF_{3RsMEJF0H7NJ2x z8^A6gjV*19&dr$3?2A9~FaEG<@)yxTx~brcG_x!!HGkvsMUYUWwlIltY6}&eUqt@U z7EDy^ZJ~)mz6F?-CJZu1C2aviQ>F%EeT;j9@c&UxO`XT7B$zVaSvou_b6pd!q#f6IxpFuut z`8mj6)(61+mcEaCg$AI+NK$EaV3N{~Qx@~8!G|!1)Y%cx@KIF1j)#vyWEUDqDuR4t z&qFpRo6JgepcNEx&Ht!h9a1O7zeRI=n*>>jEQAZGTEBqRbG8fu@&!R43uGv$<^(k9 z5~-k+^3zmwawj3Jw^0C*0-ypB#iGHCEu#-YWu_uqbK@e&7AKqy3yWC; zn~vcCxc^4(ADn zl4tK$9WZ24Ux|bq&9xuwFf|5Ci<}gH0fUqhIoSYARuA|&sym{c;obEEwc_{CB_(q- z%!=+PSW^Eduo#>lAbKvwewApAQGb!{yG~}T=@3eQfe6D$?;wOVO~s&SM-1!LMpbi^3VO9SfvOfF6P@QZ$)|oZyO-j6iI3qRgQv^qLR!yF>ztdaST38d*_U6LHpTbtK0G5l~J$ z7K`gbnpxc8Wct!(-Bu^%WC60!=`Mh>8hDksz0`zz7D*?r^Y75;cStD9Q$}a;-=!f= z1GbFlvCs)Onn}o>YljumTA<>~JZgRqL(^9vT$5Zj(UgnF(+$|=aow_wuPkTyYpPL> zoUrvnbU#F6C@WX~2GaC>h)BcbNW;ZQqe~GkL?@ZeoX2|2&Lhd1Ll+ucObR;}8h7VH zQ{1`Gcsm!G($0mZ%ue9cPf1(F=Gz4O`McZKk<`gi?fxhaF3ZV+HbQ_6w4MbCR$T_1 z0{|D&?or-B9f%;ntqc-(GDwe6JK(xJgm4*T!YtZ6z-yy^n87Pn-C1S#cVq3Jb#0p* zl+aJai^Mz1++}g^=%h7!^WGh4DWG}HFcouQ3l|q@FNW=R+R}sfN=3-w zF3A*l93s}8%Tp8Osx7TFYA$j*M7~6#L5sU@&d%R?_x+o1A7yVh2;z1uf}n$LigO&G z!^N{J9sR7H4XeYM*zJ6Xen>}qCqrQx=@xVhjV}{rQwkbUsR=qMK+6ctj7xsi%J;d9 zLA*&~1>y^xuTuVqpD6@*a~=dew5f>fB|RJ3-Ii*3W>@53$hmw;prTz3^qd;WO{mj0 zKJuBt=Q}y4t zJHCd_&`8x$%jBC->yu`;pFrdd8tAsB2 zDrf8~N0rSj<%OBCqVZSvYY?KCN^P4WK^sR@g9=Fud!wROeRjS_p4dv*7w~2GVavp@SxaKPasB`T9_)9v7 zM|t5xRp{r1X;m1_3s+R(*fyzo`^nR%PqWgE#l2tE`w!HsAOBM%|5s5hE7VDEu?c>E zv)$l-0G_nNv>wM* zFDI-&qejJApHhw@Q%-2%e@=q1!apN%3Idd@$oJ<|@mCOXD0eeKyW3g-aE@@^m^#Hn zU4DsCDc#)w4T1z71&y!E)=_ATnUbj8<~k5W)?dQX9+ksU1U&UOew#Ny-*D!ru@JS_VvbvuEwVHcoUSVQ-G{y< z=CDk8>+{Ea Date: Fri, 13 Oct 2023 14:06:18 -0400 Subject: [PATCH 25/99] We added a wait feature to initialize sensor before reading data. It takes roughly 30-60 sec to start the sensor. Also fixed the keyboad interrupt to properly terminate the program. --- Alphasense/OPC_Simple_v2.py | 99 ++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 41 deletions(-) diff --git a/Alphasense/OPC_Simple_v2.py b/Alphasense/OPC_Simple_v2.py index 1c4f43f..d4ef165 100644 --- a/Alphasense/OPC_Simple_v2.py +++ b/Alphasense/OPC_Simple_v2.py @@ -9,74 +9,91 @@ import subprocess from datetime import datetime # datetime.now() -#This exits one directory at a time so its on the local computer so you dont get csvs on your repository -os.chdir("..") -os.chdir("..") - -spi = SPI('/dev/ttyACM0') -spi.mode = 1 -spi.max_speed_hz = 500000 -spi.lsbfirst = False - +#this is a slightly ugly way to make sure the while loops are killed and the program can exit propperly +global DataLoop +global InitializeLoop +InitializeLoop = True +DataLoop = True - -#This is to hand interuptions and turn the alphasense +# This is to handle interuptions and turn the alphasense def handle_interrupt(signal, frame): + global DataLoop + global InitializeLoop print("Ctrl+C pressed. Performing cleanup or other actions...") + + #exits the while loops + DataLoop = False + InitializeLoop = False + time.sleep(.5) dev.off() + print('sensor off') exit(0) # Terminate the script gracefully signal.signal(signal.SIGINT, handle_interrupt) -#This detects which model of opc and prints the relevent data -dev = opc.detect(spi) -print(f'device information: {dev.info()}') -print(f'serial: {dev.serial()}') - -#this just formats the serial number string to look better -SerialNumberStr = str(dev.serial()) -SerialNumberStr = SerialNumberStr.replace("N3","N3-") -SerialNumberStr = SerialNumberStr.replace(" ","") +#setting up more sensor stuff from the library +spi = SPI('/dev/ttyACM0') +spi.mode = 1 +spi.max_speed_hz = 500000 +spi.lsbfirst = False -print(f'firmware version: {dev.serial()}') -# power on fan and laser +#This loop initializes the sensor and should only run at the start +while InitializeLoop == True: + try: + #This detects which model of opc and prints the relevent data + dev = opc.detect(spi) + # print(type(dev)) + print(f'device information: {dev.info()}') + print(f'serial: {dev.serial()}') + + #this just formats the serial number string to look better + SerialNumberStr = str(dev.serial()) + SerialNumberStr = SerialNumberStr.replace("N3","N3-") + SerialNumberStr = SerialNumberStr.replace(" ","") + + print(f'firmware version: {dev.serial()}') + print('sucessfully connected to device') + break + + except Exception as e: + print('ERROR connecting to sensor, trying again........') + print(e) + time.sleep(1) +#This exits one directory at a time so its on the local computer so you dont get csvs on your repository +os.chdir("..") +os.chdir("..") +#This creates the file and the first row and and the labels fname = '{0}_Alphasense_{1}.csv'.format(datetime.now().strftime("%Y_%m_%d__%H_%M_%S"), SerialNumberStr) file = open(fname,'w') titleStr = 'Serial Number,Date Label, Dates (YMD), Bin 0, Bin 1, Bin 2, Bin 3, Bin 4, Bin 5, Bin 6, Bin 7, Bin 8, Bin 9, Bin 10, Bin 11, Bin 12, Bin 13, Bin 14, Bin 15, Bin 16, Bin 17, Bin 18, Bin 19, Bin 20, Bin 21, Bin 22, Bin 23, Bin1 MToF,Bin3 MToF,Bin5 MToF,Bin7 MToF,Sampling Period,SFR,Temperature,Relative humidity,PM1,PM2.5,PM10,#RejectGlitch,#RejectLongTOF,#RejectRatio,#RejectOutOfRange,Fan rev count,Laser status,Checksum' file.write(titleStr +"\n") file.flush() - - - dev.on() -while True: +#This loop controls data collection +while DataLoop ==True: try: # query particle mass readings time.sleep(1) #print(dev.pm()) - #print(dev.histogram()) recorded_data = dev.histogram() - print(recorded_data) - - except RuntimeError: - print("Unable to read from sensor, retrying...") - continue - + #print(recorded_data) + dataStr = ', {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, {17}, {18}, {19}, {20}, {21}, {22}, {23}, {24}, {25}, {26}, {27}, {28}, {29}, {30}, {31}, {32}, {33}, {34}, {35}, {36}, {37}, {38}, {39}, {40}, {41}'.format(recorded_data.get('Bin 0', ''),recorded_data.get('Bin 1', ''),recorded_data.get('Bin 2', ''),recorded_data.get('Bin 3', ''),recorded_data.get('Bin 4', ''),recorded_data.get('Bin 5', ''),recorded_data.get('Bin 6', ''),recorded_data.get('Bin 7', ''),recorded_data.get('Bin 8', ''),recorded_data.get('Bin 9', ''),recorded_data.get('Bin 10', ''),recorded_data.get('Bin 11', ''),recorded_data.get('Bin 12', ''),recorded_data.get('Bin 13', ''),recorded_data.get('Bin 14', ''),recorded_data.get('Bin 15', ''),recorded_data.get('Bin 16', ''),recorded_data.get('Bin 17', ''),recorded_data.get('Bin 18', ''),recorded_data.get('Bin 19', ''),recorded_data.get('Bin 20', ''),recorded_data.get('Bin 21', ''),recorded_data.get('Bin 22', ''),recorded_data.get('Bin 23', ''),recorded_data.get('Bin1 MToF', ''),recorded_data.get('Bin3 MToF', ''),recorded_data.get('Bin5 MToF', ''),recorded_data.get('Bin7 MToF', ''),recorded_data.get('Sampling Period', ''),recorded_data.get('SFR', ''),recorded_data.get('Temperature', ''),recorded_data.get('Relative humidity', ''),recorded_data.get('PM1', ''),recorded_data.get('PM2.5', ''),recorded_data.get('PM10', ''),recorded_data.get('#RejectGlitch', ''),recorded_data.get('#RejectLongTOF', ''),recorded_data.get('#RejectRatio', ''),recorded_data.get('#RejectOutOfRange', ''),recorded_data.get('Fan rev count', ''),recorded_data.get('Laser status', ''),recorded_data.get('Checksum', '')) - dataStr = ', {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, {17}, {18}, {19}, {20}, {21}, {22}, {23}, {24}, {25}, {26}, {27}, {28}, {29}, {30}, {31}, {32}, {33}, {34}, {35}, {36}, {37}, {38}, {39}, {40}, {41}'.format(recorded_data.get('Bin 0', ''),recorded_data.get('Bin 1', ''),recorded_data.get('Bin 2', ''),recorded_data.get('Bin 3', ''),recorded_data.get('Bin 4', ''),recorded_data.get('Bin 5', ''),recorded_data.get('Bin 6', ''),recorded_data.get('Bin 7', ''),recorded_data.get('Bin 8', ''),recorded_data.get('Bin 9', ''),recorded_data.get('Bin 10', ''),recorded_data.get('Bin 11', ''),recorded_data.get('Bin 12', ''),recorded_data.get('Bin 13', ''),recorded_data.get('Bin 14', ''),recorded_data.get('Bin 15', ''),recorded_data.get('Bin 16', ''),recorded_data.get('Bin 17', ''),recorded_data.get('Bin 18', ''),recorded_data.get('Bin 19', ''),recorded_data.get('Bin 20', ''),recorded_data.get('Bin 21', ''),recorded_data.get('Bin 22', ''),recorded_data.get('Bin 23', ''),recorded_data.get('Bin1 MToF', ''),recorded_data.get('Bin3 MToF', ''),recorded_data.get('Bin5 MToF', ''),recorded_data.get('Bin7 MToF', ''),recorded_data.get('Sampling Period', ''),recorded_data.get('SFR', ''),recorded_data.get('Temperature', ''),recorded_data.get('Relative humidity', ''),recorded_data.get('PM1', ''),recorded_data.get('PM2.5', ''),recorded_data.get('PM10', ''),recorded_data.get('#RejectGlitch', ''),recorded_data.get('#RejectLongTOF', ''),recorded_data.get('#RejectRatio', ''),recorded_data.get('#RejectOutOfRange', ''),recorded_data.get('Fan rev count', ''),recorded_data.get('Laser status', ''),recorded_data.get('Checksum', '')) + dateStr =', Date:, {0}'.format(datetime.now()) - dateStr =', Date:, {0}'.format(datetime.now()) + print("\n" + SerialNumberStr + dateStr + dataStr + "\n") + file.write(SerialNumberStr + dateStr + dataStr + "\n") + file.flush() - print("\n" + SerialNumberStr + dateStr + dataStr + "\n") - file.write(SerialNumberStr + dateStr + dataStr + "\n") - file.flush() - -# power off fan and laser -#dev.off() + except BaseException as e: + print(e) + except RuntimeError: + print("Unable to read from sensor, retrying...") + continue From 923465c8a6cbc95dc3edf02dbd60c5fa0a786e70 Mon Sep 17 00:00:00 2001 From: KalebNails Date: Fri, 13 Oct 2023 15:33:49 -0400 Subject: [PATCH 26/99] fixed the sensirion example and made the RUN code. Also in future add an interrupt for the RUN code so that it stops the sensor from running --- Sensirion/SPS30_Senirion_Run.py | 83 ++++++++++++++++++++ Sensirion/__pycache__/sps30.cpython-310.pyc | Bin 6775 -> 6833 bytes Sensirion/exampleReadSPS30.py | 9 ++- Sensirion/sps30.py | 28 ++++--- 4 files changed, 105 insertions(+), 15 deletions(-) diff --git a/Sensirion/SPS30_Senirion_Run.py b/Sensirion/SPS30_Senirion_Run.py index a034af5..51c6429 100644 --- a/Sensirion/SPS30_Senirion_Run.py +++ b/Sensirion/SPS30_Senirion_Run.py @@ -1,2 +1,85 @@ #Kaleb Nails +# Moral Support: Erik Liebergall #10/11/2023 +#!/usr/bin/env python3 +# +# UART interface to Sparkfun SPS-30: sparkfun.com/products/15103 +# documented here: https://binh-bk.github.io/Sensirion_SPS30/ +# https://github.com/binh-bk/Sensirion_SPS30 +# +# output format (is probably) documented in 4.3 Measurement Output Formats +# at datasheet: https://cdn.sparkfun.com/assets/4/e/e/f/8/Sensirion_PM_Sensors_Datasheet_SPS30.pdf +# +# needs paho-mqtt: +# install with: pip3 install paho-mqtt +# +# Erik Liebergall +# Marc Compere +# Kaleb Nails +# created : 13 Oct 2023 + +from sps30 import get_usb +from sps30 import SPS30 +import time +from datetime import datetime +import os + +device='/dev/ttyUSB0' +#device='/dev/ttyUSB1' + +p = SPS30(port=device, push_mqtt=False) + +SerialNumberStr = p.read_serial_number() +print(SerialNumberStr) + + + +#This exits one directory at a time so its on the local computer so you dont get csvs on your repository +os.chdir("..") +os.chdir("..") + +#This creates the file and the first row and and the labels +fname = '{0}_Sensirion_sps30_{1}.csv'.format(datetime.now().strftime("%Y_%m_%d__%H_%M_%S"), SerialNumberStr) +file = open(fname,'w') +titleStr = 'Serial Number,Date Label, Dates (YMD), Mass Concentration PM1.0 (µg/m³),Mass Concentration PM2.5 (µg/m³),Mass Concentration PM4.0 (µg/m³),Mass Concentration PM10.0 (µg/m³),Number Concentration PM0.5 (µg/m³),Number Concentration PM1.0 (µg/m³),Number Concentration PM2.5 (µg/m³),Number Concentration PM4.0(µg/m³),Number Concentration PM10.0 (µg/m³),Typical Particle Size [µm]' +file.write(titleStr +"\n") +file.flush() + + + +p.start() +#p.stop() + +#This sleep is very important +print(' \n LOADING... \n') +time.sleep(5) + + + +while True: + + #read values + out=p.read_values() + print(out) + #data_struct = {'Mass Concentration PM1.0 (µg/m³)',out[0],'Mass Concentration PM2.5 (µg/m³)',out[1],'Mass Concentration PM4.0 (µg/m³)',out[2],'Mass Concentration PM10.0 (µg/m³)',out[3],'Number Concentration PM0.5 (µg/m³)',out[4],'Number Concentration PM1.0 (µg/m³)',out[5],'Number Concentration PM2.5 (µg/m³)',out[6],'Number Concentration PM4.0(µg/m³)',out[7],'Number Concentration PM10.0 (µg/m³)',out[8],'Typical Particle Size [µm]',out[9]} + #dataStr = '{0}, {1}, {2}, {3}, {4},{5},{6},{7},{7},{8},{9}'.format(data_struct.get('Mass Concentration PM1.0 (µg/m³)',''),data_struct.get('Mass Concentration PM2.5 (µg/m³)',''),data_struct.get('Mass Concentration PM10.0 (µg/m³)',''),data_struct.get('Mass Concentration PM1.0 (µg/m³)',''), + dataStr = ', {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7},{8},{9}'.format(out[0],out[1],out[2],out[3],out[4],out[5],out[6],out[7],out[8],out[9]) + dateStr =', Date:, {0}'.format(datetime.now()) + + + print("\n" + SerialNumberStr + dateStr + dataStr + "\n") + file.write(SerialNumberStr + dateStr + dataStr + "\n") + file.flush() + time.sleep(1) + + +# docStr.append('Mass Concentration PM1.0 (µg/m³)') +# docStr.append('Mass Concentration PM2.5 (µg/m³)') +# docStr.append('Mass Concentration PM4.0 (µg/m³)') +# docStr.append('Mass Concentration PM10.0 (µg/m³)') +# docStr.append('Number Concentration PM0.5 [#/cm³]') +# docStr.append('Number Concentration PM1.0 [#/cm³]') +# docStr.append('Number Concentration PM2.5 [#/cm³]') +# docStr.append('Number Concentration PM4.0 [#/cm³]') +# docStr.append('Number Concentration PM10.0 [#/cm³]') +# docStr.append('Typical Particle Size [µm]') diff --git a/Sensirion/__pycache__/sps30.cpython-310.pyc b/Sensirion/__pycache__/sps30.cpython-310.pyc index 646f5e56874f53bbfe81f6459a1db2cf3bdc476a..d95c53615fd784895aea3eb92534b2647b2adc32 100644 GIT binary patch delta 210 zcmexvveA?`pO=@50SJ`)G*j1X)Z*gA^i+l7)Vz|F%tZx2{Y5q) zLTd7P0g1_Z0^IB@K&}8I&*VA*T}I}~YXlU@M6Jy8b sXkjTP#+94PL~WTE4^G}9zJ~GM3dli3C38Jjn& z3+6I0wr!p&EXBmQeDelTTPDW+li!K2VZ1YWw!~UyW)9}f-jcqIk|k_R9E@y?9E?0b Q$ic`2#9WL_|5&~Q0R81CCjbBd diff --git a/Sensirion/exampleReadSPS30.py b/Sensirion/exampleReadSPS30.py index f670392..81eb10c 100644 --- a/Sensirion/exampleReadSPS30.py +++ b/Sensirion/exampleReadSPS30.py @@ -12,6 +12,7 @@ # # Erik Liebergall, lieberge@my.erau.edu # Marc Compere, comperem@erau.edu +# Kaleb Nails # created : 02 Feb 2023 # modified: 02 Feb 2023 @@ -41,9 +42,13 @@ print('{0}, '.format(docStr[i]), end='') p.start() -p.stop() -time.sleep(1) +#p.stop() + +#This sleep is very important +print(' \n LOADING... \n') +time.sleep(5) while True: + out=p.read_values() for i in range(len(out)): print('{0}, '.format(out[i]), end='') diff --git a/Sensirion/sps30.py b/Sensirion/sps30.py index e94b681..fd84673 100644 --- a/Sensirion/sps30.py +++ b/Sensirion/sps30.py @@ -2,7 +2,7 @@ Binh Nguyen, June 14, 2020 - A fork from Szymon Jakubiak (2018) - additional feature: - 1. push data to MQTT server + 1. push data to MQTT server 2. log file to CSV format (both of them are optional) 3. run multiple SPS30 with USB hub (quality check SPS30 sensors) """ @@ -84,12 +84,12 @@ def record_data(data): def push_mqtt_server(data): '''push data to MQTT server''' - + header = ['sensor','time','PM1','PM25','PM4','PM10','b0305', 'b031','b0325','b034','b0310','tsize'] data = data.split(',') if len(header) == len(data): - print(f'Process for MQTT {data}') + print(f'Process for MQTT {data}') payload = dict(zip(header,data)) payload['type'] = 'json' print(f'MQTT: {payload}') @@ -108,7 +108,7 @@ def push_mqtt_server(data): class SPS30: NAME = 'SPS30' WARMUP = 20 # seconds - + def __init__(self, port, save_data=True, push_mqtt=False, INTERVAL=60): self.port = port self.interval = INTERVAL @@ -124,13 +124,15 @@ def __init__(self, port, save_data=True, push_mqtt=False, INTERVAL=60): def __str__(self): return f'{self.port}, {self.name}, {self.fanOn}, {self.lastSample}' - + def start(self): self.ser.write([0x7E, 0x00, 0x00, 0x02, 0x01, 0x03, 0xF9, 0x7E]) + print('Started message sent') def stop(self): self.ser.write([0x7E, 0x00, 0x01, 0x00, 0xFE, 0x7E]) - + print('stopped message sent') + def read_values(self): self.ser.flushInput() # Ask for data @@ -144,7 +146,7 @@ def read_values(self): print(f'Wait: {toRead}') time.sleep(1) raw = self.ser.read(toRead) - + # Reverse byte-stuffing if b'\x7D\x5E' in raw: raw = raw.replace(b'\x7D\x5E', b'\x7E') @@ -154,16 +156,16 @@ def read_values(self): raw = raw.replace(b'\x7D\x31', b'\x11') if b'\x7D\x33' in raw: raw = raw.replace(b'\x7D\x33', b'\x13') - + # Discard header and tail rawData = raw[5:-2] - + try: data = struct.unpack(">ffffffffff", rawData) except struct.error: data = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) return data - + def read_serial_number(self): self.ser.flushInput() self.ser.write([0x7E, 0x00, 0xD0, 0x01, 0x03, 0x2B, 0x7E]) @@ -173,7 +175,7 @@ def read_serial_number(self): print(f'Wait: {toRead}') time.sleep(1) raw = self.ser.read(toRead) - + # Reverse byte-stuffing if b'\x7D\x5E' in raw: raw = raw.replace(b'\x7D\x5E', b'\x7E') @@ -183,14 +185,14 @@ def read_serial_number(self): raw = raw.replace(b'\x7D\x31', b'\x11') if b'\x7D\x33' in raw: raw = raw.replace(b'\x7D\x33', b'\x13') - + # Discard header, tail and decode serial_number = raw[5:-3].decode('ascii') return serial_number def run_query(self): if time_() - self.lastSample >= self.interval: - if not self.is_started: + if not self.is_started: self.start() self.fanOn = time_() self.is_started = True From 4889faa686f37fbbce0472e2067d82ed9edb2838 Mon Sep 17 00:00:00 2001 From: KalebNails Date: Fri, 27 Oct 2023 13:21:40 -0400 Subject: [PATCH 27/99] Added an option to dictate what port you want to use in the command line, also commmented out some the title string so now this is for one sender --- PMS plantower/pm25_simpletest.py | 78 ++++++++++++++++---------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/PMS plantower/pm25_simpletest.py b/PMS plantower/pm25_simpletest.py index cc11836..25fe34e 100644 --- a/PMS plantower/pm25_simpletest.py +++ b/PMS plantower/pm25_simpletest.py @@ -22,14 +22,20 @@ """ # pylint: disable=unused-import + + import time import board -import busio +import busio #pip3 install adafruit-blinka import os +import sys from datetime import datetime # datetime.now() from digitalio import DigitalInOut, Direction, Pull #from adafruit_pm25.i2c import PM25_I2C +# For use with USB-to-serial cable: +import serial + # go back 2 directories to store CSV's os.chdir("..") os.chdir("..") @@ -53,17 +59,37 @@ # import serial # uart = serial.Serial("/dev/ttyS0", baudrate=9600, timeout=0.25) -# For use with USB-to-serial cable: -import serial dev1='/dev/ttyUSB0' # dev2='/dev/ttyUSB2' + +if len(sys.argv)==1: + #print('sys.argv[0]={0}'.format(sys.argv[0])) + print('provide a device name to read, like:') + print(' python3 pm25_simpletest.py /dev/ttyUSB0' +"\n") + print('\033[91mWARNING: DEFAULT PORT WILL BE USB0\033[0m' +"\n") + print('this default setting was left for developement' + "\n" + "\n") + time.sleep(2.5) + + + +if len(sys.argv)>1: + print('using command line arg, and provided device!') + dev1=sys.argv[1] + +devName=os.path.basename(dev1) # get device name for logfile name +print('using devName=[{0}]'.format(devName)) + + + + + uart = serial.Serial(dev1, baudrate=9600, timeout=0.25) # uart2 = serial.Serial(dev2, baudrate=9600, timeout=0.25) # Connect to a PM2.5 sensor over UART -from adafruit_pm25.uart import PM25_UART +from adafruit_pm25.uart import PM25_UART #pip3 install adafruit-circuitpython-pm25 pm25 = PM25_UART(uart, reset_pin) # pm25_2 = PM25_UART(uart2, reset_pin) @@ -73,12 +99,12 @@ #pm25 = PM25_I2C(i2c, reset_pin) fname = '{0}_pm25_simplest_CSV.csv'.format(datetime.now().strftime("%Y_%m_%d__%H_%M_%S") ) file = open(fname,'w') - + print("Found PM2.5 sensor, reading data...") #adds heading columns #Add Labels to the top of the file TEST this works -titleStr = ',Date Label, Dates (YMD), Sensor 1, pm10 standard, pm25 standard, pm100 standard, pm10 env, pm25 env, pm100 env,particles 03um, particles 05um, particles 10um, particles 25um, particles 50um, particles 100um, Dates (YMD), Sensor 2, pm10 standard, pm25 standard, pm100 standard, pm10 env, pm25 env, pm100 env,particles 03um, particles 05um, particles 10um, particles 25um, particles 50um, particles 100um' +titleStr = ',Date Label, Dates (YMD), Sensor 1, pm10 standard, pm25 standard, pm100 standard, pm10 env, pm25 env, pm100 env,particles 03um, particles 05um, particles 10um, particles 25um, particles 50um, particles 100um'#, Dates (YMD), Sensor 2, pm10 standard, pm25 standard, pm100 standard, pm10 env, pm25 env, pm100 env,particles 03um, particles 05um, particles 10um, particles 25um, particles 50um, particles 100um' file.write(titleStr +"\n") file.flush() @@ -148,36 +174,36 @@ # print("Particles2 > 5.0um / 0.1L air:", aqdata2["particles 50um"]) # print("Particles2 > 10 um / 0.1L air:", aqdata2["particles 100um"]) # print("---------------------------------------") - + # dateStr2 =', Date:, {0}'.format(datetime.now()) #dataStr1 = ', Data1:, {0}, {1}, {2}, {3}, {4}, {5}'.format(aqdata["particles 03um"],aqdata["particles 05um"],aqdata["particles 10um"],aqdata["particles 25um"],aqdata["particles 50um"],aqdata["particles 100um"]) #dataStr2 = ', Data2:, {0}, {1}, {2}, {3}, {4}, {5}'.format(aqdata2["particles 03um"],aqdata2["particles 05um"],aqdata2["particles 10um"],aqdata2["particles 25um"],aqdata2["particles 50um"],aqdata2["particles 100um"]) - + dataStr1 = ', Data1:, {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}'.format( \ aqdata["pm10 standard"], \ aqdata["pm25 standard"], \ aqdata["pm100 standard"], \ - + aqdata["pm10 env"], \ aqdata["pm25 env"], \ aqdata["pm100 env"], \ - + aqdata["particles 03um"], \ aqdata["particles 05um"], \ aqdata["particles 10um"], \ aqdata["particles 25um"], \ aqdata["particles 50um"], \ aqdata["particles 100um"]) - + # dataStr2 = ', Data2:, {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}'.format( \ # aqdata2["pm10 standard"], \ # aqdata2["pm25 standard"], \ # aqdata2["pm100 standard"], \ - + # aqdata2["pm10 env"], \ # aqdata2["pm25 env"], \ # aqdata2["pm100 env"], \ - + # aqdata2["particles 03um"], \ # aqdata2["particles 05um"], \ # aqdata2["particles 10um"], \ @@ -188,29 +214,3 @@ file.write(dateStr1 + dataStr1 + "\n") file.flush() - - - - - - - - - - - - - - - - - - - - - - - - - - From 1c110ff655246e7d7521daab098e10f1a3f9e969 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 27 Oct 2023 13:31:26 -0400 Subject: [PATCH 28/99] Create README.md --- PMS plantower/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 PMS plantower/README.md diff --git a/PMS plantower/README.md b/PMS plantower/README.md new file mode 100644 index 0000000..8e4024d --- /dev/null +++ b/PMS plantower/README.md @@ -0,0 +1,10 @@ +Official Documentation: +https://www.aqmd.gov/docs/default-source/aq-spec/resources-page/plantower-pms5003-manual_v2-3.pdf + +Necessary uncommon imports pip commands are found on the line they are imported in. + +![Alt Text](https://m.media-amazon.com/images/I/71+cgzmX+pL._AC_SX679_.jpg) + +BELOW ARE IMAGES ON HOW WE WIRE THEM (not for everyone): +![image](https://github.com/MOVEUAS/Sensor_Code/assets/117048000/d1159ea7-c33e-477f-a33f-659150fc29de) +![image](https://github.com/MOVEUAS/Sensor_Code/assets/117048000/ca50b5bd-99b6-46cc-95a3-b07300ec5d48) From 9c7b2be5051cc85440d37ea4bc5675e79f287da3 Mon Sep 17 00:00:00 2001 From: KalebNails Date: Fri, 27 Oct 2023 13:56:31 -0400 Subject: [PATCH 29/99] added some extra prints and a print for time so its easy to see the values change --- PMS plantower/pm25_simpletest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PMS plantower/pm25_simpletest.py b/PMS plantower/pm25_simpletest.py index 25fe34e..3821804 100644 --- a/PMS plantower/pm25_simpletest.py +++ b/PMS plantower/pm25_simpletest.py @@ -122,6 +122,7 @@ print() print("Concentration Units (standard)") + print(f"TIME: {time.strftime('%H:%M:%S', time.localtime())}") print("---------------------------------------") print( "PM 1.0: %d\tPM2.5: %d\tPM10: %d" @@ -144,7 +145,7 @@ dateStr1 =', Date:, {0}'.format(datetime.now()) # while False: - time.sleep(1) + #time.sleep(1) # try: # aqdata2 = pm25_2.read() From ecfe6eb75b447dd7d90a2008554e5172886297a7 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 27 Oct 2023 13:58:57 -0400 Subject: [PATCH 30/99] Update README.md --- PMS plantower/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/PMS plantower/README.md b/PMS plantower/README.md index 8e4024d..1ab97e0 100644 --- a/PMS plantower/README.md +++ b/PMS plantower/README.md @@ -1,3 +1,4 @@ +# Documentation # Official Documentation: https://www.aqmd.gov/docs/default-source/aq-spec/resources-page/plantower-pms5003-manual_v2-3.pdf @@ -5,6 +6,18 @@ Necessary uncommon imports pip commands are found on the line they are imported ![Alt Text](https://m.media-amazon.com/images/I/71+cgzmX+pL._AC_SX679_.jpg) + + +# Personal Knowledge # +For this code run: python3 pm25_simpletest.py +this will default to USB0 of your computer, and you will get a warning as well. + +If you have multiple sensors or its just on another port use: +python3 pm25_simpletest.py /dev/ttyUSB# + +The # is the port it is on. + + BELOW ARE IMAGES ON HOW WE WIRE THEM (not for everyone): ![image](https://github.com/MOVEUAS/Sensor_Code/assets/117048000/d1159ea7-c33e-477f-a33f-659150fc29de) ![image](https://github.com/MOVEUAS/Sensor_Code/assets/117048000/ca50b5bd-99b6-46cc-95a3-b07300ec5d48) From 30a3279f13fb7516eedc96aa4f8e16d8afd7b5b8 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 27 Oct 2023 14:00:13 -0400 Subject: [PATCH 31/99] Update README.md --- PMS plantower/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PMS plantower/README.md b/PMS plantower/README.md index 1ab97e0..5a6289f 100644 --- a/PMS plantower/README.md +++ b/PMS plantower/README.md @@ -3,7 +3,7 @@ Official Documentation: https://www.aqmd.gov/docs/default-source/aq-spec/resources-page/plantower-pms5003-manual_v2-3.pdf Necessary uncommon imports pip commands are found on the line they are imported in. - +(THIS IMAGE IS NOT THE EXACT SENSOR, BUT THEY ARE THE SAME AT A GLANCE, just for reference) ![Alt Text](https://m.media-amazon.com/images/I/71+cgzmX+pL._AC_SX679_.jpg) From f69e2e803eb08b4099534c8025e115f90d05a9f0 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 27 Oct 2023 15:34:49 -0400 Subject: [PATCH 32/99] Update sps30.py added pip comment to import paho --- Sensirion/sps30.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sensirion/sps30.py b/Sensirion/sps30.py index fd84673..c46f043 100644 --- a/Sensirion/sps30.py +++ b/Sensirion/sps30.py @@ -13,7 +13,7 @@ import os import json import socket -import paho.mqtt.publish as publish +import paho.mqtt.publish as publish #pip install paho-mqtt # set localtime os.environ['TZ'] = 'Asia/Ho_Chi_Minh' From 5316c3c7cabc54f41dda17c00bcdcf5c92d80cc7 Mon Sep 17 00:00:00 2001 From: KalebNails Date: Fri, 27 Oct 2023 15:40:31 -0400 Subject: [PATCH 33/99] added a system arguement to the sensirion --- Sensirion/SPS30_Senirion_Run.py | 21 ++++++++++++++++++++ Sensirion/__pycache__/sps30.cpython-310.pyc | Bin 6833 -> 6774 bytes Sensirion/sps30.py | 4 +--- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/Sensirion/SPS30_Senirion_Run.py b/Sensirion/SPS30_Senirion_Run.py index 51c6429..ab4bb01 100644 --- a/Sensirion/SPS30_Senirion_Run.py +++ b/Sensirion/SPS30_Senirion_Run.py @@ -23,10 +23,31 @@ import time from datetime import datetime import os +import sys + device='/dev/ttyUSB0' #device='/dev/ttyUSB1' + + +if len(sys.argv)==1: + #print('sys.argv[0]={0}'.format(sys.argv[0])) + print('provide a device name to read, like:') + print(' python3 pm25_SPS30_Senirion_Run.py /dev/ttyUSB0' +"\n") + print('\033[91mWARNING: DEFAULT PORT WILL BE USB0\033[0m' +"\n") + print('this default setting was left for developement' + "\n" + "\n") + time.sleep(2.5) + + + +if len(sys.argv)>1: + print('using command line arg, and provided device!') + device=sys.argv[1] + +devName=os.path.basename(device) # get device name for logfile name +print('using devName=[{0}]'.format(devName)+"\n") + p = SPS30(port=device, push_mqtt=False) SerialNumberStr = p.read_serial_number() diff --git a/Sensirion/__pycache__/sps30.cpython-310.pyc b/Sensirion/__pycache__/sps30.cpython-310.pyc index d95c53615fd784895aea3eb92534b2647b2adc32..2bfb204ba910876d4de7953f028eb035a4889d2b 100644 GIT binary patch delta 155 zcmdmJ`ptwlpO=@50SE%;Xr@MN$y`(b)L&!+BBUmt6OfpkE5Oap z0^|xX@=UH3&}C$vyjnm}tV*P~B)^~lP5b0C0`iP4o8JiJGBI{+juMt)VqCeoRMeJ< g@!;gm;%gZ1O-_(l%gn;TvROdVmyxk-a-5U|07pAF00000 diff --git a/Sensirion/sps30.py b/Sensirion/sps30.py index c46f043..7fc16b9 100644 --- a/Sensirion/sps30.py +++ b/Sensirion/sps30.py @@ -127,11 +127,9 @@ def __str__(self): def start(self): self.ser.write([0x7E, 0x00, 0x00, 0x02, 0x01, 0x03, 0xF9, 0x7E]) - print('Started message sent') - + def stop(self): self.ser.write([0x7E, 0x00, 0x01, 0x00, 0xFE, 0x7E]) - print('stopped message sent') def read_values(self): self.ser.flushInput() From cd321c317e1174f4ec3325cf5351d7fdbb104e70 Mon Sep 17 00:00:00 2001 From: KalebNails Date: Fri, 27 Oct 2023 15:45:00 -0400 Subject: [PATCH 34/99] added a system arguement to the sensirion --- Alphasense/OPC_Simple_v2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Alphasense/OPC_Simple_v2.py b/Alphasense/OPC_Simple_v2.py index d4ef165..eeeb9f9 100644 --- a/Alphasense/OPC_Simple_v2.py +++ b/Alphasense/OPC_Simple_v2.py @@ -82,7 +82,7 @@ def handle_interrupt(signal, frame): time.sleep(1) #print(dev.pm()) recorded_data = dev.histogram() - #print(recorded_data) + print(recorded_data) dataStr = ', {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, {17}, {18}, {19}, {20}, {21}, {22}, {23}, {24}, {25}, {26}, {27}, {28}, {29}, {30}, {31}, {32}, {33}, {34}, {35}, {36}, {37}, {38}, {39}, {40}, {41}'.format(recorded_data.get('Bin 0', ''),recorded_data.get('Bin 1', ''),recorded_data.get('Bin 2', ''),recorded_data.get('Bin 3', ''),recorded_data.get('Bin 4', ''),recorded_data.get('Bin 5', ''),recorded_data.get('Bin 6', ''),recorded_data.get('Bin 7', ''),recorded_data.get('Bin 8', ''),recorded_data.get('Bin 9', ''),recorded_data.get('Bin 10', ''),recorded_data.get('Bin 11', ''),recorded_data.get('Bin 12', ''),recorded_data.get('Bin 13', ''),recorded_data.get('Bin 14', ''),recorded_data.get('Bin 15', ''),recorded_data.get('Bin 16', ''),recorded_data.get('Bin 17', ''),recorded_data.get('Bin 18', ''),recorded_data.get('Bin 19', ''),recorded_data.get('Bin 20', ''),recorded_data.get('Bin 21', ''),recorded_data.get('Bin 22', ''),recorded_data.get('Bin 23', ''),recorded_data.get('Bin1 MToF', ''),recorded_data.get('Bin3 MToF', ''),recorded_data.get('Bin5 MToF', ''),recorded_data.get('Bin7 MToF', ''),recorded_data.get('Sampling Period', ''),recorded_data.get('SFR', ''),recorded_data.get('Temperature', ''),recorded_data.get('Relative humidity', ''),recorded_data.get('PM1', ''),recorded_data.get('PM2.5', ''),recorded_data.get('PM10', ''),recorded_data.get('#RejectGlitch', ''),recorded_data.get('#RejectLongTOF', ''),recorded_data.get('#RejectRatio', ''),recorded_data.get('#RejectOutOfRange', ''),recorded_data.get('Fan rev count', ''),recorded_data.get('Laser status', ''),recorded_data.get('Checksum', '')) From 3ef8967409bac37863226c0be650760c1fefbcb1 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 6 Dec 2023 16:30:44 -0500 Subject: [PATCH 35/99] Update OPC_Simple_v2.py added an a command line input to run multiple sensors --- Alphasense/OPC_Simple_v2.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Alphasense/OPC_Simple_v2.py b/Alphasense/OPC_Simple_v2.py index eeeb9f9..0cd7192 100644 --- a/Alphasense/OPC_Simple_v2.py +++ b/Alphasense/OPC_Simple_v2.py @@ -33,8 +33,28 @@ def handle_interrupt(signal, frame): signal.signal(signal.SIGINT, handle_interrupt) +device='/dev/ttyACM0' +#device='/dev/ttyUSB1' + + + +if len(sys.argv)==1: + #print('sys.argv[0]={0}'.format(sys.argv[0])) + print('provide a device name to read, like:') + print(' python3 pm25_SPS30_Senirion_Run.py /dev/ttyACM0' +"\n") + print('\033[91mWARNING: DEFAULT PORT WILL BE ACM0\033[0m' +"\n") + print('this default setting was left for developement' + "\n" + "\n") + time.sleep(2.5) + + + +if len(sys.argv)>1: + print('using command line arg, and provided device!') + device=sys.argv[1] + + #setting up more sensor stuff from the library -spi = SPI('/dev/ttyACM0') +spi = SPI(device) spi.mode = 1 spi.max_speed_hz = 500000 spi.lsbfirst = False From 6360c3d7aaa0c1d8bcba9d8536de2193dad6b39e Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 13 Dec 2023 12:22:32 -0500 Subject: [PATCH 36/99] Update pm25_simpletest.py changed headers --- PMS plantower/pm25_simpletest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PMS plantower/pm25_simpletest.py b/PMS plantower/pm25_simpletest.py index 3821804..8134011 100644 --- a/PMS plantower/pm25_simpletest.py +++ b/PMS plantower/pm25_simpletest.py @@ -97,14 +97,14 @@ #i2c = busio.I2C(board.SCL, board.SDA, frequency=100000) # Connect to a PM2.5 sensor over I2C #pm25 = PM25_I2C(i2c, reset_pin) -fname = '{0}_pm25_simplest_CSV.csv'.format(datetime.now().strftime("%Y_%m_%d__%H_%M_%S") ) +fname = '{0}_pm25_simplest_plantower_CSV.csv'.format(datetime.now().strftime("%Y_%m_%d__%H_%M_%S") ) file = open(fname,'w') print("Found PM2.5 sensor, reading data...") #adds heading columns #Add Labels to the top of the file TEST this works -titleStr = ',Date Label, Dates (YMD), Sensor 1, pm10 standard, pm25 standard, pm100 standard, pm10 env, pm25 env, pm100 env,particles 03um, particles 05um, particles 10um, particles 25um, particles 50um, particles 100um'#, Dates (YMD), Sensor 2, pm10 standard, pm25 standard, pm100 standard, pm10 env, pm25 env, pm100 env,particles 03um, particles 05um, particles 10um, particles 25um, particles 50um, particles 100um' +titleStr = ',Date Label, Dates (YMD), Sensor 1, pm1.0 standard ug/m3, pm2.5 standard ug/m3, pm10.0 standard ug/m3, pm1.0 env ug/m3, pm2.5 env ug/m3, pm10.0 env ug/m3,particles 0.3um, particles 0.5um, particles 1.0um, particles 2.5um, particles 5.0um, particles 10.0um'#, Dates (YMD), Sensor 2, pm10 standard, pm25 standard, pm100 standard, pm10 env, pm25 env, pm100 env,particles 03um, particles 05um, particles 10um, particles 25um, particles 50um, particles 100um' file.write(titleStr +"\n") file.flush() From 0195cab882503b94ea78093856695bd2b4d4c537 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 13 Dec 2023 12:31:26 -0500 Subject: [PATCH 37/99] Update README.md added an image from the documentation --- Alphasense/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Alphasense/README.md b/Alphasense/README.md index 5f08850..f52d16c 100644 --- a/Alphasense/README.md +++ b/Alphasense/README.md @@ -1 +1,7 @@ This is code to read the alphasense through USB. There is also code to read it through the aurduino if thats possible. + + + +Below is an image from the datasheet: +![image](https://github.com/MOVEUAS/Sensor_Code/assets/117048000/1c60443e-5b40-4c41-8a4c-d5d731657155) + From e0823fabd06e0a01e15d31428113bc2880797690 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 13 Dec 2023 12:50:33 -0500 Subject: [PATCH 38/99] Update OPC_Simple_v2.py comfirmed order & documented data order --- Alphasense/OPC_Simple_v2.py | 60 +++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/Alphasense/OPC_Simple_v2.py b/Alphasense/OPC_Simple_v2.py index 0cd7192..b628286 100644 --- a/Alphasense/OPC_Simple_v2.py +++ b/Alphasense/OPC_Simple_v2.py @@ -1,5 +1,6 @@ -#Created by: Kaleb Nails, Erik Liebergall -#Date: 10/6/2023 +#Created by: Kaleb Nails, Erik Liebergall, Marc Compere +#created : 10/6/2023 +#modified: 13 Dec 2023 #from time import sleep from usbiss.spi import SPI import opcng as opc @@ -104,7 +105,59 @@ def handle_interrupt(signal, frame): recorded_data = dev.histogram() print(recorded_data) - dataStr = ', {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, {17}, {18}, {19}, {20}, {21}, {22}, {23}, {24}, {25}, {26}, {27}, {28}, {29}, {30}, {31}, {32}, {33}, {34}, {35}, {36}, {37}, {38}, {39}, {40}, {41}'.format(recorded_data.get('Bin 0', ''),recorded_data.get('Bin 1', ''),recorded_data.get('Bin 2', ''),recorded_data.get('Bin 3', ''),recorded_data.get('Bin 4', ''),recorded_data.get('Bin 5', ''),recorded_data.get('Bin 6', ''),recorded_data.get('Bin 7', ''),recorded_data.get('Bin 8', ''),recorded_data.get('Bin 9', ''),recorded_data.get('Bin 10', ''),recorded_data.get('Bin 11', ''),recorded_data.get('Bin 12', ''),recorded_data.get('Bin 13', ''),recorded_data.get('Bin 14', ''),recorded_data.get('Bin 15', ''),recorded_data.get('Bin 16', ''),recorded_data.get('Bin 17', ''),recorded_data.get('Bin 18', ''),recorded_data.get('Bin 19', ''),recorded_data.get('Bin 20', ''),recorded_data.get('Bin 21', ''),recorded_data.get('Bin 22', ''),recorded_data.get('Bin 23', ''),recorded_data.get('Bin1 MToF', ''),recorded_data.get('Bin3 MToF', ''),recorded_data.get('Bin5 MToF', ''),recorded_data.get('Bin7 MToF', ''),recorded_data.get('Sampling Period', ''),recorded_data.get('SFR', ''),recorded_data.get('Temperature', ''),recorded_data.get('Relative humidity', ''),recorded_data.get('PM1', ''),recorded_data.get('PM2.5', ''),recorded_data.get('PM10', ''),recorded_data.get('#RejectGlitch', ''),recorded_data.get('#RejectLongTOF', ''),recorded_data.get('#RejectRatio', ''),recorded_data.get('#RejectOutOfRange', ''),recorded_data.get('Fan rev count', ''),recorded_data.get('Laser status', ''),recorded_data.get('Checksum', '')) + dataStr = ', {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, {17}, {18}, {19}, {20}, {21}, {22}, {23},\ + {24}, {25}, {26}, {27},\ + {28}, {29}, {30}, {31},\ + {32}, {33}, {34}, \ + {35}, {36}, {37}, {38}, {39}, {40}, {41}'.format( + recorded_data.get('Bin 0', ''), + recorded_data.get('Bin 1', ''), + recorded_data.get('Bin 2', ''), + recorded_data.get('Bin 3', ''), + recorded_data.get('Bin 4', ''), + recorded_data.get('Bin 5', ''), + recorded_data.get('Bin 6', ''), + recorded_data.get('Bin 7', ''), + recorded_data.get('Bin 8', ''), + recorded_data.get('Bin 9', ''), + recorded_data.get('Bin 10', ''), + recorded_data.get('Bin 11', ''), + recorded_data.get('Bin 12', ''), + recorded_data.get('Bin 13', ''), + recorded_data.get('Bin 14', ''), + recorded_data.get('Bin 15', ''), + recorded_data.get('Bin 16', ''), + recorded_data.get('Bin 17', ''), + recorded_data.get('Bin 18', ''), + recorded_data.get('Bin 19', ''), + recorded_data.get('Bin 20', ''), + recorded_data.get('Bin 21', ''), + recorded_data.get('Bin 22', ''), + recorded_data.get('Bin 23', ''), + recorded_data.get('Bin1 MToF', ''), # 24 + #recorded_data.get('Bin2 MToF', ''), + recorded_data.get('Bin3 MToF', ''), # 25 + #recorded_data.get('Bin4 MToF', ''), + recorded_data.get('Bin5 MToF', ''), # 26 + #recorded_data.get('Bin6 MToF', ''), + recorded_data.get('Bin7 MToF', ''), # 27 + + recorded_data.get('Sampling Period', ''), # 28 + recorded_data.get('SFR', ''), # 29 + recorded_data.get('Temperature', ''), # 30 + recorded_data.get('Relative humidity', ''), # 31 + + recorded_data.get('PM1', ''), # 32 (ug/m3), datasheet pdf page 13 + recorded_data.get('PM2.5', ''), # 33 (ug/m3) + recorded_data.get('PM10', ''), # 34 (ug/m3) + + recorded_data.get('#RejectGlitch', ''), # 35 + recorded_data.get('#RejectLongTOF', ''), # 36 + recorded_data.get('#RejectRatio', ''), # 37 + recorded_data.get('#RejectOutOfRange', ''), # 38 + recorded_data.get('Fan rev count', ''), # 39 + recorded_data.get('Laser status', ''), # 40 + recorded_data.get('Checksum', '')) # 41 dateStr =', Date:, {0}'.format(datetime.now()) @@ -117,3 +170,4 @@ def handle_interrupt(signal, frame): except RuntimeError: print("Unable to read from sensor, retrying...") continue + From 6cc8947da1220a2f1082a971fb95e77bc5169f4b Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 13 Dec 2023 12:52:25 -0500 Subject: [PATCH 39/99] Update OPC_Simple_v2.py added units to the headers --- Alphasense/OPC_Simple_v2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Alphasense/OPC_Simple_v2.py b/Alphasense/OPC_Simple_v2.py index b628286..b7daa09 100644 --- a/Alphasense/OPC_Simple_v2.py +++ b/Alphasense/OPC_Simple_v2.py @@ -91,7 +91,7 @@ def handle_interrupt(signal, frame): #This creates the file and the first row and and the labels fname = '{0}_Alphasense_{1}.csv'.format(datetime.now().strftime("%Y_%m_%d__%H_%M_%S"), SerialNumberStr) file = open(fname,'w') -titleStr = 'Serial Number,Date Label, Dates (YMD), Bin 0, Bin 1, Bin 2, Bin 3, Bin 4, Bin 5, Bin 6, Bin 7, Bin 8, Bin 9, Bin 10, Bin 11, Bin 12, Bin 13, Bin 14, Bin 15, Bin 16, Bin 17, Bin 18, Bin 19, Bin 20, Bin 21, Bin 22, Bin 23, Bin1 MToF,Bin3 MToF,Bin5 MToF,Bin7 MToF,Sampling Period,SFR,Temperature,Relative humidity,PM1,PM2.5,PM10,#RejectGlitch,#RejectLongTOF,#RejectRatio,#RejectOutOfRange,Fan rev count,Laser status,Checksum' +titleStr = 'Serial Number,Date Label, Dates (YMD), Bin 0, Bin 1, Bin 2, Bin 3, Bin 4, Bin 5, Bin 6, Bin 7, Bin 8, Bin 9, Bin 10, Bin 11, Bin 12, Bin 13, Bin 14, Bin 15, Bin 16, Bin 17, Bin 18, Bin 19, Bin 20, Bin 21, Bin 22, Bin 23, Bin1 MToF,Bin3 MToF,Bin5 MToF,Bin7 MToF,Sampling Period,SFR,Temperature C,Relative humidity,PM1 ug/m3,PM2.5 ug/m3,PM10 ug/m3,#RejectGlitch,#RejectLongTOF,#RejectRatio,#RejectOutOfRange,Fan rev count,Laser status,Checksum' file.write(titleStr +"\n") file.flush() dev.on() From 32f797deb015e66ae7927f70344eadcf7ef0db8e Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 13 Dec 2023 13:00:31 -0500 Subject: [PATCH 40/99] Update SPS30_Senirion_Run.py changed the number concentration unit to be corrected --- Sensirion/SPS30_Senirion_Run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sensirion/SPS30_Senirion_Run.py b/Sensirion/SPS30_Senirion_Run.py index ab4bb01..a0d1370 100644 --- a/Sensirion/SPS30_Senirion_Run.py +++ b/Sensirion/SPS30_Senirion_Run.py @@ -62,7 +62,7 @@ #This creates the file and the first row and and the labels fname = '{0}_Sensirion_sps30_{1}.csv'.format(datetime.now().strftime("%Y_%m_%d__%H_%M_%S"), SerialNumberStr) file = open(fname,'w') -titleStr = 'Serial Number,Date Label, Dates (YMD), Mass Concentration PM1.0 (µg/m³),Mass Concentration PM2.5 (µg/m³),Mass Concentration PM4.0 (µg/m³),Mass Concentration PM10.0 (µg/m³),Number Concentration PM0.5 (µg/m³),Number Concentration PM1.0 (µg/m³),Number Concentration PM2.5 (µg/m³),Number Concentration PM4.0(µg/m³),Number Concentration PM10.0 (µg/m³),Typical Particle Size [µm]' +titleStr = 'Serial Number,Date Label, Dates (YMD), Mass Concentration PM1.0 (µg/m³),Mass Concentration PM2.5 (µg/m³),Mass Concentration PM4.0 (µg/m³),Mass Concentration PM10.0 (µg/m³),Number Concentration PM0.5 (µg/m³),Number Concentration PM1.0 (#/cm³),Number Concentration PM2.5 (#/cm³),Number Concentration PM4.0(#/cm³),Number Concentration PM10.0 (#/cm³),Typical Particle Size [µm]' file.write(titleStr +"\n") file.flush() From e4b09a3fd4df5b1e7bdf189e8bd2073fef3ec8ce Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 13 Dec 2023 13:01:21 -0500 Subject: [PATCH 41/99] Update README.md --- Sensirion/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sensirion/README.md b/Sensirion/README.md index 089d6e7..f3d8c7b 100644 --- a/Sensirion/README.md +++ b/Sensirion/README.md @@ -30,3 +30,6 @@ black -black white - purple green -white nothing - green + +HERE IS THE OUTPUT: +![image](https://github.com/MOVEUAS/Sensor_Code/assets/117048000/bbf6a458-fb85-43ca-ae11-5ab87164ae53) From 8bfb6f926b4256ff7c5d85e384fd138c735c6e59 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 13 Dec 2023 13:01:32 -0500 Subject: [PATCH 42/99] Update README.md --- Sensirion/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sensirion/README.md b/Sensirion/README.md index f3d8c7b..f592b7b 100644 --- a/Sensirion/README.md +++ b/Sensirion/README.md @@ -32,4 +32,7 @@ green -white nothing - green HERE IS THE OUTPUT: + + + ![image](https://github.com/MOVEUAS/Sensor_Code/assets/117048000/bbf6a458-fb85-43ca-ae11-5ab87164ae53) From 1a435fae56a5c08a28ce1ffba665ce284677bac3 Mon Sep 17 00:00:00 2001 From: KalebNails Date: Fri, 26 Jan 2024 16:48:58 -0500 Subject: [PATCH 43/99] added gui files --- gui_popup.py | 215 ++++++++++++++++++++++++++++++++++++++ sensors_UART_configs.json | 5 + 2 files changed, 220 insertions(+) create mode 100755 gui_popup.py create mode 100644 sensors_UART_configs.json diff --git a/gui_popup.py b/gui_popup.py new file mode 100755 index 0000000..6512d19 --- /dev/null +++ b/gui_popup.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python3 +#Kaleb Nails +from tkinter import * +import json +import subprocess +import os +import sys +root = Tk() # create parent window +#Set the geometry +root.geometry("750x280") +root.title("MOVEUAS_WIZARD.exe") + +#screen -dm bash -c "./OPC_Simple_v2.py;exec sh" + + +#This will actually run the sensors based on the json given +def run_sensors(sensor_ports): + subprocess.run("pkill screen", shell=True,text=True) + + #This is so i can get the home directory of any computer + #home_directory = os.path.expanduser('~') + + + + for i in sensor_ports['alphasense']: + print(i) + try: + command = "sleep 15;./Alphasense/OPC_Simple_v2.py" +" " + i + ";exec sh" + #p3 = subprocess.run(["sudo", "screen", "-dm", "./OPC_Simple_v2.py"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) # you can also put stderr to stdout + #p3 = subprocess.run(["screen", "-dm","bash","-c" , "sleep 15;./Alphasense/OPC_Simple_v2.py;exec sh"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) # you can also put stderr to stdout + + p3 = subprocess.run(["screen", "-dm","bash","-c" , command], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) # you can also put stderr to stdout + + + + + + print('----') + print('p3 = {}'.format(p3)) + print('----') + + except subprocess.CalledProcessError as err: + print( "\n\ncaught an rsync() subprocess error: {0}".format(err) ) + print( "exiting.") + sys.exit(-1) + + print('p.stdout = {}'.format(p3.stdout.decode('utf-8'))) + + #print(command) + + # Expand the tilde (~) to the full home directory path + #home_directory = os.path.expanduser('~') + + # Specify the relative path from the home directory + #relative_path = 'Sensor_Code/Alphasense' + + # Combine the home directory and the relative path + #full_path = os.path.join(home_directory, relative_path) + + #subprocess.call(['/bin/bash', '-x', 'ls']) + + # Now, you can use the full path as the current working directory + #subprocess.run(['lxterminal', '-e', 'python3', '-c', "print('hiiiiii')"], cwd=full_path, shell=False) + #subprocess.run(["lxterminal","-e","cd" "Sensor_Code","cd" "Alphasense","sudo","python3","print('hi')"]) + + + #subprocess.call(["lxterminal","-e","sudo","nano","/boot/config.txt"]) + #subprocess.call(["lxterminal","-e","bash","sleep","10","cd" "Sensor_Code","cd" "Alphasense"]) + #subprocess.run(["lxterminal", "-e", "bash", "-c", "cd Sensor_Code/Alphasense && sudo python3 -c 'print(\"hi\")'"]) + + + + + #This opens a new screen + #open_screen_command = f"screen -dmS alphasense{i.replace('/', '_')}" + ##open_screen_command = f"screen -S alphasense{i.replace('/', '_')}" + + #print( "OPEN_SCREEN_COMMAND: " + open_screen_command) + #subprocess.call(f"screen -dmS alphasense{i.replace('/', '_')}",shell=True) + + #subprocess.call(f"screen -dmS alphasense{i.replace('/', '_')}", shell=True) + #subprocess.call(f"screen -S alphasense{i.replace('/', '_')} -X stuff 'echo test_hello\\15'", shell=True) + #subprocess.call(f"screen -S alphasense{i.replace('/', '_')} -X stuff 'ls'$(echo -ne '\015')", shell=True) + + #subprocess.call(f"screen -S alphasense{i.replace('/', '_')} -X stuff 'python3 OPC_Simple_v2.py'$(echo -ne '\015')", shell=True) + #subprocess.run(f"screen -S alphasense_{i.replace('/', '_')} -X stuff 'python3 OPC_Simple_v2.py > /path/to/output.log 2>&1'", shell=True) + + + + #run_code_command = f"screen -r alphasense{i.replace('/', '_')}" + #subprocess.run(f"screen -r alphasense{i.replace('/', '_')}", shell=True,text=True) + print("REATTACHED") + #subprocess.run("echo 'hello'", shell=True,text=True) + + #subprocess.run(f"screen -d", shell=True,text=True) + + + + + #run_code_command = f"screen -r alphasense{i.replace('/', '_')} -X stuff 'python3 OPC_Simple_v2.py'$(echo -ne '\\015')" + + #subprocess.run("echo 'Hello World'") + + #subprocess.run("chmod +x OPC_Simple_v2.py.py") + #subprocess.run(run_code_command, shell=True,text=True) + #subprocess.run("echo 'Hello World'") + + + + + + + + + + + + + #This gives the relative and full path for the subprocess.call() later on + #relative_path = 'Sensor_Code/Alphasense' + #full_path = os.path.join(home_directory, relative_path) + + #subprocess.call(['lxterminal' , '-e', 'python3', command], cwd=full_path, shell = False) + + + + + + + + + +#THIS IS THE YES OPTION +def retrieve_old_data(): + + shown_text.config(text="USING OLD CONFIGURATION") + + #Open the Json file + with open ('sensors_UART_configs.json','r') as file: + sensor_ports = json.load(file) + + print( sensor_ports) + + run_sensors(sensor_ports) + + + + + + #FIGURE OUT WHAT THE HECK I AM DOING + #turn_on.config(text= "NO LONGER YES") + +#THIS IS THE NO OPTION +def setup_tty(): + turn_off.config(text="QUIT",command=root.quit) + shown_text.config(text="UNPLUG ALL SENSORS, when done hit next") + + + print("HELLO") + +# use Button and Label widgets to create a simple TV remote +turn_on = Button(root, text="YES",command = retrieve_old_data) +#turn_on.pack(side=BOTTOM) + +turn_off = Button(root, text="NO", command=setup_tty) + +INITIALIZING_TEXT_PROMPT = "HELLO, welcome to the MOVEUAS program." +INITIALIZING_TEXT_PROMPT += " Would you like to use previous sensor USB port setups?" +INITIALIZING_TEXT_PROMPT += " (If this is your first time setting up hit NO)" + +shown_text = Label(root, text=INITIALIZING_TEXT_PROMPT, wraplength=300, justify=LEFT) +shown_text.grid(column=1,row=0) + + +turn_on.grid(column=0,row=1,sticky='se') +turn_off.grid(column=2,row=1,sticky='sw') +root.grid_columnconfigure(1,weight=1) +root.grid_rowconfigure(1,weight=1) + + + + +root.mainloop() + + + #turn_on.config(text= "NO LONGER YES") +# +# def ask_question(question): +# root = tk.Tk() +# +# root.withdraw() +# +# answer = simpledialog.askstring("Question", question) +# +# root.destroy() +# +# return answer +# +# if __name__ == "__main__": +# questions = [ +# "Question 1: What is your name?", +# "Question 2: Where are you from?", +# "Question 3: What is your favorite color?", +# "Question 4: What is your age?", +# "Question 5: What is your hobby?" +# ] +# +# answers = [] +# for question in questions: +# answer = ask_question(question) +# answers.append(answer) +# +# print("Answers:") +# for i, answer in enumerate(answers): +# print(f"Question {i + 1}: {answer}") diff --git a/sensors_UART_configs.json b/sensors_UART_configs.json new file mode 100644 index 0000000..cd71f18 --- /dev/null +++ b/sensors_UART_configs.json @@ -0,0 +1,5 @@ +{ + "alphasense": ["/dev/ttyUSB0"], + "sensirion": {}, + "PMS": {} +} From 8004b935a562d4e978d8088733a6317978410f7f Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 26 Jan 2024 16:50:04 -0500 Subject: [PATCH 44/99] Add files via upload --- Alphasense/start_Alphasense_loggers.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Alphasense/start_Alphasense_loggers.sh diff --git a/Alphasense/start_Alphasense_loggers.sh b/Alphasense/start_Alphasense_loggers.sh new file mode 100644 index 0000000..3a54778 --- /dev/null +++ b/Alphasense/start_Alphasense_loggers.sh @@ -0,0 +1,20 @@ +#!/bin/bash -x +# +# bash shell script to start multiple logger codes +# +# mdc +# created : 13 Dec 2023 +# modified: 13 Dec 2023 + +alpha1_device='/dev/ttyACM0' +alpha2_device='/dev/ttyACM1' + +echo "starting alpha1 logger" +screen -dm -S alpha1 python3 OPC_Simple_v2.py $alpha1_device + +echo "starting alpha2 logger" +screen -dm -S alpha2 python3 OPC_Simple_v2.py $alpha2_device + + + + From 9674b212dbbd28d046f05e2f4f30712ca26197dd Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 26 Jan 2024 16:50:43 -0500 Subject: [PATCH 45/99] Add files via upload --- PMS plantower/start_Plantower_loggers.sh | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 PMS plantower/start_Plantower_loggers.sh diff --git a/PMS plantower/start_Plantower_loggers.sh b/PMS plantower/start_Plantower_loggers.sh new file mode 100644 index 0000000..1d3fb3a --- /dev/null +++ b/PMS plantower/start_Plantower_loggers.sh @@ -0,0 +1,27 @@ +#!/bin/bash -x +# +# bash shell script to start multiple logger codes +# +# mdc +# created : 13 Dec 2023 +# modified: 13 Dec 2023 + +device1='/dev/ttyUSB2' +device2='/dev/ttyUSB3' +device3='/dev/ttyUSB4' +device4='/dev/ttyUSB5' + +echo "starting plantower1 logger" +screen -dm -S pt1 python3 pm25_simpletest.py $device1 pt1 + +echo "starting plantower2 logger" +screen -dm -S pt2 python3 pm25_simpletest.py $device2 pt2 + +echo "starting plantower3 logger" +screen -dm -S pt3 python3 pm25_simpletest.py $device3 pt3 + +echo "starting plantower4 logger" +screen -dm -S pt4 python3 pm25_simpletest.py $device4 pt4 + + + From 2bafc4a441c2881689b47b8b416e8d24df262e51 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 26 Jan 2024 16:51:18 -0500 Subject: [PATCH 46/99] Add files via upload --- Sensirion/start_Sensirion_loggers.sh | 21 +++++++++++++++++++++ Sensirion/stop_Sensirion_loggers.sh | 15 +++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 Sensirion/start_Sensirion_loggers.sh create mode 100644 Sensirion/stop_Sensirion_loggers.sh diff --git a/Sensirion/start_Sensirion_loggers.sh b/Sensirion/start_Sensirion_loggers.sh new file mode 100644 index 0000000..04b0b8d --- /dev/null +++ b/Sensirion/start_Sensirion_loggers.sh @@ -0,0 +1,21 @@ +#!/bin/bash -x +# +# bash shell script to start multiple logger codes +# +# mdc +# created : 13 Dec 2023 +# modified: 13 Dec 2023 + +device1='/dev/ttyUSB0' +device2='/dev/ttyUSB1' + +DATE=`date` +echo "starting sensirion1 logger at $DATE" +screen -dm -S sen1 python3 SPS30_Senirion_Run.py $device1 + +echo "starting sensirion2 logger" +screen -dm -S sen2 python3 SPS30_Senirion_Run.py $device2 + + + + diff --git a/Sensirion/stop_Sensirion_loggers.sh b/Sensirion/stop_Sensirion_loggers.sh new file mode 100644 index 0000000..18a3fe7 --- /dev/null +++ b/Sensirion/stop_Sensirion_loggers.sh @@ -0,0 +1,15 @@ +#!/bin/bash -x +# +# bash shell script to stop specific logger codes +# +# mdc +# created : 13 Dec 2023 +# modified: 13 Dec 2023 + +# stop sensorion loggers +pkill -f "python3 SPS30" + + + + + From 9be4497cc38bcf696a4f746a2e8390e08e46aa90 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 26 Jan 2024 16:58:31 -0500 Subject: [PATCH 47/99] Create README.md --- README.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..8398f15 --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +# Air Quality Sensor Repository + +This repository contains code for different air quality sensors. Each directory represents a specific sensor model, and the code within each directory is tailored to that sensor. + +## ALSPHASENSE + +**Authors:** Kaleb Nails, Erik Liebergall, Marc Compere +**Created:** 10/6/2023 +**Modified:** 13 Dec 2023 + +### Description + +This directory contains code for the ALSPHASENSE air quality sensor. The main script, `pm25_SPS30_Senirion_Run.py`, interfaces with the sensor, reads data, and logs it to a CSV file. + +### Instructions + +1. Connect the ALSPHASENSE sensor to your system. +2. Run the `pm25_SPS30_Senirion_Run.py` script with the appropriate device name as a command-line argument (default is `/dev/ttyUSB0`). + ```bash + python3 pm25_SPS30_Senirion_Run.py /dev/ttyUSB0 + +The script will log air quality data to a CSV file with a timestamp. + +# PMS plantower + +**Authors:** Erik Liebergall, Leah Smith, Kaleb Nails, Marc Compere +**Created:** 10 Feb 2023 +**Modified:** 11 Oct 2023 + +## Description + +This directory contains code for the PMS plantower air quality sensor. The `pm25_simpletest.py` script interfaces with the sensor, reads data, and outputs air quality information to the console. + +## Instructions + +1. Connect the PMS plantower sensor to your system. +2. Run the `pm25_simpletest.py` script with the appropriate device name as a command-line argument (default is `/dev/ttyUSB0`). + ```bash + python3 pm25_simpletest.py /dev/ttyUSB0 + + + From 04e9eae2504b8bfb0a18504e4294b78dde3c7e27 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 26 Jan 2024 17:12:49 -0500 Subject: [PATCH 48/99] Update README.md --- README.md | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 119 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8398f15..7932b6f 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ # Air Quality Sensor Repository -This repository contains code for different air quality sensors. Each directory represents a specific sensor model, and the code within each directory is tailored to that sensor. +This repository contains code for different air quality sensors. Each directory represents a specific sensor model, and the code within each directory is tailored to that sensor. Authors listed are immediate authors to our club, of course a lot of this code is found from other online, and the are cited within the programs files themselves. -## ALSPHASENSE +##Sensor Models + +## ALPHASENSE **Authors:** Kaleb Nails, Erik Liebergall, Marc Compere **Created:** 10/6/2023 -**Modified:** 13 Dec 2023 ### Description @@ -21,11 +22,15 @@ This directory contains code for the ALSPHASENSE air quality sensor. The main sc The script will log air quality data to a CSV file with a timestamp. + + + + + # PMS plantower **Authors:** Erik Liebergall, Leah Smith, Kaleb Nails, Marc Compere **Created:** 10 Feb 2023 -**Modified:** 11 Oct 2023 ## Description @@ -38,5 +43,114 @@ This directory contains code for the PMS plantower air quality sensor. The `pm25 ```bash python3 pm25_simpletest.py /dev/ttyUSB0 +## Output +The script continuously reads data from the sensor and prints the concentration units (standard and environmental) for PM1.0, PM2.5, and PM10.0. Additionally, it outputs particle counts for different particle sizes. + +Concentration Units (standard) +--------------------------------------- +PM 1.0: 10 PM2.5: 15 PM10: 20 +Concentration Units (environmental) +--------------------------------------- +PM 1.0: 8 PM2.5: 12 PM10: 16 +--------------------------------------- +Particles > 0.3um / 0.1L air: 1000 +Particles > 0.5um / 0.1L air: 800 +Particles > 1.0um / 0.1L air: 500 +Particles > 2.5um / 0.1L air: 200 +Particles > 5.0um / 0.1L air: 100 +Particles > 10 um / 0.1L air: 50 + +# Sensirion SPS30 + +**Authors:** Erik Liebergall, Marc Compere, Kaleb Nails +**Created:** 13 Oct 2023 + +## Description + +This directory contains code for the Sensirion SPS30 air quality sensor. The `pm25_SPS30_Senirion_Run.py` script interfaces with the sensor, reads data, and logs air quality information to a CSV file. + +## Instructions + +1. Connect the Sensirion SPS30 sensor to your system. +2. Run the `pm25_SPS30_Senirion_Run.py` script with the appropriate device name as a command-line argument (default is `/dev/ttyUSB0`). + ```bash + python3 pm25_SPS30_Senirion_Run.py /dev/ttyUSB0 + +The script will continuously read data from the sensor and log it to a CSV file. + +# CSV File Format + +Note these are subject to change are are up to date as off 1/26/2024 + + +## AlphaSense CSV File Format +The script outputs air quality information to a CSV file. The CSV format includes the following columns: +- **Serial Number** +- **Date Label** +- **Dates (YMD)** +- **Bin 0 to 23** +- **Bin1 MToF to Bin7 MToF** +- **Sampling Period** +- **SFR** +- **Temperature C** +- **Relative humidity** +- **PM1 ug/m³** +- **PM2.5 ug/m³** +- **PM10 ug/m³** +- **#RejectGlitch** +- **#RejectLongTOF** +- **#RejectRatio** +- **#RejectOutOfRange** +- **Fan rev count** +- **Laser status** +- **Checksum** +Each row in the CSV file represents a set of air quality measurements at a specific date and time, with corresponding values for each parameter. + + + +# PMS plantower CSV File Format + +The script outputs air quality information to a CSV file. The CSV format includes the following columns: + +- **Date Label** +- **Dates (YMD)** +- **Sensor 1** +- **pm1.0 standard ug/m³** +- **pm2.5 standard ug/m³** +- **pm10.0 standard ug/m³** +- **pm1.0 env ug/m³** +- **pm2.5 env ug/m³** +- **pm10.0 env ug/m³** +- **particles 0.3um** +- **particles 0.5um** +- **particles 1.0um** +- **particles 2.5um** +- **particles 5.0um** +- **particles 10.0um** + +Each row in the CSV file represents a set of air quality measurements at a specific date and time, with corresponding values for each parameter. + +## SENSIRION CSV File Format ## +The script outputs air quality information to a CSV file. The CSV format includes the following columns: +- **Serial Number** +- **Date Label** +- **Dates (YMD)** +- **Mass Concentration PM1.0 (µg/m³)** +- **Mass Concentration PM2.5 (µg/m³)** +- **Mass Concentration PM4.0 (µg/m³)** +- **Mass Concentration PM10.0 (µg/m³)** +- **Number Concentration PM0.5 (#/cm³)** +- **Number Concentration PM1.0 (#/cm³)** +- **Number Concentration PM2.5 (#/cm³)** +- **Number Concentration PM4.0 (#/cm³)** +- **Number Concentration PM10.0 (#/cm³)** +- **Typical Particle Size [µm]** +Each row in the CSV file represents a set of air quality measurements at a specific date and time, with corresponding values for each parameter. + + + + + + + - From 619deca271a21c57dfec3b91aa51e8c82017c67e Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 26 Jan 2024 17:13:09 -0500 Subject: [PATCH 49/99] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7932b6f..dcbd628 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This repository contains code for different air quality sensors. Each directory represents a specific sensor model, and the code within each directory is tailored to that sensor. Authors listed are immediate authors to our club, of course a lot of this code is found from other online, and the are cited within the programs files themselves. -##Sensor Models +## Sensor Models ## ALPHASENSE From 3da0149b55a160556da0f7f807bbd8a6cb40c437 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 26 Jan 2024 17:13:21 -0500 Subject: [PATCH 50/99] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dcbd628..53f4a14 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This repository contains code for different air quality sensors. Each directory represents a specific sensor model, and the code within each directory is tailored to that sensor. Authors listed are immediate authors to our club, of course a lot of this code is found from other online, and the are cited within the programs files themselves. -## Sensor Models +## Sensor Models ## ## ALPHASENSE From cb3ef30eb2da247978c6077a132a113d84c43635 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 26 Jan 2024 17:15:06 -0500 Subject: [PATCH 51/99] Update README.md --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 53f4a14..cfbc1c6 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This repository contains code for different air quality sensors. Each directory represents a specific sensor model, and the code within each directory is tailored to that sensor. Authors listed are immediate authors to our club, of course a lot of this code is found from other online, and the are cited within the programs files themselves. -## Sensor Models ## +# Sensor Models # ## ALPHASENSE @@ -27,23 +27,23 @@ The script will log air quality data to a CSV file with a timestamp. -# PMS plantower +## PMS plantower **Authors:** Erik Liebergall, Leah Smith, Kaleb Nails, Marc Compere **Created:** 10 Feb 2023 -## Description +### Description This directory contains code for the PMS plantower air quality sensor. The `pm25_simpletest.py` script interfaces with the sensor, reads data, and outputs air quality information to the console. -## Instructions +### Instructions 1. Connect the PMS plantower sensor to your system. 2. Run the `pm25_simpletest.py` script with the appropriate device name as a command-line argument (default is `/dev/ttyUSB0`). ```bash python3 pm25_simpletest.py /dev/ttyUSB0 -## Output +### Output The script continuously reads data from the sensor and prints the concentration units (standard and environmental) for PM1.0, PM2.5, and PM10.0. Additionally, it outputs particle counts for different particle sizes. Concentration Units (standard) @@ -60,16 +60,16 @@ Particles > 2.5um / 0.1L air: 200 Particles > 5.0um / 0.1L air: 100 Particles > 10 um / 0.1L air: 50 -# Sensirion SPS30 +## Sensirion SPS30 **Authors:** Erik Liebergall, Marc Compere, Kaleb Nails **Created:** 13 Oct 2023 -## Description +### Description This directory contains code for the Sensirion SPS30 air quality sensor. The `pm25_SPS30_Senirion_Run.py` script interfaces with the sensor, reads data, and logs air quality information to a CSV file. -## Instructions +### Instructions 1. Connect the Sensirion SPS30 sensor to your system. 2. Run the `pm25_SPS30_Senirion_Run.py` script with the appropriate device name as a command-line argument (default is `/dev/ttyUSB0`). @@ -108,7 +108,7 @@ Each row in the CSV file represents a set of air quality measurements at a speci -# PMS plantower CSV File Format +## PMS plantower CSV File Format The script outputs air quality information to a CSV file. The CSV format includes the following columns: From b975089b475c06aeb258947dc410811e7e482d1f Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 26 Jan 2024 17:15:55 -0500 Subject: [PATCH 52/99] Update README.md --- README.md | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/README.md b/README.md index cfbc1c6..2315410 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,16 @@ # Air Quality Sensor Repository - This repository contains code for different air quality sensors. Each directory represents a specific sensor model, and the code within each directory is tailored to that sensor. Authors listed are immediate authors to our club, of course a lot of this code is found from other online, and the are cited within the programs files themselves. # Sensor Models # ## ALPHASENSE - **Authors:** Kaleb Nails, Erik Liebergall, Marc Compere **Created:** 10/6/2023 ### Description - This directory contains code for the ALSPHASENSE air quality sensor. The main script, `pm25_SPS30_Senirion_Run.py`, interfaces with the sensor, reads data, and logs it to a CSV file. ### Instructions - 1. Connect the ALSPHASENSE sensor to your system. 2. Run the `pm25_SPS30_Senirion_Run.py` script with the appropriate device name as a command-line argument (default is `/dev/ttyUSB0`). ```bash @@ -28,16 +24,13 @@ The script will log air quality data to a CSV file with a timestamp. ## PMS plantower - **Authors:** Erik Liebergall, Leah Smith, Kaleb Nails, Marc Compere **Created:** 10 Feb 2023 ### Description - This directory contains code for the PMS plantower air quality sensor. The `pm25_simpletest.py` script interfaces with the sensor, reads data, and outputs air quality information to the console. ### Instructions - 1. Connect the PMS plantower sensor to your system. 2. Run the `pm25_simpletest.py` script with the appropriate device name as a command-line argument (default is `/dev/ttyUSB0`). ```bash @@ -61,28 +54,22 @@ Particles > 5.0um / 0.1L air: 100 Particles > 10 um / 0.1L air: 50 ## Sensirion SPS30 - **Authors:** Erik Liebergall, Marc Compere, Kaleb Nails **Created:** 13 Oct 2023 ### Description - This directory contains code for the Sensirion SPS30 air quality sensor. The `pm25_SPS30_Senirion_Run.py` script interfaces with the sensor, reads data, and logs air quality information to a CSV file. ### Instructions - 1. Connect the Sensirion SPS30 sensor to your system. 2. Run the `pm25_SPS30_Senirion_Run.py` script with the appropriate device name as a command-line argument (default is `/dev/ttyUSB0`). ```bash python3 pm25_SPS30_Senirion_Run.py /dev/ttyUSB0 - The script will continuously read data from the sensor and log it to a CSV file. # CSV File Format - Note these are subject to change are are up to date as off 1/26/2024 - ## AlphaSense CSV File Format The script outputs air quality information to a CSV file. The CSV format includes the following columns: - **Serial Number** @@ -106,10 +93,7 @@ The script outputs air quality information to a CSV file. The CSV format include - **Checksum** Each row in the CSV file represents a set of air quality measurements at a specific date and time, with corresponding values for each parameter. - - ## PMS plantower CSV File Format - The script outputs air quality information to a CSV file. The CSV format includes the following columns: - **Date Label** From 49c108c3d739d61adbf06340b7538c0b3fadff4c Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 26 Jan 2024 17:17:53 -0500 Subject: [PATCH 53/99] Update README.md --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2315410..b3b39d0 100644 --- a/README.md +++ b/README.md @@ -37,21 +37,24 @@ This directory contains code for the PMS plantower air quality sensor. The `pm25 python3 pm25_simpletest.py /dev/ttyUSB0 ### Output -The script continuously reads data from the sensor and prints the concentration units (standard and environmental) for PM1.0, PM2.5, and PM10.0. Additionally, it outputs particle counts for different particle sizes. +The script provides air quality measurements in both standard and environmental concentration units. Below is an example output: -Concentration Units (standard) +```plaintext +Concentration Units (Standard) --------------------------------------- -PM 1.0: 10 PM2.5: 15 PM10: 20 -Concentration Units (environmental) +PM 1.0: 10 PM 2.5: 15 PM 10: 20 + +Concentration Units (Environmental) --------------------------------------- -PM 1.0: 8 PM2.5: 12 PM10: 16 +PM 1.0: 8 PM 2.5: 12 PM 10: 16 --------------------------------------- Particles > 0.3um / 0.1L air: 1000 Particles > 0.5um / 0.1L air: 800 Particles > 1.0um / 0.1L air: 500 Particles > 2.5um / 0.1L air: 200 Particles > 5.0um / 0.1L air: 100 -Particles > 10 um / 0.1L air: 50 +Particles > 10um / 0.1L air: 50 + ## Sensirion SPS30 **Authors:** Erik Liebergall, Marc Compere, Kaleb Nails From d6e796b006fe08613943f7371c7895dae49351f6 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 26 Jan 2024 17:18:14 -0500 Subject: [PATCH 54/99] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b3b39d0..80e7793 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Particles > 1.0um / 0.1L air: 500 Particles > 2.5um / 0.1L air: 200 Particles > 5.0um / 0.1L air: 100 Particles > 10um / 0.1L air: 50 - +''' ## Sensirion SPS30 **Authors:** Erik Liebergall, Marc Compere, Kaleb Nails From 02025abb1ac42bf5cf09f128feae15ec6c12af8e Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 26 Jan 2024 17:33:15 -0500 Subject: [PATCH 55/99] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 80e7793..0f30943 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Particles > 1.0um / 0.1L air: 500 Particles > 2.5um / 0.1L air: 200 Particles > 5.0um / 0.1L air: 100 Particles > 10um / 0.1L air: 50 -''' +``` ## Sensirion SPS30 **Authors:** Erik Liebergall, Marc Compere, Kaleb Nails From 9d59dd09af8c13cd02c89b045da996cce0030df6 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 26 Jan 2024 17:33:40 -0500 Subject: [PATCH 56/99] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0f30943..23fe786 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,7 @@ The script outputs air quality information to a CSV file. The CSV format include - **Fan rev count** - **Laser status** - **Checksum** + Each row in the CSV file represents a set of air quality measurements at a specific date and time, with corresponding values for each parameter. ## PMS plantower CSV File Format From c0f6ef24108a1d837ce04b6bcc5cb9d80d1f46a4 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 26 Jan 2024 17:36:09 -0500 Subject: [PATCH 57/99] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 23fe786..f6be605 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,12 @@ This repository contains code for different air quality sensors. Each directory # Sensor Models # -## ALPHASENSE +## ALPHASENSE OPC **Authors:** Kaleb Nails, Erik Liebergall, Marc Compere **Created:** 10/6/2023 ### Description -This directory contains code for the ALSPHASENSE air quality sensor. The main script, `pm25_SPS30_Senirion_Run.py`, interfaces with the sensor, reads data, and logs it to a CSV file. +This directory contains code for the ALSPHASENSE air quality sensor. The main script, `OPC_Simple_v2.py`, interfaces with the sensor, reads data, and logs it to a CSV file. ### Instructions 1. Connect the ALSPHASENSE sensor to your system. From 84daea35d8dbf333271f8d1a6ca7e33081607521 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 26 Jan 2024 17:37:33 -0500 Subject: [PATCH 58/99] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f6be605..3f59889 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ This repository contains code for different air quality sensors. Each directory # Sensor Models # ## ALPHASENSE OPC +https://www.google.com/url?sa=i&url=https%3A%2F%2Fwww.alibaba.com%2Fproduct-detail%2FAlphasense-Optical-Particle-Sensor-PM1-PM2_60719814502.html&psig=AOvVaw3F4dcEPU_PQr-lvnn87pDb&ust=1706394926582000&source=images&cd=vfe&opi=89978449&ved=0CBMQjRxqFwoTCJil5uiO_IMDFQAAAAAdAAAAABAQ + **Authors:** Kaleb Nails, Erik Liebergall, Marc Compere **Created:** 10/6/2023 From 6247e6f03598308ad7742afb8d1709d84459df5c Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 26 Jan 2024 17:49:17 -0500 Subject: [PATCH 59/99] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3f59889..a083d0e 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This repository contains code for different air quality sensors. Each directory # Sensor Models # ## ALPHASENSE OPC -https://www.google.com/url?sa=i&url=https%3A%2F%2Fwww.alibaba.com%2Fproduct-detail%2FAlphasense-Optical-Particle-Sensor-PM1-PM2_60719814502.html&psig=AOvVaw3F4dcEPU_PQr-lvnn87pDb&ust=1706394926582000&source=images&cd=vfe&opi=89978449&ved=0CBMQjRxqFwoTCJil5uiO_IMDFQAAAAAdAAAAABAQ +![Alphasense Optical Particle Sensor](https://www.google.com/url?sa=i&url=https%3A%2F%2Fwww.alibaba.com%2Fproduct-detail%2FAlphasense-Optical-Particle-Sensor-PM1-PM2_60719814502.html&psig=AOvVaw3F4dcEPU_PQr-lvnn87pDb&ust=1706394926582000&source=images&cd=vfe&opi=89978449&ved=0CBMQjRxqFwoTCJil5uiO_IMDFQAAAAAdAAAAABAQ) **Authors:** Kaleb Nails, Erik Liebergall, Marc Compere **Created:** 10/6/2023 From d4442c07636ffb3041ba000b197df64d5e5eee05 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 26 Jan 2024 17:52:07 -0500 Subject: [PATCH 60/99] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a083d0e..8f22116 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This repository contains code for different air quality sensors. Each directory # Sensor Models # ## ALPHASENSE OPC -![Alphasense Optical Particle Sensor](https://www.google.com/url?sa=i&url=https%3A%2F%2Fwww.alibaba.com%2Fproduct-detail%2FAlphasense-Optical-Particle-Sensor-PM1-PM2_60719814502.html&psig=AOvVaw3F4dcEPU_PQr-lvnn87pDb&ust=1706394926582000&source=images&cd=vfe&opi=89978449&ved=0CBMQjRxqFwoTCJil5uiO_IMDFQAAAAAdAAAAABAQ) +![image](https://github.com/MOVEUAS/Sensor_Code/assets/117048000/2488073d-5645-47d8-9b6e-21c8f2c8ef74) **Authors:** Kaleb Nails, Erik Liebergall, Marc Compere **Created:** 10/6/2023 @@ -21,11 +21,9 @@ This directory contains code for the ALSPHASENSE air quality sensor. The main sc The script will log air quality data to a CSV file with a timestamp. - - - - ## PMS plantower +![image](https://github.com/MOVEUAS/Sensor_Code/assets/117048000/e1553597-c06a-49dd-b361-26345a3ea2d6) + **Authors:** Erik Liebergall, Leah Smith, Kaleb Nails, Marc Compere **Created:** 10 Feb 2023 @@ -62,6 +60,8 @@ Particles > 10um / 0.1L air: 50 **Authors:** Erik Liebergall, Marc Compere, Kaleb Nails **Created:** 13 Oct 2023 +![image](https://github.com/MOVEUAS/Sensor_Code/assets/117048000/af3127f4-a29f-4fac-9898-43c5310545e8) + ### Description This directory contains code for the Sensirion SPS30 air quality sensor. The `pm25_SPS30_Senirion_Run.py` script interfaces with the sensor, reads data, and logs air quality information to a CSV file. From 324b303a8a03c142090c4e09df3bf148ec15dc53 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 26 Jan 2024 17:53:46 -0500 Subject: [PATCH 61/99] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8f22116..5c636f8 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,11 @@ This repository contains code for different air quality sensors. Each directory **Created:** 10/6/2023 ### Description -This directory contains code for the ALSPHASENSE air quality sensor. The main script, `OPC_Simple_v2.py`, interfaces with the sensor, reads data, and logs it to a CSV file. +This directory contains code for the ALSPHASENSE air quality sensor. The main script, `OPC_Simple_v2.py`, interfaces with the sensor, reads data, and logs it to a CSV file. This is using the USB adaptor for the Alphasenses ### Instructions 1. Connect the ALSPHASENSE sensor to your system. -2. Run the `pm25_SPS30_Senirion_Run.py` script with the appropriate device name as a command-line argument (default is `/dev/ttyUSB0`). +2. Run the `pm25_SPS30_Senirion_Run.py` script with the appropriate device name as a command-line argument (default is `/dev/ttyACM0`). ```bash python3 pm25_SPS30_Senirion_Run.py /dev/ttyUSB0 From 02942a513302f4e0090557001ee1e13d2881fc61 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 26 Jan 2024 19:41:14 -0500 Subject: [PATCH 62/99] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 5c636f8..f23f9eb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # Air Quality Sensor Repository This repository contains code for different air quality sensors. Each directory represents a specific sensor model, and the code within each directory is tailored to that sensor. Authors listed are immediate authors to our club, of course a lot of this code is found from other online, and the are cited within the programs files themselves. +## GitHub Created and Managed By: Kaleb Nails + # Sensor Models # ## ALPHASENSE OPC From f5c14ab961bdf2ff1713c8da492bc30ef61b5eed Mon Sep 17 00:00:00 2001 From: KalebNails Date: Fri, 26 Jan 2024 19:43:08 -0500 Subject: [PATCH 63/99] changed up the autorunning stuff --- Alphasense_Start_Script.sh | 14 +++++++ Sensor_Start_Script.sh | 62 ++++++++++++++++++++++++++++ gui_popup.py | 82 ++++++++++++++++++++++++++++++-------- sensors_UART_configs.json | 6 +-- 4 files changed, 144 insertions(+), 20 deletions(-) create mode 100644 Alphasense_Start_Script.sh create mode 100755 Sensor_Start_Script.sh diff --git a/Alphasense_Start_Script.sh b/Alphasense_Start_Script.sh new file mode 100644 index 0000000..20ed22c --- /dev/null +++ b/Alphasense_Start_Script.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +alpha0_device="/dev/ttyACM0" +echo "Starting alpha0 logger" +screen -dm -S alpha0 python3 OPC_Simple_v2.py $alpha0_device + +alpha1_device="/dev/ttyACM1" +echo "Starting alpha1 logger" +screen -dm -S alpha1 python3 OPC_Simple_v2.py $alpha1_device + +alpha2_device="/dev/ttyACM2" +echo "Starting alpha2 logger" +screen -dm -S alpha2 python3 OPC_Simple_v2.py $alpha2_device + diff --git a/Sensor_Start_Script.sh b/Sensor_Start_Script.sh new file mode 100755 index 0000000..8b83402 --- /dev/null +++ b/Sensor_Start_Script.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +alpha0_device="/dev/ttyACM0" +echo "Starting alpha0 logger" +screen -dm -S alpha0 python3 OPC_Simple_v2.py $alpha0_device + +alpha1_device="/dev/ttyACM1" +echo "Starting alpha1 logger" +screen -dm -S alpha1 python3 OPC_Simple_v2.py $alpha1_device + +alpha2_device="/dev/ttyACM2" +echo "Starting alpha2 logger" +screen -dm -S alpha2 python3 OPC_Simple_v2.py $alpha2_device + +sensirion0_device="/dev/ttyUSB0" +echo "Starting sensirion0 logger" +screen -dm -S sensirion0 python3 SPS30_Sensirion_Run.py $sensirion0_device + +PMS0_device="/dev/ttyUSB3" +echo "Starting PMS0 logger" +screen -dm -S PMS0 python3 pm25_simpletest.py $PMS0_device + +PMS1_device="/dev/ttyUSB4" +echo "Starting PMS1 logger" +screen -dm -S PMS1 python3 pm25_simpletest.py $PMS1_device + +PMS2_device="/dev/ttyUSB5" +echo "Starting PMS2 logger" +screen -dm -S PMS2 python3 pm25_simpletest.py $PMS2_device + +sensirion1_device="/dev/ttyUSB1" +echo "Starting sensirion1 logger" +screen -dm -S sensirion1 python3 SPS30_Sensirion_Run.py $sensirion1_device + +PMS0_device="/dev/ttyUSB3" +echo "Starting PMS0 logger" +screen -dm -S PMS0 python3 pm25_simpletest.py $PMS0_device + +PMS1_device="/dev/ttyUSB4" +echo "Starting PMS1 logger" +screen -dm -S PMS1 python3 pm25_simpletest.py $PMS1_device + +PMS2_device="/dev/ttyUSB5" +echo "Starting PMS2 logger" +screen -dm -S PMS2 python3 pm25_simpletest.py $PMS2_device + +sensirion2_device="/dev/ttyUSB2" +echo "Starting sensirion2 logger" +screen -dm -S sensirion2 python3 SPS30_Sensirion_Run.py $sensirion2_device + +PMS0_device="/dev/ttyUSB3" +echo "Starting PMS0 logger" +screen -dm -S PMS0 python3 pm25_simpletest.py $PMS0_device + +PMS1_device="/dev/ttyUSB4" +echo "Starting PMS1 logger" +screen -dm -S PMS1 python3 pm25_simpletest.py $PMS1_device + +PMS2_device="/dev/ttyUSB5" +echo "Starting PMS2 logger" +screen -dm -S PMS2 python3 pm25_simpletest.py $PMS2_device + diff --git a/gui_popup.py b/gui_popup.py index 6512d19..a4bcc72 100755 --- a/gui_popup.py +++ b/gui_popup.py @@ -15,36 +15,85 @@ #This will actually run the sensors based on the json given def run_sensors(sensor_ports): - subprocess.run("pkill screen", shell=True,text=True) + #subprocess.run("pkill screen", shell=True,text=True) - #This is so i can get the home directory of any computer - #home_directory = os.path.expanduser('~') + bash_script_content = '#!/bin/bash\n\n' + # Loop through each sensor device + for i, device in enumerate(sensor_ports['alphasense'], start=0): + # Set the variable for each sensor + variable_name = f'alpha{i}_device' + device_port = sensor_ports['alphasense'][i] + bash_script_content += f'{variable_name}="{device_port}"\n' - for i in sensor_ports['alphasense']: - print(i) - try: - command = "sleep 15;./Alphasense/OPC_Simple_v2.py" +" " + i + ";exec sh" + # Start the logger for each sensor + bash_script_content += f'echo "Starting alpha{i} logger"\n' + bash_script_content += f'screen -dm -S alpha{i} python3 OPC_Simple_v2.py ${variable_name}\n\n' + #print(bash_script_content) + + + + + # Loop through each sensor device + for i, device in enumerate(sensor_ports['sensirion'], start=0): + # Set the variable for each sensor + variable_name = f'sensirion{i}_device' + device_port = sensor_ports['sensirion'][i] + bash_script_content += f'{variable_name}="{device_port}"\n' + + # Start the logger for each sensor + bash_script_content += f'echo "Starting sensirion{i} logger"\n' + bash_script_content += f'screen -dm -S sensirion{i} python3 SPS30_Sensirion_Run.py ${variable_name}\n\n' + #print(bash_script_content) + + + # Loop through each sensor device + for i, device in enumerate(sensor_ports['PMS'], start=0): + # Set the variable for each sensor + variable_name = f'PMS{i}_device' + device_port = sensor_ports['PMS'][i] + bash_script_content += f'{variable_name}="{device_port}"\n' + + # Start the logger for each sensor + bash_script_content += f'echo "Starting PMS{i} logger"\n' + bash_script_content += f'screen -dm -S PMS{i} python3 pm25_simpletest.py ${variable_name}\n\n' + + + + print(bash_script_content) + + # Write the Bash script to a file + with open('Sensor_Start_Script.sh', 'w') as bash_script_file: + bash_script_file.write(bash_script_content) + + # Make the Bash script executable + subprocess.run(['chmod', '+x', 'Sensor_Start_Script.sh']) + + +# for i in sensor_ports['alphasense']: +# print(i) +# try: +# command = "sleep 15;./Alphasense/OPC_Simple_v2.py" +" " + i + ";exec sh" #p3 = subprocess.run(["sudo", "screen", "-dm", "./OPC_Simple_v2.py"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) # you can also put stderr to stdout #p3 = subprocess.run(["screen", "-dm","bash","-c" , "sleep 15;./Alphasense/OPC_Simple_v2.py;exec sh"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) # you can also put stderr to stdout - p3 = subprocess.run(["screen", "-dm","bash","-c" , command], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) # you can also put stderr to stdout +# p3 = subprocess.run(["screen", "-dm","bash","-c" , command], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) # you can also put stderr to stdout - print('----') - print('p3 = {}'.format(p3)) - print('----') +# print('----') +# print('p3 = {}'.format(p3)) +# print('----') - except subprocess.CalledProcessError as err: - print( "\n\ncaught an rsync() subprocess error: {0}".format(err) ) - print( "exiting.") - sys.exit(-1) +# except subprocess.CalledProcessError as err: +# print( "\n\ncaught an rsync() subprocess error: {0}".format(err) ) +# print( "exiting.") +# sys.exit(-1) - print('p.stdout = {}'.format(p3.stdout.decode('utf-8'))) +# print('p.stdout = {}'.format(p3.stdout.decode('utf-8'))) #print(command) @@ -89,7 +138,6 @@ def run_sensors(sensor_ports): #run_code_command = f"screen -r alphasense{i.replace('/', '_')}" #subprocess.run(f"screen -r alphasense{i.replace('/', '_')}", shell=True,text=True) - print("REATTACHED") #subprocess.run("echo 'hello'", shell=True,text=True) #subprocess.run(f"screen -d", shell=True,text=True) diff --git a/sensors_UART_configs.json b/sensors_UART_configs.json index cd71f18..c4b6537 100644 --- a/sensors_UART_configs.json +++ b/sensors_UART_configs.json @@ -1,5 +1,5 @@ { - "alphasense": ["/dev/ttyUSB0"], - "sensirion": {}, - "PMS": {} + "alphasense": ["/dev/ttyACM0","/dev/ttyACM1","/dev/ttyACM2"], + "sensirion": ["/dev/ttyUSB0","/dev/ttyUSB1","/dev/ttyUSB2"], + "PMS": ["/dev/ttyUSB3","/dev/ttyUSB4","/dev/ttyUSB5"] } From 38eff64fbf053de157aba083313640c2a7d824fd Mon Sep 17 00:00:00 2001 From: KalebNails Date: Mon, 29 Jan 2024 17:40:39 -0500 Subject: [PATCH 64/99] added requirements --- requirements.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b918f96 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +Adafruit_Blinka==8.24.0 +adafruit_circuitpython_pm25==2.1.15 +board==1.0 +paho_mqtt==1.6.1 +py_opc_ng==0.0.5 +pyserial==3.5 +pyusbiss==0.2.2 From 0e0f22742521f166e1ce6040d52bc7bc27cc7d86 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Mon, 29 Jan 2024 17:42:55 -0500 Subject: [PATCH 65/99] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index f23f9eb..1b189a3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ # Air Quality Sensor Repository This repository contains code for different air quality sensors. Each directory represents a specific sensor model, and the code within each directory is tailored to that sensor. Authors listed are immediate authors to our club, of course a lot of this code is found from other online, and the are cited within the programs files themselves. +To pip install the dependent libraries run: + ```bash + pip install -r requirements.txt + ## GitHub Created and Managed By: Kaleb Nails # Sensor Models # From f306f4511a427c1db730b88c534d362995ff11a0 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 2 Feb 2024 11:56:11 -0500 Subject: [PATCH 66/99] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1b189a3..571084a 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ To pip install the dependent libraries run: ```bash pip install -r requirements.txt -## GitHub Created and Managed By: Kaleb Nails +## GitHub Created and Managed By: Kaleb Nails ## # Sensor Models # From 928db44a307da03ef5470b08786bab316c79999b Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Fri, 2 Feb 2024 11:56:23 -0500 Subject: [PATCH 67/99] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 571084a..3d0c4ec 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This repository contains code for different air quality sensors. Each directory To pip install the dependent libraries run: ```bash pip install -r requirements.txt - +``` ## GitHub Created and Managed By: Kaleb Nails ## # Sensor Models # From f2db208549b046ab71e9999bb04a1d085d56c3e4 Mon Sep 17 00:00:00 2001 From: KalebNails <102830532+KalebNails@users.noreply.github.com> Date: Fri, 2 Feb 2024 13:20:02 -0500 Subject: [PATCH 68/99] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3d0c4ec..6b298d4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +## GitHub Created and Managed By: Kaleb Nails ## + # Air Quality Sensor Repository This repository contains code for different air quality sensors. Each directory represents a specific sensor model, and the code within each directory is tailored to that sensor. Authors listed are immediate authors to our club, of course a lot of this code is found from other online, and the are cited within the programs files themselves. @@ -5,7 +7,6 @@ To pip install the dependent libraries run: ```bash pip install -r requirements.txt ``` -## GitHub Created and Managed By: Kaleb Nails ## # Sensor Models # From 806f90a1774f1350f3b0e6e8c56ec7d5f2e1d2a9 Mon Sep 17 00:00:00 2001 From: KalebNails <102830532+KalebNails@users.noreply.github.com> Date: Mon, 5 Feb 2024 16:58:17 -0500 Subject: [PATCH 69/99] Delete Alphasense/.gitkeep --- Alphasense/.gitkeep | 1 - 1 file changed, 1 deletion(-) delete mode 100644 Alphasense/.gitkeep diff --git a/Alphasense/.gitkeep b/Alphasense/.gitkeep deleted file mode 100644 index 8b13789..0000000 --- a/Alphasense/.gitkeep +++ /dev/null @@ -1 +0,0 @@ - From d6dec5a0cc437110fc7fb494d4c8ae0c3c0f0909 Mon Sep 17 00:00:00 2001 From: KalebNails <102830532+KalebNails@users.noreply.github.com> Date: Tue, 6 Feb 2024 13:12:36 -0500 Subject: [PATCH 70/99] Update OPC_Simple_v2.py --- Alphasense/OPC_Simple_v2.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Alphasense/OPC_Simple_v2.py b/Alphasense/OPC_Simple_v2.py index b7daa09..baa91db 100644 --- a/Alphasense/OPC_Simple_v2.py +++ b/Alphasense/OPC_Simple_v2.py @@ -2,6 +2,7 @@ #created : 10/6/2023 #modified: 13 Dec 2023 #from time import sleep +import sys from usbiss.spi import SPI import opcng as opc import os From daa349887013b9b61a6ee65cb4170e058ec825b4 Mon Sep 17 00:00:00 2001 From: KalebNails Date: Wed, 20 Mar 2024 01:16:32 -0400 Subject: [PATCH 71/99] added post processing --- Post_Processing/Post_Processing.py | 119 +++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 Post_Processing/Post_Processing.py diff --git a/Post_Processing/Post_Processing.py b/Post_Processing/Post_Processing.py new file mode 100644 index 0000000..f056a55 --- /dev/null +++ b/Post_Processing/Post_Processing.py @@ -0,0 +1,119 @@ +#Created by Kaleb Nails, Ahsan Ali + + +import pandas as pd +import os +from datetime import datetime + + + + +def Process(df): + + #print(list(df.keys())[0]) + column_length= len(list(df.values())[0].keys()) + print(f"each sensor has {column_length} columns") + + # Create column headers based on the number of sensors + columns = [ + f"{col}_{i}" + for i in range(len(df)) + for col in df[list(df.keys())[0]].keys() + ] + #print(columns) + new_column = [] + + #new_column += [element for name in columns for element in columns if name[:-2] in element] this might do it in one line but i dont care + #print(new_column) + + #reoganize you columns + for name in columns: + + #print(name[:-2]) + result = [element for element in columns if name[:-2] in element] + new_column += result #it was in a double matrix in a matrix, this stops it from being that way + + print(f"\nthe new organized column is \n {new_column}") + columns = new_column + + + # Create an empty DataFrame with the specified columns + result = pd.DataFrame(columns=columns) + + # Iterate over the sensor data\ + sensor_count = 0 + for df_name, df_data in df.items(): + # Iterate over the columns in the sensor data + #print(f"df_data {df_name} is:{df_data}") + + for col in df_data.keys(): + # Append columns from each sensor to the interleaved_columns list + result[f"{col}_{sensor_count}"] = df_data[col] + sensor_count =+ 1 + + + #print(result) + result.to_csv(f"{datetime.now().strftime('%Y_%m_%d')}_{list(df.keys())[0][:-2]}.csv", index=False) + print(f"Saved {datetime.now().strftime('%Y_%m_%d')}_{list(df.keys())[0][:-2]}.csv") + return result + + + +#define initial variables +Sensirion_df = {} +Sensirion_count = 0 + +plantower_df = {} +plantower_count = 0 + +Alphasense_df = {} +Alphasense_count = 0 + +#Go to the directory above it to look for the csv's +current_dir = os.path.abspath(os.curdir) +parent_dir = os.path.dirname(current_dir) + +for filename in os.listdir(parent_dir): + if os.path.isfile(os.path.join(parent_dir, filename)) and filename.endswith(".csv"): + + if "Sensirion" in filename: + print("Sensirion file being read:", filename) + df_name = f"Sensirion_{Sensirion_count}" + Sensirion_count += 1 + Sensirion_df[df_name] = pd.read_csv(os.path.join(parent_dir, filename)) + + #Below is hard coded column headers I am just deleting + Sensirion_df[df_name].drop(columns=['Date Label'], inplace=True) + + elif "plantower" in filename: + print("plantower file being read:", filename) + df_name = f"Plantower_{plantower_count}" + plantower_count += 1 + plantower_df[df_name] = pd.read_csv(os.path.join(parent_dir, filename)) + + #remove extra columns: + plantower_df[df_name].drop(columns=['Date Label', ' Sensor 1'], inplace=True) + + elif "Alphasense" in filename: + print("Alphasense file being read:", filename) + df_name = f"Alphasense_{Alphasense_count}" + Alphasense_count += 1 + Alphasense_df[df_name] = pd.read_csv(os.path.join(parent_dir, filename)) + + #print(Alphasense_df[df_name].keys()) + + #remove extra columns: + Alphasense_df[df_name].drop(columns=['Date Label'," Bin 0", " Bin 1", " Bin 2", " Bin 3", " Bin 4", " Bin 5", " Bin 6", " Bin 7"," Bin 8", " Bin 9", " Bin 10", " Bin 11", " Bin 12", " Bin 13", " Bin 14", " Bin 15"," Bin 16", " Bin 17", " Bin 18", " Bin 19", " Bin 20", " Bin 21", " Bin 22", " Bin 23"," Bin1 MToF", "Bin3 MToF", "Bin5 MToF", "Bin7 MToF", "Sampling Period", "SFR", "Checksum", "Fan rev count","Laser status"], inplace=True) + +#This runs post processing if the files exist, this a simple solution +if Sensirion_count > 0: + print("\n\033[1;31m" + "Processing Sensirion:" + "\033[0m") + Process(Sensirion_df) + +if plantower_count > 0: + print("\n\033[1;31m" + "Processing plantower:" + "\033[0m") + Process(plantower_df) + +if Alphasense_count > 0: + print("\n\033[1;31m" + "Processing Alphasense:" + "\033[0m") + Process(Alphasense_df) From a2103d42e82eae4b677fce19fe31dfa4d0c228f8 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 20 Mar 2024 01:21:57 -0400 Subject: [PATCH 72/99] Create README.md --- Post_Processing/README.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Post_Processing/README.md diff --git a/Post_Processing/README.md b/Post_Processing/README.md new file mode 100644 index 0000000..0d4412e --- /dev/null +++ b/Post_Processing/README.md @@ -0,0 +1,36 @@ +# Sensor Data Post-Processing + +## Overview + +This Python script is designed for post-processing sensor data files. It reads CSV files containing sensor data, organizes the data by sensor type, combines the data, and saves the combined data into new CSV files. The script is configured to work with Sensirion, Plantower, and Alphasense sensor data. +This is robust, but if sensor heading are changed from the sensors it will not work. +## Features + +- **Sensor Data Organization:** The script organizes sensor data by sensor type and combines it into a single DataFrame. +- **Column Interleaving:** Columns from each sensor are interleaved in the output DataFrame. +- **CSV Output:** The combined and cleaned sensor data is saved as a CSV file with a timestamp in the filename. +- **Variable Column Omision:** If you are missing a column or you have one you dont need, there is a .drop(columns=['...']) for each sensor. If you add something inside these brackets they will be removed from the final product. + +## Usage + +1. **Prepare Data Files:** Place the sensor data CSV files in the same directory as the script. +2. **Run the Script:** Execute the script, and it will process the data files and save the combined data in CSV format. + +## Dependencies + +- pandas +- os +- datetime + +## Usage Example + +```bash +python post_processing.py ``` + +## Dependencies + +- pandas +- os +- datetime + +--- From 2068569530a07d0ce83bbfd83185fdb6cfff3d37 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 20 Mar 2024 01:22:12 -0400 Subject: [PATCH 73/99] Update README.md --- Post_Processing/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Post_Processing/README.md b/Post_Processing/README.md index 0d4412e..d3dcea1 100644 --- a/Post_Processing/README.md +++ b/Post_Processing/README.md @@ -25,7 +25,7 @@ This is robust, but if sensor heading are changed from the sensors it will not w ## Usage Example ```bash -python post_processing.py ``` +python post_processing.py ## Dependencies From c4708db8ce05e0b091aeb8b46919de60817e880a Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 20 Mar 2024 01:23:06 -0400 Subject: [PATCH 74/99] Update README.md --- Post_Processing/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Post_Processing/README.md b/Post_Processing/README.md index d3dcea1..9895030 100644 --- a/Post_Processing/README.md +++ b/Post_Processing/README.md @@ -26,7 +26,7 @@ This is robust, but if sensor heading are changed from the sensors it will not w ```bash python post_processing.py - +``` ## Dependencies - pandas From a2435c2d44252e6a13efda57e0e31b9272a8f904 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 20 Mar 2024 01:23:57 -0400 Subject: [PATCH 75/99] Update README.md --- Post_Processing/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Post_Processing/README.md b/Post_Processing/README.md index 9895030..1cc8dbf 100644 --- a/Post_Processing/README.md +++ b/Post_Processing/README.md @@ -9,7 +9,10 @@ This is robust, but if sensor heading are changed from the sensors it will not w - **Sensor Data Organization:** The script organizes sensor data by sensor type and combines it into a single DataFrame. - **Column Interleaving:** Columns from each sensor are interleaved in the output DataFrame. - **CSV Output:** The combined and cleaned sensor data is saved as a CSV file with a timestamp in the filename. -- **Variable Column Omision:** If you are missing a column or you have one you dont need, there is a .drop(columns=['...']) for each sensor. If you add something inside these brackets they will be removed from the final product. +- **Variable Column Omision:** If you are missing a column or you have one you dont need, there is a + ```bash .drop(columns=['...']) + + for each sensor. If you add something inside these brackets they will be removed from the final product. ## Usage From 8c9dd4aa2a927c2e6258a6385cbcb9d1ce9590cc Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 20 Mar 2024 01:24:22 -0400 Subject: [PATCH 76/99] Update README.md --- Post_Processing/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Post_Processing/README.md b/Post_Processing/README.md index 1cc8dbf..3a9873d 100644 --- a/Post_Processing/README.md +++ b/Post_Processing/README.md @@ -10,7 +10,9 @@ This is robust, but if sensor heading are changed from the sensors it will not w - **Column Interleaving:** Columns from each sensor are interleaved in the output DataFrame. - **CSV Output:** The combined and cleaned sensor data is saved as a CSV file with a timestamp in the filename. - **Variable Column Omision:** If you are missing a column or you have one you dont need, there is a - ```bash .drop(columns=['...']) + ```bash + .drop(columns=['...']) + ``` for each sensor. If you add something inside these brackets they will be removed from the final product. From 6f59fc079428b800b8ed86ce12ce66fbb587a177 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 20 Mar 2024 01:24:33 -0400 Subject: [PATCH 77/99] Update README.md --- Post_Processing/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Post_Processing/README.md b/Post_Processing/README.md index 3a9873d..9d99f3b 100644 --- a/Post_Processing/README.md +++ b/Post_Processing/README.md @@ -14,7 +14,7 @@ This is robust, but if sensor heading are changed from the sensors it will not w .drop(columns=['...']) ``` - for each sensor. If you add something inside these brackets they will be removed from the final product. + for each sensor. If you add something inside these brackets they will be removed from the final product. ## Usage From a402db33f644ce8bf679cd4cac6ee391b95b1dae Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Wed, 20 Mar 2024 01:25:12 -0400 Subject: [PATCH 78/99] Update README.md --- Post_Processing/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Post_Processing/README.md b/Post_Processing/README.md index 9d99f3b..f0bedfd 100644 --- a/Post_Processing/README.md +++ b/Post_Processing/README.md @@ -30,6 +30,7 @@ This is robust, but if sensor heading are changed from the sensors it will not w ## Usage Example ```bash +cd ~/Sensor_Code/Post_Processing python post_processing.py ``` ## Dependencies From 5656506c477db903fb0bde52e54a9d0b76be06da Mon Sep 17 00:00:00 2001 From: KalebNails <102830532+KalebNails@users.noreply.github.com> Date: Sun, 24 Mar 2024 20:09:41 -0400 Subject: [PATCH 79/99] Update README.md --- Post_Processing/README.md | 74 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/Post_Processing/README.md b/Post_Processing/README.md index f0bedfd..19e9c9b 100644 --- a/Post_Processing/README.md +++ b/Post_Processing/README.md @@ -4,6 +4,80 @@ This Python script is designed for post-processing sensor data files. It reads CSV files containing sensor data, organizes the data by sensor type, combines the data, and saves the combined data into new CSV files. The script is configured to work with Sensirion, Plantower, and Alphasense sensor data. This is robust, but if sensor heading are changed from the sensors it will not work. + +Note the sensors automatically make their csv in the directories directly above /sensor_Code, to run post processing move them into Sensor_Code + + +## 📝 File Structure Before post processing after collecting data. + +```text +📦Sensor_code + ┣ 📂Alphasense // Source Website + ┃ ┣ 📄OPC_Simple_v2.py + ┃ ┣ 📄README.md + ┃ ┗ 📄start_Alphasense_loggers.sh + ┣ 📂PMS plantower + ┃ ┣ 📄.gitkeep + ┃ ┣ 📄README.md + ┃ ┣ 📄 pm25_simpletest.py + ┃ ┗ 📄start_Plantower_loggers.sh + ┣ 📂Post_Processing + ┃ ┣ 📄Post_Processing.py + ┃ ┗ 📄README.md + ┣ 📂Sensirion + ┃ ┣ 📄.gitkeep + ┃ ┣ 📄 README.md + ┃ ┣ 📄SPS30_Senirion_run.py + ┃ ┣ 📄exampleReadSPS30.py + ┃ ┣ 📄sps30.py + ┃ ┣ 📄start_Sensirion_loggers.sh + ┃ ┗ 📄stop_Sensirion_loggers.sh + ┣ 📂Viasala + ┃ ┗ 📄.gitkeep + ┣ 📄Alphasense_Start_Script.sh + ┣ 📄Sensor_Start_Script.sh + ┣ 📄gui_popup.py + ┣ 📄requirements.txt + ┣ 📄sensor_UART_configs.json + ┗ 📄README.md +``` +## File structure after post processing +```text +📦Sensor_code + ┣ 📂Alphasense // Source Website + ┃ ┣ 📄OPC_Simple_v2.py + ┃ ┣ 📄README.md + ┃ ┗ 📄start_Alphasense_loggers.sh + ┣ 📂PMS plantower + ┃ ┣ 📄.gitkeep + ┃ ┣ 📄README.md + ┃ ┣ 📄 pm25_simpletest.py + ┃ ┗ 📄start_Plantower_loggers.sh + ┣ 📂Post_Processing + ┃ ┣ 📄Post_Processing.py + ┃ ┗ 📄README.md + ┣ 📂Sensirion + ┃ ┣ 📄.gitkeep + ┃ ┣ 📄 README.md + ┃ ┣ 📄SPS30_Senirion_run.py + ┃ ┣ 📄exampleReadSPS30.py + ┃ ┣ 📄sps30.py + ┃ ┣ 📄start_Sensirion_loggers.sh + ┃ ┗ 📄stop_Sensirion_loggers.sh + ┣ 📂Viasala + ┃ ┗ 📄.gitkeep + ┣ 📄Alphasense_Start_Script.sh + ┣ 📄Sensor_Start_Script.sh + ┣ 📄gui_popup.py + ┣ 📄requirements.txt + ┣ 📄sensor_UART_configs.json + ┗ 📄README.md +``` + + + + + ## Features - **Sensor Data Organization:** The script organizes sensor data by sensor type and combines it into a single DataFrame. From 77d0573388f0ced779459cf8d73307e514b256c3 Mon Sep 17 00:00:00 2001 From: KalebNails <102830532+KalebNails@users.noreply.github.com> Date: Sun, 24 Mar 2024 20:10:14 -0400 Subject: [PATCH 80/99] Update README.md --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.md b/README.md index 6b298d4..820f45d 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,39 @@ To pip install the dependent libraries run: pip install -r requirements.txt ``` +## 📝 File Structure Before post processing after collecting data. + +```text +📦Sensor_code + ┣ 📂Alphasense // Source Website + ┃ ┣ 📄OPC_Simple_v2.py + ┃ ┣ 📄README.md + ┃ ┗ 📄start_Alphasense_loggers.sh + ┣ 📂PMS plantower + ┃ ┣ 📄.gitkeep + ┃ ┣ 📄README.md + ┃ ┣ 📄 pm25_simpletest.py + ┃ ┗ 📄start_Plantower_loggers.sh + ┣ 📂Post_Processing + ┃ ┣ 📄Post_Processing.py + ┃ ┗ 📄README.md + ┣ 📂Sensirion + ┃ ┣ 📄.gitkeep + ┃ ┣ 📄 README.md + ┃ ┣ 📄SPS30_Senirion_run.py + ┃ ┣ 📄exampleReadSPS30.py + ┃ ┣ 📄sps30.py + ┃ ┣ 📄start_Sensirion_loggers.sh + ┃ ┗ 📄stop_Sensirion_loggers.sh + ┣ 📂Viasala + ┃ ┗ 📄.gitkeep + ┣ 📄Alphasense_Start_Script.sh + ┣ 📄Sensor_Start_Script.sh + ┣ 📄gui_popup.py + ┣ 📄requirements.txt + ┣ 📄sensor_UART_configs.json + ┗ 📄README.md +``` # Sensor Models # ## ALPHASENSE OPC From cc0f63483e593f7b8e4766f3e1c3a6bdfeb05980 Mon Sep 17 00:00:00 2001 From: KalebNails <102830532+KalebNails@users.noreply.github.com> Date: Sun, 24 Mar 2024 20:10:35 -0400 Subject: [PATCH 81/99] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 820f45d..689947e 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ To pip install the dependent libraries run: ```text 📦Sensor_code - ┣ 📂Alphasense // Source Website + ┣ 📂Alphasense ┃ ┣ 📄OPC_Simple_v2.py ┃ ┣ 📄README.md ┃ ┗ 📄start_Alphasense_loggers.sh From b2d27f355490e42fe3711da0871b939e4bf7c0fb Mon Sep 17 00:00:00 2001 From: KalebNails <102830532+KalebNails@users.noreply.github.com> Date: Sun, 24 Mar 2024 20:25:37 -0400 Subject: [PATCH 82/99] Update README.md --- Post_Processing/README.md | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/Post_Processing/README.md b/Post_Processing/README.md index 19e9c9b..3048a34 100644 --- a/Post_Processing/README.md +++ b/Post_Processing/README.md @@ -8,11 +8,11 @@ This is robust, but if sensor heading are changed from the sensors it will not w Note the sensors automatically make their csv in the directories directly above /sensor_Code, to run post processing move them into Sensor_Code -## 📝 File Structure Before post processing after collecting data. +## 📝 File Structure Before post processing after collecting data, after moving the relevant CSV's into Sensor_Code. ```text -📦Sensor_code - ┣ 📂Alphasense // Source Website +📦Sensor_Code + ┣ 📂Alphasense ┃ ┣ 📄OPC_Simple_v2.py ┃ ┣ 📄README.md ┃ ┗ 📄start_Alphasense_loggers.sh @@ -38,13 +38,17 @@ Note the sensors automatically make their csv in the directories directly above ┣ 📄Sensor_Start_Script.sh ┣ 📄gui_popup.py ┣ 📄requirements.txt - ┣ 📄sensor_UART_configs.json + ┣ 📄YYYY_MM_DD_##_##_##_Sensirion_sps30_{serial code}.csv + ┣ 📄YYYY_MM_DD_##_##_##_pm25_simplest_plantower_pt#_CSV.csv + ┣ 📄YYYY_MM_DD_##_##_##_Alphasense_OPC-N3-{serial code}.csv ┗ 📄README.md ``` -## File structure after post processing + +## File structure after post processing, it should look like this, and new files should appear in Post_Processing. Once you are done delete the CSV's in Sensor_Code after you backed them up. + ```text -📦Sensor_code - ┣ 📂Alphasense // Source Website +📦Sensor_Code + ┣ 📂Alphasense ┃ ┣ 📄OPC_Simple_v2.py ┃ ┣ 📄README.md ┃ ┗ 📄start_Alphasense_loggers.sh @@ -55,6 +59,10 @@ Note the sensors automatically make their csv in the directories directly above ┃ ┗ 📄start_Plantower_loggers.sh ┣ 📂Post_Processing ┃ ┣ 📄Post_Processing.py + ┃ ┣ 📄YYYY_MM_DD_Sensirion.csv + ┃ ┣ 📄YYYY_MM_DD_plantower.csv + ┃ ┣ 📄YYYY_MM_DD_Alphasense.csv + ┃ ┣ 📄Post_Processing.py ┃ ┗ 📄README.md ┣ 📂Sensirion ┃ ┣ 📄.gitkeep @@ -71,6 +79,9 @@ Note the sensors automatically make their csv in the directories directly above ┣ 📄gui_popup.py ┣ 📄requirements.txt ┣ 📄sensor_UART_configs.json + ┣ 📄YYYY_MM_DD_##_##_##_Sensirion_sps30_{serial code}.csv + ┣ 📄YYYY_MM_DD_##_##_##_pm25_simplest_plantower_pt#_CSV.csv + ┣ 📄YYYY_MM_DD_##_##_##_Alphasense_OPC-N3-{serial code}.csv ┗ 📄README.md ``` From 0b23f286049d7942653c3f03e86c7cd62b12c18e Mon Sep 17 00:00:00 2001 From: KalebNails <102830532+KalebNails@users.noreply.github.com> Date: Sun, 24 Mar 2024 20:27:24 -0400 Subject: [PATCH 83/99] Update README.md --- Post_Processing/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Post_Processing/README.md b/Post_Processing/README.md index 3048a34..22ce282 100644 --- a/Post_Processing/README.md +++ b/Post_Processing/README.md @@ -8,7 +8,7 @@ This is robust, but if sensor heading are changed from the sensors it will not w Note the sensors automatically make their csv in the directories directly above /sensor_Code, to run post processing move them into Sensor_Code -## 📝 File Structure Before post processing after collecting data, after moving the relevant CSV's into Sensor_Code. +# 📝 File Structure Before post processing after collecting data, after moving the relevant CSV's into Sensor_Code. ```text 📦Sensor_Code @@ -44,7 +44,7 @@ Note the sensors automatically make their csv in the directories directly above ┗ 📄README.md ``` -## File structure after post processing, it should look like this, and new files should appear in Post_Processing. Once you are done delete the CSV's in Sensor_Code after you backed them up. +# File structure after post processing, it should look like this, and new files should appear in Post_Processing. Once you are done delete the CSV's in Sensor_Code after you backed them up. ```text 📦Sensor_Code From 4f29f4d656ae5bddfa607c88f475a39b47dfaa2b Mon Sep 17 00:00:00 2001 From: KalebNails <102830532+KalebNails@users.noreply.github.com> Date: Sun, 24 Mar 2024 20:27:40 -0400 Subject: [PATCH 84/99] Update README.md --- Post_Processing/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Post_Processing/README.md b/Post_Processing/README.md index 22ce282..0b3d5bc 100644 --- a/Post_Processing/README.md +++ b/Post_Processing/README.md @@ -8,7 +8,7 @@ This is robust, but if sensor heading are changed from the sensors it will not w Note the sensors automatically make their csv in the directories directly above /sensor_Code, to run post processing move them into Sensor_Code -# 📝 File Structure Before post processing after collecting data, after moving the relevant CSV's into Sensor_Code. +### 📝 File Structure Before post processing after collecting data, after moving the relevant CSV's into Sensor_Code. ```text 📦Sensor_Code @@ -44,7 +44,7 @@ Note the sensors automatically make their csv in the directories directly above ┗ 📄README.md ``` -# File structure after post processing, it should look like this, and new files should appear in Post_Processing. Once you are done delete the CSV's in Sensor_Code after you backed them up. +### File structure after post processing, it should look like this, and new files should appear in Post_Processing. Once you are done delete the CSV's in Sensor_Code after you backed them up. ```text 📦Sensor_Code From 8fc060506a0624b850ab1e3eb83351ddd739c09c Mon Sep 17 00:00:00 2001 From: KalebNails <102830532+KalebNails@users.noreply.github.com> Date: Sun, 24 Mar 2024 20:29:39 -0400 Subject: [PATCH 85/99] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 689947e..8fe5b0e 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ To pip install the dependent libraries run: ## 📝 File Structure Before post processing after collecting data. ```text -📦Sensor_code +📦Sensor_Code ┣ 📂Alphasense ┃ ┣ 📄OPC_Simple_v2.py ┃ ┣ 📄README.md From fc62033a3039b065ac83e2928d6e8ca59a7f7461 Mon Sep 17 00:00:00 2001 From: KalebNails <102830532+KalebNails@users.noreply.github.com> Date: Sun, 24 Mar 2024 22:02:34 -0400 Subject: [PATCH 86/99] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 8fe5b0e..5b72336 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,7 @@ To pip install the dependent libraries run: pip install -r requirements.txt ``` -## 📝 File Structure Before post processing after collecting data. - +## 📝 File Structure ```text 📦Sensor_Code ┣ 📂Alphasense From a2139b65d6ead1d25df510197c72842c89465be2 Mon Sep 17 00:00:00 2001 From: KalebNails <102830532+KalebNails@users.noreply.github.com> Date: Mon, 25 Mar 2024 11:45:31 -0400 Subject: [PATCH 87/99] Update pm25_simpletest.py --- PMS plantower/pm25_simpletest.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/PMS plantower/pm25_simpletest.py b/PMS plantower/pm25_simpletest.py index 8134011..2a6618f 100644 --- a/PMS plantower/pm25_simpletest.py +++ b/PMS plantower/pm25_simpletest.py @@ -61,6 +61,7 @@ dev1='/dev/ttyUSB0' +devLabel = 'unlabeled' # dev2='/dev/ttyUSB2' @@ -73,18 +74,19 @@ time.sleep(2.5) - if len(sys.argv)>1: print('using command line arg, and provided device!') dev1=sys.argv[1] +# Plantower does not report unique serial number, so must label reporting device on the command line +if len(sys.argv)>2: + devLabel=sys.argv[2] + print('using command line arg 2, device label: {}'.format(devLabel)) # make this match the handwritten label using the cmd line arg #2 + devName=os.path.basename(dev1) # get device name for logfile name print('using devName=[{0}]'.format(devName)) - - - uart = serial.Serial(dev1, baudrate=9600, timeout=0.25) # uart2 = serial.Serial(dev2, baudrate=9600, timeout=0.25) @@ -97,7 +99,9 @@ #i2c = busio.I2C(board.SCL, board.SDA, frequency=100000) # Connect to a PM2.5 sensor over I2C #pm25 = PM25_I2C(i2c, reset_pin) -fname = '{0}_pm25_simplest_plantower_CSV.csv'.format(datetime.now().strftime("%Y_%m_%d__%H_%M_%S") ) +#fname = '{0}_pm25_simplest_plantower_CSV.csv'.format(datetime.now().strftime("%Y_%m_%d__%H_%M_%S") ) +fname = '{0}_pm25_simplest_plantower_{1}_CSV.csv'.format(datetime.now().strftime("%Y_%m_%d__%H_%M_%S"),devLabel ) # <-- handwritten label (not usb device) + file = open(fname,'w') print("Found PM2.5 sensor, reading data...") From 0eceb968dfceefd5ae2f81b2bf989d8b8ead77e2 Mon Sep 17 00:00:00 2001 From: KalebNails <102830532+KalebNails@users.noreply.github.com> Date: Mon, 25 Mar 2024 12:08:41 -0400 Subject: [PATCH 88/99] Update pm25_simpletest.py Removed the extra column that it was making at the start of the data sets --- PMS plantower/pm25_simpletest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PMS plantower/pm25_simpletest.py b/PMS plantower/pm25_simpletest.py index 2a6618f..c7b4358 100644 --- a/PMS plantower/pm25_simpletest.py +++ b/PMS plantower/pm25_simpletest.py @@ -108,7 +108,7 @@ #adds heading columns #Add Labels to the top of the file TEST this works -titleStr = ',Date Label, Dates (YMD), Sensor 1, pm1.0 standard ug/m3, pm2.5 standard ug/m3, pm10.0 standard ug/m3, pm1.0 env ug/m3, pm2.5 env ug/m3, pm10.0 env ug/m3,particles 0.3um, particles 0.5um, particles 1.0um, particles 2.5um, particles 5.0um, particles 10.0um'#, Dates (YMD), Sensor 2, pm10 standard, pm25 standard, pm100 standard, pm10 env, pm25 env, pm100 env,particles 03um, particles 05um, particles 10um, particles 25um, particles 50um, particles 100um' +titleStr = 'Date Label, Dates (YMD), Sensor 1, pm1.0 standard ug/m3, pm2.5 standard ug/m3, pm10.0 standard ug/m3, pm1.0 env ug/m3, pm2.5 env ug/m3, pm10.0 env ug/m3,particles 0.3um, particles 0.5um, particles 1.0um, particles 2.5um, particles 5.0um, particles 10.0um'#, Dates (YMD), Sensor 2, pm10 standard, pm25 standard, pm100 standard, pm10 env, pm25 env, pm100 env,particles 03um, particles 05um, particles 10um, particles 25um, particles 50um, particles 100um' file.write(titleStr +"\n") file.flush() @@ -184,7 +184,7 @@ #dataStr1 = ', Data1:, {0}, {1}, {2}, {3}, {4}, {5}'.format(aqdata["particles 03um"],aqdata["particles 05um"],aqdata["particles 10um"],aqdata["particles 25um"],aqdata["particles 50um"],aqdata["particles 100um"]) #dataStr2 = ', Data2:, {0}, {1}, {2}, {3}, {4}, {5}'.format(aqdata2["particles 03um"],aqdata2["particles 05um"],aqdata2["particles 10um"],aqdata2["particles 25um"],aqdata2["particles 50um"],aqdata2["particles 100um"]) - dataStr1 = ', Data1:, {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}'.format( \ + dataStr1 = ' Data1:, {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}'.format( \ aqdata["pm10 standard"], \ aqdata["pm25 standard"], \ aqdata["pm100 standard"], \ From a16c42612013b358020fa9fbe06226098f227359 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:58:03 -0400 Subject: [PATCH 89/99] Create .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..afed073 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.csv From 7a091b9e7f12dde89afad4c09faea75f9a5c59ec Mon Sep 17 00:00:00 2001 From: KalebNails Date: Mon, 25 Mar 2024 14:00:30 -0400 Subject: [PATCH 90/99] updated Post_Processing and fixed issues. the issues included not incrementing through sensor count properly, as well as getting rid of duplicate headers that would appear. I just used unique instead of figuring out why they appeared, this does occasionally cause the order to mix up, but i think it is fine for now --- Post_Processing/Post_Processing.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Post_Processing/Post_Processing.py b/Post_Processing/Post_Processing.py index f056a55..4910541 100644 --- a/Post_Processing/Post_Processing.py +++ b/Post_Processing/Post_Processing.py @@ -4,6 +4,7 @@ import pandas as pd import os from datetime import datetime +import numpy as np @@ -33,23 +34,23 @@ def Process(df): result = [element for element in columns if name[:-2] in element] new_column += result #it was in a double matrix in a matrix, this stops it from being that way + new_column = np.unique(new_column) #I need the unique, is the easiest way to removing some duplicates I accidentally created print(f"\nthe new organized column is \n {new_column}") - columns = new_column - + columns = new_column # Create an empty DataFrame with the specified columns result = pd.DataFrame(columns=columns) - # Iterate over the sensor data\ + # Iterate over the sensor data sensor_count = 0 for df_name, df_data in df.items(): # Iterate over the columns in the sensor data #print(f"df_data {df_name} is:{df_data}") - + for col in df_data.keys(): # Append columns from each sensor to the interleaved_columns list result[f"{col}_{sensor_count}"] = df_data[col] - sensor_count =+ 1 + sensor_count = sensor_count + 1 #print(result) @@ -103,7 +104,7 @@ def Process(df): #print(Alphasense_df[df_name].keys()) #remove extra columns: - Alphasense_df[df_name].drop(columns=['Date Label'," Bin 0", " Bin 1", " Bin 2", " Bin 3", " Bin 4", " Bin 5", " Bin 6", " Bin 7"," Bin 8", " Bin 9", " Bin 10", " Bin 11", " Bin 12", " Bin 13", " Bin 14", " Bin 15"," Bin 16", " Bin 17", " Bin 18", " Bin 19", " Bin 20", " Bin 21", " Bin 22", " Bin 23"," Bin1 MToF", "Bin3 MToF", "Bin5 MToF", "Bin7 MToF", "Sampling Period", "SFR", "Checksum", "Fan rev count","Laser status"], inplace=True) + Alphasense_df[df_name].drop(columns=['Date Label'," Bin 0", " Bin 1", " Bin 2", " Bin 3", " Bin 4", " Bin 5", " Bin 6", " Bin 7"," Bin 8", " Bin 9", " Bin 10", " Bin 11", " Bin 12", " Bin 13", " Bin 14", " Bin 15"," Bin 16", " Bin 17", " Bin 18", " Bin 19", " Bin 20", " Bin 21", " Bin 22", " Bin 23"," Bin1 MToF", "Bin3 MToF", "Bin5 MToF", "Bin7 MToF", "Sampling Period", "SFR", "Checksum", "Fan rev count","Laser status",'#RejectGlitch','#RejectLongTOF','#RejectOutOfRange','#RejectRatio'], inplace=True) #This runs post processing if the files exist, this a simple solution if Sensirion_count > 0: From 90bb4f4fe2eddba4f51c8b65c7a06f2f5ef8f492 Mon Sep 17 00:00:00 2001 From: KalebNails <102830532+KalebNails@users.noreply.github.com> Date: Mon, 25 Mar 2024 17:54:15 -0400 Subject: [PATCH 91/99] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 5b72336..4f5f49c 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ To pip install the dependent libraries run: ```bash pip install -r requirements.txt ``` +Also I have made some YouTube tutorials. They explain some simple things and issues you might run into. I will continue to update as the need arises. +https://youtube.com/playlist?list=PLwAqsBL94ygYDSl61qIoNLhPo3zRYZZli&si=xE07LIelMbSo7oQt ## 📝 File Structure ```text From 646bcb5a773856448dc97f85329fb27c4a782d20 Mon Sep 17 00:00:00 2001 From: KalebNails <102830532+KalebNails@users.noreply.github.com> Date: Mon, 25 Mar 2024 17:55:09 -0400 Subject: [PATCH 92/99] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4f5f49c..8290bcd 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ https://youtube.com/playlist?list=PLwAqsBL94ygYDSl61qIoNLhPo3zRYZZli&si=xE07LIel ┣ 📄Sensor_Start_Script.sh ┣ 📄gui_popup.py ┣ 📄requirements.txt + ┣ 📄.gitnore ┣ 📄sensor_UART_configs.json ┗ 📄README.md ``` From 07bd2177315d0c62ab85949ce994cd8791ee071e Mon Sep 17 00:00:00 2001 From: KalebNails <102830532+KalebNails@users.noreply.github.com> Date: Fri, 29 Mar 2024 15:00:53 -0400 Subject: [PATCH 93/99] Create serial_Vaisala_AQT_420.py added viasala code finally --- Vialsala/serial_Vaisala_AQT_420.py | 203 +++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 Vialsala/serial_Vaisala_AQT_420.py diff --git a/Vialsala/serial_Vaisala_AQT_420.py b/Vialsala/serial_Vaisala_AQT_420.py new file mode 100644 index 0000000..af27792 --- /dev/null +++ b/Vialsala/serial_Vaisala_AQT_420.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python3 +# +# read Vaisala AQT-420 air quality and particulate matter sensor +# +# 'minicom -s' works with suggestions here: https://help.ubuntu.com/community/Minicom +# set HW flow control=OFF +# +# exit minicom: ctrl-A, then x +# +# what works: minicom with 115200/8N1, hw flow control=OFF +# then: date +# meas +# $ meas +# NO2 (ppm): 0.012 +# SO2 (ppm): 0.023 +# CO (ppm): 0.310 +# O3 (ppm): -0.002 +# PM2.5 (ug/m3): 0.7 +# PM10 (ug/m3): 1.9 +# TEMP (C): 21.5 +# HUM (%RH): 46.1 +# PRES (mbar): 1016.2 +# Uptime (s): 1802374 +# Validity: TRUE +# +# meas --csv +# 0.012,0.023,0.308,0.000,0.000,0.000,0.000,0.000,0.7,1.9,21.5,45.9,1016.3,1802444 +# +# +# +# ------------------------------------------------------------------------------ +# MODBUS example that has not worked because it's unclear how MODBUS on AQT-420 works over usb cable: +# from: https://medium.com/@peterfitch/modbus-and-rs485-a-python-test-rig-1b5014f709ec +# +# Marc Compere, comperem@erau.edu +# created : 04 Jan 2020 +# modified: 20 Apr 2023 +# modified by David Benning, Erik, Gabe, Leah, Kaleb: 20 Jan 2023 +#Thoughts and Prayers +#modified by Kaleb: 20 April 2023 + +# this got it started: +# ser.write('meas\r') +# while True: +# res=ser.read_until() +# print(res) + +import time # sleep() +from datetime import datetime # datetime.now() +import serial +import os +from pydrive.auth import GoogleAuth +from pydrive.drive import GoogleDrive +import socket + +gauth = GoogleAuth() +drive = GoogleDrive(gauth) +ser = serial.Serial('/dev/ttyUSB1', baudrate=115200, bytesize=8, parity='N', stopbits=1) + +ser.reset_input_buffer() # flush input buffer +ser.reset_output_buffer() # flush output buffer, more may be in usb adapter which is separate from linux buffer +ser.timeout=2 # (s) timeout for serial.read_until() + +class myDataClass: + NO2 = -1.0 # (ppm) + SO2 = -1.0 # (ppm) + CO = -1.0 # (ppm) + O3 = -1.0 # (ppm) + PM2pt5 = -1.0 # (ug/m3) + PM10 = -1.0 # (ug/m3) + TEMP = -1.0 # (C) + HUM = -1.0 # (%RH) + PRES = -1.0 # (mbar) + Uptime = -1.0 # (s) + Validity = -1.0 # (True/False) + +data = myDataClass() + +fname='{0}_vaisala_aqt420.csv'.format( datetime.now().strftime("%Y_%m_%d__%H_%M_%S") ) +folder = '1-ZUsOWRls8vGFxDhgCK5cudBqNecKI-d' +file1 = drive.CreateFile({'parents':[{'id':folder}],'title' : fname}) + +file=open(fname,'w') + +dt=10 # (sec) measurement request interval +cnt=0 + +print('Reading serial device: Vaisala AQT-420...') +print('logging to [{0}] every {1} seconds'.format(fname,dt)) +print('\nPress Crtl-C to quit...') + +while True: + #print('[{0}] --- requesting measurement ---'.format(datetime.now())) + nBytes = ser.write(b'meas\r') # write these bytes to the AQT-420, note: the \r does the trick (it's the return) + done=False # not done yet + while done==False: + res=ser.read_until() # returns byte arrays upto newline: + #print(res) + # b'NO2 (ppm): 0.011\r\n' + # b'SO2 (ppm): 0.020\r\n' + # b'CO (ppm): 0.248\r\n' + # b'O3 (ppm): 0.000\r\n' + # b'PM2.5 (ug/m3): 1.9\r\n' + # b'PM10 (ug/m3): 2.8\r\n' + # b'TEMP (C): 22.1\r\n' + # b'HUM (%RH): 43.6\r\n' + # b'PRES (mbar): 1015.6\r\n' + # b'Uptime (s): 1806282\r\n' + # b'Validity: TRUE\r\n' + # b'\r\n' + myStr=res.decode('utf-8') + if len(myStr)>5: + if myStr.find('NO2')>=0: # myStr='NO2 (ppm): 0.011\r\n' + (desc,val) = myStr.strip().split(':') + data.NO2 = float(val) + + if myStr.find('SO2')>=0: # myStr='SO2 (ppm): 0.020\r\n' + (desc,val) = myStr.strip().split(':') + data.SO2 = float(val) + + if myStr.find('CO')>=0: # myStr='CO (ppm): 0.248\r\n' + (desc,val) = myStr.strip().split(':') + data.CO = float(val) + + if myStr.find('O3')>=0: # myStr='O3 (ppm): 0.000\r\n' + (desc,val) = myStr.strip().split(':') + data.O3 = float(val) + + if myStr.find('PM2.5')>=0: # myStr='PM2.5 (ug/m3): 2.3\r\n' + (desc,val) = myStr.strip().split(':') + data.PM2pt5 = float(val) + + if myStr.find('PM10')>=0: # myStr='PM10 (ug/m3): 2.8\r\n' + (desc,val) = myStr.strip().split(':') + data.PM10 = float(val) + + if myStr.find('TEMP')>=0: # myStr='TEMP (C): 22.1\r\n' + (desc,val) = myStr.strip().split(':') + data.TEMP = float(val) + + if myStr.find('HUM')>=0: # myStr='HUM (%RH): 43.6\r\n' + (desc,val) = myStr.strip().split(':') + data.HUM = float(val) + + if myStr.find('PRES')>=0: # myStr='PRES (mbar): 1015.6\r\n' + (desc,val) = myStr.strip().split(':') + data.PRES = float(val) + + if myStr.find('Uptime')>=0: # myStr='Uptime (s): 1806282\r\n' + (desc,val) = myStr.strip().split(':') + data.Uptime = float(val) + + if myStr.find('Validity')>=0: # myStr='Validity: TRUE\r\n' + (desc,val) = myStr.strip().split(':') + data.Validity = bool(val) + done=True # exit this measurement string parsing loop + #print('done={0}'.format(done)) + + # print 1 complete line composed of all values just decoded + dateStr='{0}, cnt=,{1}, '.format(datetime.now(),cnt) + dataStr='NO2 (ppm),{0:>12.3f}, SO2 (ppm),{1:>12.3f}, CO (ppm),{2:>12.3f}, O3 (ppm),{3:>12.3f}, PM2.5 (ug/m3),{4:>12.3f}, PM10 (ug/m3),{5:>12.2f}, TEMP (C),{6:>8.2f}, HUM (%),{7:>8.1f}, PRES (mbar),{8:>10.2f}, Uptime (s),{9:>12.2f}, Validity,{10}' \ + .format(data.NO2,data.SO2,data.CO,data.O3,data.PM2pt5,data.PM10,data.TEMP,data.HUM,data.PRES,data.Uptime,data.Validity) + print(dateStr + dataStr) + + # just in case these formatting choices delete information, tack on the identical but raw floating point values as a long string at the end in the file only + file.write(dateStr + dataStr + ',raw values,' + ','.join([ str(elem) for elem in [data.NO2,data.SO2,data.CO,data.O3,data.PM2pt5,data.PM10,data.TEMP,data.HUM,data.PRES,data.Uptime,data.Validity] ]) + '\n' ) + file.flush() + + file1.SetContentFile(fname) + + try: + #Try to send data to bokeh table over sockets + ServerAddress = ('169.254.26.44',2222) + bufferSize = 1024 + #This is fine for now, change if there are multiple vaisalas + devName = 'Vaisala0' + UDPClient = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + Msg_to_bokeh_str ='"Sensors":"' + devName + '","NO2 (ppm)":"{0:>12.3f}", "SO2 (ppm)":"{1:>12.3f}", "CO (ppm)":"{2:>12.3f}", "O3 (ppm)":"{3:>12.3f}", "PM2.5 (ug/m3)":"{4:>12.3f}", "PM10 (ug/m3)":"{5:>12.2f}", "TEMP (C)":"{6:>8.2f}", "HUM (%)":"{7:>8.1f}", "PRES (mbar)":"{8:>10.2f}", "Uptime (s)":"{9:>12.2f}", "Validity":"{10}"'.format(data.NO2,data.SO2,data.CO,data.O3,data.PM2pt5,data.PM10,data.TEMP,data.HUM,data.PRES,data.Uptime,data.Validity) + Msg_to_bokeh_str = '{' + Msg_to_bokeh_str + '}' + Msg_to_bokeh_bytes = Msg_to_bokeh_str.encode('utf-8') + UDPClient.sendto(Msg_to_bokeh_bytes,ServerAddress) + + + + + except Exception as e: + print(e) + pass + + + try: + #upload to Google Drive + file1.Upload() + except: + print('No internet connection. Drive upload Failed') + pass + + + time.sleep(dt) + cnt=cnt+1 + + #file1.Upload() + From ad88f3a8fde483f585191c601e70d3443f72b43e Mon Sep 17 00:00:00 2001 From: KalebNails <102830532+KalebNails@users.noreply.github.com> Date: Fri, 29 Mar 2024 15:02:38 -0400 Subject: [PATCH 94/99] Create README.md --- Vialsala/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Vialsala/README.md diff --git a/Vialsala/README.md b/Vialsala/README.md new file mode 100644 index 0000000..4ea0c94 --- /dev/null +++ b/Vialsala/README.md @@ -0,0 +1,3 @@ +Note this has the google drive API in it which is not in the pip requirements, you can just get rid of the google drive upload stuff if you want. It should cause an issue. + +We only have one of these so I'm not going to change it on the github for now From 92c3e970dd8fd3dc92a9942cca58225d92619b8c Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Sat, 22 Jun 2024 18:16:33 -0400 Subject: [PATCH 95/99] Create CITATION.cff added documentation --- CITATION.cff | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 CITATION.cff diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..e270796 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,9 @@ +cff-version: 1.0.0 +message: "If you use this software, please cite it as below. Additional authors and contributors are cited in individual files or READMEs." +authors: + - family-names: Nails + given-names: Kaleb + orcid: https://orcid.org/0009-0007-8786-837X +title: "MOVEUAS PM sensor software" +version: patch-1 +date-released: 2023-07-19 From 369c47c28eaff6ee7a2e23b17caa7199f744a537 Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Sat, 22 Jun 2024 18:29:51 -0400 Subject: [PATCH 96/99] Create LICENSE added LICENSE --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1c739a8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 MOVEUAS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 9053521fa84c781245ed8d2e8326d5ed4e72ecfa Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Sun, 23 Jun 2024 23:44:44 -0400 Subject: [PATCH 97/99] Create plot_generator.py --- Post_Processing/plot_generator.py | 115 ++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 Post_Processing/plot_generator.py diff --git a/Post_Processing/plot_generator.py b/Post_Processing/plot_generator.py new file mode 100644 index 0000000..9f85ebf --- /dev/null +++ b/Post_Processing/plot_generator.py @@ -0,0 +1,115 @@ +# Author: Kaleb Nails +#This is a simple plotter to plot you data, note that is does down sample down to 20 minute intervals. + +import pandas as pd +import matplotlib.pyplot as plt + +# Read the CSV data into DataFrames +df1 = pd.read_csv('2024_03_21__14_25_57_Alphasense_OPC-N3-177380512.csv') +df1 = df1[[' Dates (YMD)', 'PM2.5 ug/m3']] +df1[' Dates (YMD)'] = pd.to_datetime(df1[' Dates (YMD)']) +df1['PM2.5 ug/m3'] = pd.to_numeric(df1['PM2.5 ug/m3'], errors='coerce') +df1.set_index(' Dates (YMD)', inplace=True) +df1_resampled = df1.resample('20min').mean().reset_index() + + +df3 = pd.read_csv('2024_03_21__14_25_57_Alphasense_OPC-N3-177380511.csv') +df3 = df3[[' Dates (YMD)', 'PM2.5 ug/m3']] +df3[' Dates (YMD)'] = pd.to_datetime(df3[' Dates (YMD)']) +df3['PM2.5 ug/m3'] = pd.to_numeric(df3['PM2.5 ug/m3'], errors='coerce') +df3.set_index(' Dates (YMD)', inplace=True) +df3_resampled = df3.resample('20min').mean().reset_index() + + +df2 = pd.read_csv('2024_03_21__10_34_46_vaisala_aqt420.csv', header=None, skipinitialspace=True) +df2 = df2[[0, 12]] # Date is in the first column, PM10 is in the 11th column +print(df2) +df2.columns = [' Dates (YMD)', 'PM2.5 ug/m3'] +df2[' Dates (YMD)'] = pd.to_datetime(df2[' Dates (YMD)']) + +########################################################################### +df4 = pd.read_csv('2024_03_21__14_48_19_pm25_simplest_plantower_pt1_CSV.csv') +print(df4.keys()) +df4 = df4[[' Dates (YMD)', ' pm2.5 standard ug/m3']] +print(df4) +df4[' Dates (YMD)'] = pd.to_datetime(df4[' Dates (YMD)']) +df4[' pm2.5 standard ug/m3'] = pd.to_numeric(df4[' pm2.5 standard ug/m3'], errors='coerce') +df4.set_index(' Dates (YMD)', inplace=True) +df4_resampled = df4.resample('20min').mean().reset_index() + +df5 = pd.read_csv('2024_03_21__14_48_19_pm25_simplest_plantower_pt2_CSV.csv') +df5 = df5[[' Dates (YMD)', ' pm2.5 standard ug/m3']] +df5[' Dates (YMD)'] = pd.to_datetime(df5[' Dates (YMD)']) +df5[' pm2.5 standard ug/m3'] = pd.to_numeric(df5[' pm2.5 standard ug/m3'], errors='coerce') +df5.set_index(' Dates (YMD)', inplace=True) +df5_resampled = df5.resample('20min').mean().reset_index() + +df6 = pd.read_csv('2024_03_21__14_48_19_pm25_simplest_plantower_pt3_CSV.csv') +df6 = df6[[' Dates (YMD)', ' pm2.5 standard ug/m3']] +df6[' Dates (YMD)'] = pd.to_datetime(df6[' Dates (YMD)']) +df6[' pm2.5 standard ug/m3'] = pd.to_numeric(df6[' pm2.5 standard ug/m3'], errors='coerce') +df6.set_index(' Dates (YMD)', inplace=True) +df6_resampled = df6.resample('20min').mean().reset_index() + +df7 = pd.read_csv('2024_03_21__14_48_19_pm25_simplest_plantower_pt4_CSV.csv') +df7 = df7[[' Dates (YMD)', ' pm2.5 standard ug/m3']] +df7[' Dates (YMD)'] = pd.to_datetime(df7[' Dates (YMD)']) +df7[' pm2.5 standard ug/m3'] = pd.to_numeric(df7[' pm2.5 standard ug/m3'], errors='coerce') +df7.set_index(' Dates (YMD)', inplace=True) +df7_resampled = df7.resample('20min').mean().reset_index() + +############################################### +df8 = pd.read_csv('2024_03_21__14_17_18_Sensirion_sps30_533D09A32681430C.csv') +df8 = df8[[' Dates (YMD)', 'Mass Concentration PM2.5 (µg/m³)']] +df8[' Dates (YMD)'] = pd.to_datetime(df8[' Dates (YMD)']) +df8['Mass Concentration PM2.5 (µg/m³)'] = pd.to_numeric(df8['Mass Concentration PM2.5 (µg/m³)'], errors='coerce') +df8.set_index(' Dates (YMD)', inplace=True) +df8_resampled = df8.resample('20min').mean().reset_index() + +df9 = pd.read_csv('2024_03_21__14_04_09_Sensirion_sps30_674C8CAB9A3123F6.csv') +df9 = df9[[' Dates (YMD)', 'Mass Concentration PM2.5 (µg/m³)']] +df9[' Dates (YMD)'] = pd.to_datetime(df9[' Dates (YMD)']) +df9['Mass Concentration PM2.5 (µg/m³)'] = pd.to_numeric(df9['Mass Concentration PM2.5 (µg/m³)'], errors='coerce') +df9.set_index(' Dates (YMD)', inplace=True) +df9_resampled = df9.resample('20min').mean().reset_index() + + + +# Plot the data +plt.figure(figsize=(10, 6)) + +plt.plot(df2[' Dates (YMD)'], df2['PM2.5 ug/m3'], + marker='x', linestyle='-.', label='Vaisala', markersize=.5, linewidth=4) + +plt.plot(df3_resampled[' Dates (YMD)'], df3_resampled['PM2.5 ug/m3'], + marker='o', linestyle='-', label='Alphasense 2', markersize=.5, linewidth=4) + +plt.plot(df1_resampled[' Dates (YMD)'], df1_resampled['PM2.5 ug/m3'], + marker='o', linestyle='-', label='Alphasense 1', markersize=.5, linewidth=4) + +plt.plot(df4_resampled[' Dates (YMD)'], df4_resampled[' pm2.5 standard ug/m3'], + marker='*', linestyle='--', label='Plantower 1', markersize=.5, linewidth=4) + +plt.plot(df5_resampled[' Dates (YMD)'], df5_resampled[' pm2.5 standard ug/m3'], + marker='*', linestyle='--', label='Plantower 2', markersize=.5, linewidth=4) + +plt.plot(df6_resampled[' Dates (YMD)'], df6_resampled[' pm2.5 standard ug/m3'], + marker='*', linestyle='--', label='Plantower 3', markersize=.5, linewidth=4) + +plt.plot(df7_resampled[' Dates (YMD)'], df7_resampled[' pm2.5 standard ug/m3'], + marker='*', linestyle='--', label='Plantower 4', markersize=.5, linewidth=4) + +plt.plot(df8_resampled[' Dates (YMD)'], df8_resampled['Mass Concentration PM2.5 (µg/m³)'], + marker='*', linestyle=':', label='Sensirion 1', markersize=.5, linewidth=4) + +plt.plot(df9_resampled[' Dates (YMD)'], df9_resampled['Mass Concentration PM2.5 (µg/m³)'], + marker='*', linestyle=':', label='Sensirion 2', markersize=.5, linewidth=4) + +plt.xlabel('Time (MM-DD HH)',fontsize=20) +plt.ylabel('PM2.5 ug/m3',fontsize=20) +plt.grid(True) +plt.xticks(rotation=45,fontsize=18) +plt.yticks(fontsize=18) +plt.legend(fontsize=18) +plt.tight_layout() +plt.show() From 1fae0c478435dbf07b085068d61428519a43a1fe Mon Sep 17 00:00:00 2001 From: MOVEUAS <117048000+MOVEUAS@users.noreply.github.com> Date: Sun, 23 Jun 2024 23:47:13 -0400 Subject: [PATCH 98/99] Update README.md --- Post_Processing/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Post_Processing/README.md b/Post_Processing/README.md index 0b3d5bc..0b2cb5a 100644 --- a/Post_Processing/README.md +++ b/Post_Processing/README.md @@ -125,3 +125,7 @@ python post_processing.py - datetime --- +# plot_generator.py +This is an independent program from what is mentioned above. This uses current file formating as of 2024 to create graphs for a specific amount of sensors listed in the hardcoded program. This should help quick data generation. It is important to note it downsamples down to 20 minutes, so don't use it on very short data sets. Below is a sample image of the type of graph it will generate given the correct inputs. +![image](https://github.com/MOVEUAS/Sensor_Code/assets/117048000/8cd80975-528e-47b7-bceb-4e5dbd0442fd) + From 099e0d19eb276065adc17e9bc6687cd54abe94bf Mon Sep 17 00:00:00 2001 From: KalebNails <102830532+KalebNails@users.noreply.github.com> Date: Sun, 11 Jan 2026 19:14:10 -0500 Subject: [PATCH 99/99] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8290bcd..2423a6f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,9 @@ This repository contains code for different air quality sensors. Each directory To pip install the dependent libraries run: ```bash pip install -r requirements.txt -``` +``` +Here is the link to the AIAA paper that explains this system: https://arc.aiaa.org/doi/10.2514/6.2024-4293 + Also I have made some YouTube tutorials. They explain some simple things and issues you might run into. I will continue to update as the need arises. https://youtube.com/playlist?list=PLwAqsBL94ygYDSl61qIoNLhPo3zRYZZli&si=xE07LIelMbSo7oQt