Integrating Multiple Grove Sensors with an IoT Service

Hi,

I am trying to integrate multiple Grove environmental sensors with AWS IoT Core service. The recommended best practice from AWS is to establish one connection per board to their IoT Core service. I am using Raspberry Pi with the Grove Base Hat. The sensors I am using are I2C UV (VEML6070), Temperature and Humidity Sensor Pro v1.3, and Air Quality Sensor v1.3.

Because these sensors are executed through separate scripts and commands on the Raspberry Pi, I can only get them to integrate with AWS IoT by establishing one connection per sensor. Are there any examples of creating a unified application consisting of multiple Grove sensors on Raspberry Pi? AWS is recommending that I integrate these sensors together in a single application, and establish a single MQTT connection with their IoT Core service.

Thanks,

Steve

It’s a great idea, we don’t have it right now, and we’ll look at it in the future.

So what if we combine the multiple scripts to one, also read and push the data from the same script?

Hi Salman,

That is what is required to integrate the best-practice way with AWS IoT. Here are the three sensors in question.

https://wiki.seeedstudio.com/Grove-Air_Quality_Sensor_v1.3/
https://wiki.seeedstudio.com/Grove-Temperature_and_Humidity_Sensor_Pro/
https://wiki.seeedstudio.com/Grove-I2C_UV_Sensor-VEML6070/

They are run with different scripts / commands on my Raspberry Pi 4. So right now, I launch three command prompts through VNC Viewer and start the three of them in parallel. Each sensor script had to be modified to establish an MQTT client connection to AWS IoT and construct a JSON payload containing the sensor data.

Ideally, a single script could be run to establish the MQTT connection and build a combined JSON payload (AWS shadow) for the three sensors.

Thanks,

Steve

Yes. but since it can be done with single scripts, why you are going with different scripts for each sensors? any particular reasons or problems?

I dont know how to make it work with a single script across three sensors. That is why I am posting this question to the forum as requested by Seeed technical support.

I guess since all the sensors are implemented in Python, it is technically possible. But I do not know how to do it personally.

Thanks,

Steve

Can you share the individual scripts here that you used?

Does not look like I can attach a file to the post, so here is the UV Sensor integrated with AWS IoT.

indent preformatted text by 4 spaces

#!/usr/bin/env python
#
# This is the code for Grove - UV Sensor
# (https://www.seeedstudio.com/Grove-UV-Sensor-p-1540.html)
#
# This is the library used with Grove Base Hat for RPi.

'''
## License
The MIT License (MIT)
Grove Base Hat for the Raspberry Pi, used to connect grove sensors.
Copyright (C) 2018  Seeed Technology Co.,Ltd. 
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 __future__ import print_function
import time, sys, signal, atexit, json
from upm import pyupm_veml6070 as veml6070
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTShadowClient

#thingName = 'UVSensor'
#clientId = 'UVSensor'
#endpoint = 'a3pujgfo1m12iq-ats.iot.us-west-2.amazonaws.com'
#rootCA = '/usr/local/lib/python3.7/dist-packages/grove/Certs/AmazonRootCA1.pem'
#cert = '/usr/local/lib/python3.7/dist-packages/grove/Certs/b4f241635b-certificate.pem.crt'
#key = '/usr/local/lib/python3.7/dist-packages/grove/Certs/b4f241635b-private.pem.key'
#port = 8883

thingName = 'RaspberryPi001'
clientId = 'RaspberryPi001'
endpoint = 'a3pujgfo1m12iq-ats.iot.us-west-2.amazonaws.com'
rootCA = '/usr/local/lib/python3.7/dist-packages/grove/Certs/AmazonRootCA1.pem'
cert = '/usr/local/lib/python3.7/dist-packages/grove/Certs/96c23c7d37-certificate.pem.crt'
key = '/usr/local/lib/python3.7/dist-packages/grove/Certs/96c23c7d37-private.pem.key'
port = 8883


def main():
    # Instantiate a Vishay UV Sensor on the I2C bus 0
    veml6070_sensor = veml6070.VEML6070(0);

   ## Exit handlers ##
   # This function stops python from printing a stacktrace when you hit control-C
   def SIGINTHandler(signum, frame):
        raise SystemExit

    # This function lets you run code on exit, including functions from abpdrrt005pg2a5
    def exitHandler():
        print("Exiting")
        sys.exit(0)

    # Register exit handlers
    atexit.register(exitHandler)
    signal.signal(signal.SIGINT, SIGINTHandler)

    # Read the value every second and detect the pressure
    while(1):
        UVLevel = veml6070_sensor.getUVIntensity()
        #print("UVLevel: " + str(UVLevel))
        print("UV* Value: {0}".format(veml6070_sensor.getUVIntensity()))
        # Create message payload
        payload = {"state":{"reported":{"UVIntensity":int(UVLevel)}}}
        # Update shadow
        deviceShadowHandler.shadowUpdate(json.dumps(payload), 
        customShadowCallback_Update, 5)
        time.sleep(10)

# Function called when a shadow is updated
def customShadowCallback_Update(payload, responseStatus, token):

    # Display status and data from update request
    if responseStatus == "timeout":
        print("Update request " + token + " time out!")

    if responseStatus == "accepted":
        payloadDict = json.loads(payload)
        print("~~~~~~~~~~~~~~~~~~~~~~~")
        print("Update request with token: " + token + " accepted!")
        print("UV Level: " + str(payloadDict["state"]["reported"]["UVIntensity"]))
        print("~~~~~~~~~~~~~~~~~~~~~~~\n\n")

    if responseStatus == "rejected":
        print("Update request " + token + " rejected!")

# Function called when a shadow is deleted
def customShadowCallback_Delete(payload, responseStatus, token):

       # Display status and data from delete request
     if responseStatus == "timeout":
        print("Delete request " + token + " time out!")

    if responseStatus == "accepted":
        print("~~~~~~~~~~~~~~~~~~~~~~~")
        print("Delete request with token: " + token + " accepted!")
        print("~~~~~~~~~~~~~~~~~~~~~~~\n\n")

    if responseStatus == "rejected":
        print("Delete request " + token + " rejected!")

# Configure logging
# AWSIoTMQTTShadowClient writes data to the log
   def configureLogging():

    logger = logging.getLogger("AWSIoTPythonSDK.core")
    logger.setLevel(logging.DEBUG)
    streamHandler = logging.StreamHandler()
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    streamHandler.setFormatter(formatter)
    logger.addHandler(streamHandler)

# Init AWSIoTMQTTShadowClient
myAWSIoTMQTTShadowClient = None
myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient(clientId)
myAWSIoTMQTTShadowClient.configureEndpoint(endpoint, port)
myAWSIoTMQTTShadowClient.configureCredentials(rootCA, key, cert)

# AWSIoTMQTTShadowClient connection configuration
myAWSIoTMQTTShadowClient.configureAutoReconnectBackoffTime(1, 32, 20)
myAWSIoTMQTTShadowClient.configureConnectDisconnectTimeout(10) # 10 sec
myAWSIoTMQTTShadowClient.configureMQTTOperationTimeout(5) # 5 sec

# Connect to AWS IoT
myAWSIoTMQTTShadowClient.connect()

# Create a device shadow handler, use this to update and delete shadow document
deviceShadowHandler = 
myAWSIoTMQTTShadowClient.createShadowHandlerWithName(thingName, True)

# Delete current shadow JSON doc
# deviceShadowHandler.shadowDelete(customShadowCallback_Delete, 5)

if __name__ == '__main__':
    main()

Here is the Temperature & Humidity Sensor Pro integrated with AWS IoT:

indent preformatted text by 4 spaces

import time
import seeed_dht
import json
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTShadowClient

# thingName = 'TemperatureHumidity'
# clientId = 'TemperatureHumidity'
# endpoint = 'a3pujgfo1m12iq-ats.iot.us-west-2.amazonaws.com'
# rootCA = '/home/pi/Seeed_Python_DHT/examples/certs/AmazonRootCA1.pem'
# cert = '/home/pi/Seeed_Python_DHT/examples/certs/fef6b4edde-certificate.pem.crt'
# key = '/home/pi/Seeed_Python_DHT/examples/certs/fef6b4edde-private.pem.key'
# port = 8883

thingName = 'RaspberryPi001'
clientId = 'RaspberryPi002'
endpoint = 'a3pujgfo1m12iq-ats.iot.us-west-2.amazonaws.com'
rootCA = '/home/pi/Seeed_Python_DHT/examples/certs/AmazonRootCA1.pem'
cert = '/home/pi/Seeed_Python_DHT/examples/certs/96c23c7d37-certificate.pem.crt'
key = '/home/pi/Seeed_Python_DHT/examples/certs/96c23c7d37-private.pem.key'
port = 8883



   def main():

    # for DHT11/DHT22
    sensor = seeed_dht.DHT("22", 12)
    # for DHT10
    # sensor = seeed_dht.DHT("10")

    while True:
        humi, temp = sensor.read()
       if not humi is None:
            print('DHT{0}, humidity {1:.1f}%, temperature {2:.1f}*'.format(sensor.dht_type, humi, temp))
       else:
            print('DHT{0}, humidity & temperature: {1}'.format(sensor.dht_type, temp))

        # Create message payload
        payload = {"state":{"reported":{"humidity":int(humi),"temperature":int(temp)}}}
        # Update shadow
        deviceShadowHandler.shadowUpdate(json.dumps(payload), customShadowCallback_Update, 5)
        time.sleep(10)

# Function called when a shadow is updated
def customShadowCallback_Update(payload, responseStatus, token):

    # Display status and data from update request
   if responseStatus == "timeout":
        print("Update request " + token + " time out!")

    if responseStatus == "accepted":
        payloadDict = json.loads(payload)
        print("~~~~~~~~~~~~~~~~~~~~~~~")
        print("Update request with token: " + token + " accepted!")
        print("moisture: " + str(payloadDict["state"]["reported"]["humidity"]))
        print("temperature: " + str(payloadDict["state"]["reported"]["temperature"]))
        print("~~~~~~~~~~~~~~~~~~~~~~~\n\n")

    if responseStatus == "rejected":
         print("Update request " + token + " rejected!")

# Function called when a shadow is deleted
def customShadowCallback_Delete(payload, responseStatus, token):

     # Display status and data from delete request
    if responseStatus == "timeout":
        print("Delete request " + token + " time out!")

    if responseStatus == "accepted":
        print("~~~~~~~~~~~~~~~~~~~~~~~")
        print("Delete request with token: " + token + " accepted!")
        print("~~~~~~~~~~~~~~~~~~~~~~~\n\n")

    if responseStatus == "rejected":
        print("Delete request " + token + " rejected!")

# Configure logging
# AWSIoTMQTTShadowClient writes data to the log
def configureLogging():

    logger = logging.getLogger("AWSIoTPythonSDK.core")
    logger.setLevel(logging.DEBUG)
    streamHandler = logging.StreamHandler()
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    streamHandler.setFormatter(formatter)
    logger.addHandler(streamHandler)

# Init AWSIoTMQTTShadowClient
myAWSIoTMQTTShadowClient = None
myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient(clientId)
myAWSIoTMQTTShadowClient.configureEndpoint(endpoint, port)
myAWSIoTMQTTShadowClient.configureCredentials(rootCA, key, cert)

# AWSIoTMQTTShadowClient connection configuration
myAWSIoTMQTTShadowClient.configureAutoReconnectBackoffTime(1, 32, 20)
myAWSIoTMQTTShadowClient.configureConnectDisconnectTimeout(10) # 10 sec
myAWSIoTMQTTShadowClient.configureMQTTOperationTimeout(5) # 5 sec

# Connect to AWS IoT
myAWSIoTMQTTShadowClient.connect()

# Create a device shadow handler, use this to update and delete shadow document
deviceShadowHandler = 
myAWSIoTMQTTShadowClient.createShadowHandlerWithName(thingName, True)

# Delete current shadow JSON doc
# deviceShadowHandler.shadowDelete(customShadowCallback_Delete, 5)



   if __name__ == '__main__':
        main()

I have not had an opportunity to integrate the Air Quality sensor yet, but the general principle is the same. The goal is to establish a single MQTT client connection to AWS, and to send a single JSON payload (shadow):

indent preformatted text by 4 spaces
{
  "state": {
    "reported": {
      "temperature": "25.1",
      "humidity": "47.1",
      "UVIntensity": "0"
    }
  },
  "metadata": {
    "reported": {
      "temperature": {
        "timestamp": 1590451883
      },
      "humidity": {
        "timestamp": 1590451883
      },
      "UVIntensity": {
        "timestamp": 1590451853
      }
    }
  },
  "version": 776,
  "timestamp": 1590451883,
  "clientToken": "ec28eadf-fdf5-4f26-aa60-a268253e569a"
}

@stevealbright Check this! I combined the payload with the Tempratur, Humidity Sensor value and UV sensor value!

payload = {“state”:{“reported”{“UVIntensity”:int(UVLevel),“humidity”:int(humi),“temperature”:int(temp)}}}

from __future__ import print_function

import time, sys, signal, atexit, json, seeed_dht, json

from upm import pyupm_veml6070 as veml6070

from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTShadowClient

# thingName = 'new thing name'

# clientId = 'new clinet id'

# endpoint = 'aws iot endpoint'

# rootCA = 'rootCA location'

# cert = 'cert location'

# key = 'key location'

# port = 8883

def main():

    # Instantiate a Vishay UV Sensor on the I2C bus 0

    veml6070_sensor = veml6070.VEML6070(0)

     # for DHT11/DHT22

    sensor = seeed_dht.DHT("22", 12)

    # for DHT10

    # sensor = seeed_dht.DHT("10")

   ## Exit handlers ##

   # This function stops python from printing a stacktrace when you hit control-C

   def SIGINTHandler(signum, frame):

        raise SystemExit

    # This function lets you run code on exit, including functions from abpdrrt005pg2a5

    def exitHandler():

        print("Exiting")

        sys.exit(0)

    # Register exit handlers

    atexit.register(exitHandler)

    signal.signal(signal.SIGINT, SIGINTHandler)

    # Read the value every second and detect the pressure

    while(1):

        humi, temp = sensor.read()

       if not humi is None:

            print('DHT{0}, humidity {1:.1f}%, temperature {2:.1f}*'.format(sensor.dht_type, humi, temp))

       else:

            print('DHT{0}, humidity & temperature: {1}'.format(sensor.dht_type, temp))

   

        deviceShadowHandler.shadowUpdate(json.dumps(payload), customShadowCallback_Update, 5)

        time.sleep(10)

        UVLevel = veml6070_sensor.getUVIntensity()

        #print("UVLevel: " + str(UVLevel))

        print("UV* Value: {0}".format(veml6070_sensor.getUVIntensity()))

        # Create message payload with dht and uv

        payload = {"state":{"reported":{"UVIntensity":int(UVLevel),"humidity":int(humi),"temperature":int(temp)}}}

        # Update shadow

        deviceShadowHandler.shadowUpdate(json.dumps(payload), 

        customShadowCallback_Update, 5)

        time.sleep(10)

# Function called when a shadow is updated

def customShadowCallback_Update(payload, responseStatus, token):

    # Display status and data from update request

    if responseStatus == "timeout":

        print("Update request " + token + " time out!")

    if responseStatus == "accepted":

        payloadDict = json.loads(payload)

        print("~~~~~~~~~~~~~~~~~~~~~~~")

        print("Update request with token: " + token + " accepted!")

        print("UV Level: " + str(payloadDict["state"]["reported"]["UVIntensity"]))

        print("~~~~~~~~~~~~~~~~~~~~~~~\n\n")

    if responseStatus == "rejected":

        print("Update request " + token + " rejected!")

# Function called when a shadow is deleted

def customShadowCallback_Delete(payload, responseStatus, token):

       # Display status and data from delete request

     if responseStatus == "timeout":

        print("Delete request " + token + " time out!")

    if responseStatus == "accepted":

        print("~~~~~~~~~~~~~~~~~~~~~~~")

        print("Delete request with token: " + token + " accepted!")

        print("~~~~~~~~~~~~~~~~~~~~~~~\n\n")

    if responseStatus == "rejected":

        print("Delete request " + token + " rejected!")

# Configure logging

# AWSIoTMQTTShadowClient writes data to the log

   def configureLogging():

    logger = logging.getLogger("AWSIoTPythonSDK.core")

    logger.setLevel(logging.DEBUG)

    streamHandler = logging.StreamHandler()

    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

    streamHandler.setFormatter(formatter)

    logger.addHandler(streamHandler)

# Init AWSIoTMQTTShadowClient

myAWSIoTMQTTShadowClient = None

myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient(clientId)

myAWSIoTMQTTShadowClient.configureEndpoint(endpoint, port)

myAWSIoTMQTTShadowClient.configureCredentials(rootCA, key, cert)

# AWSIoTMQTTShadowClient connection configuration

myAWSIoTMQTTShadowClient.configureAutoReconnectBackoffTime(1, 32, 20)

myAWSIoTMQTTShadowClient.configureConnectDisconnectTimeout(10) # 10 sec

myAWSIoTMQTTShadowClient.configureMQTTOperationTimeout(5) # 5 sec

# Connect to AWS IoT

myAWSIoTMQTTShadowClient.connect()

# Create a device shadow handler, use this to update and delete shadow document

deviceShadowHandler = 

myAWSIoTMQTTShadowClient.createShadowHandlerWithName(thingName, True)

# Delete current shadow JSON doc

# deviceShadowHandler.shadowDelete(customShadowCallback_Delete, 5)

if __name__ == '__main__':

    main()

So ‘sensor’ in this context refers to the temperature and humidity sensor. veml6070_sensor is the UV sensor?

And you import seeed_dht so that the dependencies are in place to run the temp and humidity sensor from the UV script?

Temperature and Humidity Sensor Pro is installed separately outside of grove.py, so I will have to test it on my Raspberry Pi to see if everything works as expected.

https://github.com/Seeed-Studio/grove.py

https://github.com/Seeed-Studio/Seeed_Python_DHT.git

Please check and let us know!

I ran the combined script on my board. With some minor modifications it seems to be working and delivered this JSON payload to AWS IoT. I guess I can combine other sensors such as the Air Quality Sensor by following similar logic?

{
“state”: {
“reported”: {
“UVIntensity”: 0,
“humidity”: 36,
“temperature”: 25
}
},
“metadata”: {
“reported”: {
“UVIntensity”: {
“timestamp”: 1592347728
},
“humidity”: {
“timestamp”: 1592347728
},
“temperature”: {
“timestamp”: 1592347728
}
}
},
“version”: 2702,
“timestamp”: 1592347728,
“clientToken”: “42db1a0d-0881-40d1-abd9-3554b0a11a78”
}

Actually, as I mentioned above, the code is working for the combined UV / Temp & Humidity sensors. I could use your guidance on the integration of the Air Quality sensor as the code structure is a bit different with a class definition included in the script.

indent preformatted text by 4 spaces

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# The MIT License (MIT)
#
# Grove Base Hat for the Raspberry Pi, used to connect grove sensors.
# Copyright (C) 2018  Seeed Technology Co.,Ltd.
'''
This is the code for
     - `Grove -  Air Quality Sensor <https://www.seeedstudio.com/Grove-Air-quality-sensor-v1.3-p-2439.html)>`_

Examples:

    .. code-block:: python

        import time
        from grove.grove_air_quality_sensor_v1_3 import GroveAirQualitySensor

        # connect to alalog pin 2(slot A2)
       PIN = 2

        sensor = GroveAirQualitySensor(pin)

        print('Detecting ...') 
        while True:
            value = sensor.value        
            if value > 100:
                print("{}, High Pollution.".format(value))
            else:
                print("{}, Air Quality OK.".format(value))
            time.sleep(.1)
'''
import time, sys
from grove.adc import ADC

__all__ = ["GroveAirQualitySensor"]

class GroveAirQualitySensor(object):
    '''
    Grove Air Quality Sensor class

    Args:
        pin(int): number of analog pin/channel the sensor connected.
    '''
    def __init__(self, channel):
        self.channel = channel
        self.adc = ADC()

    @property
    def value(self):
        '''
        Get the air quality strength value, badest value is 100.0%.

        Returns:
            (int): ratio, 0(0.0%) - 1000(100.0%)
        '''
        return self.adc.read(self.channel)

Grove = GroveAirQualitySensor


def main():
    from grove.helper import SlotHelper
    sh = SlotHelper(SlotHelper.ADC)
    pin = sh.argv2pin()

    sensor = GroveAirQualitySensor(pin)

    print('Detecting ...') 
    while True:
        value = sensor.value        
        if value > 100:
            print("{}, High Pollution.".format(value))
        else:
            print("{}, Air Quality OK.".format(value))
        time.sleep(.1)

if __name__ == '__main__':
    main()

@stevealbright Glad to know it’s worked, Can you share the final code that needs to add the Air Quality Sensor?

Here is the code with the combined UV / Temp & Humidity sensors. Needs to include Air Quality Sensor logic which I already posted.

#!/usr/bin/env python
#
# This is the code for Grove - UV Sensor
# (https://www.seeedstudio.com/Grove-UV-Sensor-p-1540.html)
#
# This is the library used with Grove Base Hat for RPi.

from __future__ import print_function
import time, sys, signal, atexit, json, seeed_dht
from upm import pyupm_veml6070 as veml6070
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTShadowClient

#thingName = 'UVSensor'
#clientId = 'UVSensor'
#endpoint = 'a3pujgfo1m12iq-ats.iot.us-west-2.amazonaws.com'
#rootCA = '/usr/local/lib/python3.7/dist-packages/grove/Certs/AmazonRootCA1.pem'
#cert = '/usr/local/lib/python3.7/dist-packages/grove/Certs/b4f241635b-certificate.pem.crt'
#key = '/usr/local/lib/python3.7/dist-packages/grove/Certs/b4f241635b-private.pem.key'
#port = 8883

thingName = 'RaspberryPi001'
clientId = 'RaspberryPi001'
endpoint = 'a3pujgfo1m12iq-ats.iot.us-west-2.amazonaws.com'
 rootCA = '/usr/local/lib/python3.7/dist-packages/grove/Certs/AmazonRootCA1.pem'
 cert = '/usr/local/lib/python3.7/dist-packages/grove/Certs/96c23c7d37-certificate.pem.crt'
 key = '/usr/local/lib/python3.7/dist-packages/grove/Certs/96c23c7d37-private.pem.key'
 port = 8883


def main():
    

# Instantiate a Vishay UV Sensor on the I2C bus 0

veml6070_sensor = veml6070.VEML6070(0)

 # for DHT11/DHT22

sensor = seeed_dht.DHT("22", 12)

# for DHT10

# sensor = seeed_dht.DHT("10")

## Exit handlers ##

# This function stops python from printing a stacktrace when you hit control-C

def SIGINTHandler(signum, frame):

    raise SystemExit

# This function lets you run code on exit, including functions from abpdrrt005pg2a5

def exitHandler():

    print("Exiting")

    sys.exit(0)

# Register exit handlers

atexit.register(exitHandler)

signal.signal(signal.SIGINT, SIGINTHandler)

# Read the value every second and detect the pressure
while(1):
    humi, temp = sensor.read()
    if not humi is None:
        print('DHT{0}, humidity {1:.1f}%, temperature {2:.1f}*'.format(sensor.dht_type, humi, temp))
    else:
        print('DHT{0}, humidity & temperature: {1}'.format(sensor.dht_type, temp))
    #deviceShadowHandler.shadowUpdate(json.dumps(payload), customShadowCallback_Update, 5)
    time.sleep(1)

    UVLevel = veml6070_sensor.getUVIntensity()
    #print("UVLevel: " + str(UVLevel))
    print("UV* Value: {0}".format(veml6070_sensor.getUVIntensity()))
    # Create message payload with dht and uv
    payload = {"state":{"reported":{"UVIntensity":int(UVLevel),"humidity":int(humi),"temperature":int(temp)}}}
    # Update shadow
    deviceShadowHandler.shadowUpdate(json.dumps(payload),customShadowCallback_Update, 5)
    time.sleep(10)

# Function called when a shadow is updated

def customShadowCallback_Update(payload, responseStatus, token):

# Display status and data from update request

if responseStatus == "timeout":

    print("Update request " + token + " time out!")

if responseStatus == "accepted":

    payloadDict = json.loads(payload)

    print("~~~~~~~~~~~~~~~~~~~~~~~")

    print("Update request with token: " + token + " accepted!")

    print("UV Level: " + str(payloadDict["state"]["reported"]["UVIntensity"]))

    print("~~~~~~~~~~~~~~~~~~~~~~~\n\n")

if responseStatus == "rejected":

    print("Update request " + token + " rejected!")

# Function called when a shadow is deleted

def customShadowCallback_Delete(payload, responseStatus, token):

   # Display status and data from delete request

 if responseStatus == "timeout":

    print("Delete request " + token + " time out!")

 if responseStatus == "accepted":

    print("~~~~~~~~~~~~~~~~~~~~~~~")

    print("Delete request with token: " + token + " accepted!")

    print("~~~~~~~~~~~~~~~~~~~~~~~\n\n")

 if responseStatus == "rejected":

    print("Delete request " + token + " rejected!")

# Configure logging

# AWSIoTMQTTShadowClient writes data to the log

def configureLogging():
      logger = logging.getLogger("AWSIoTPythonSDK.core")
      logger.setLevel(logging.DEBUG)
      streamHandler = logging.StreamHandler()
      formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
     streamHandler.setFormatter(formatter)
     logger.addHandler(streamHandler)

# Init AWSIoTMQTTShadowClient

myAWSIoTMQTTShadowClient = None

myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient(clientId)

myAWSIoTMQTTShadowClient.configureEndpoint(endpoint, port)

myAWSIoTMQTTShadowClient.configureCredentials(rootCA, key, cert)

# AWSIoTMQTTShadowClient connection configuration

myAWSIoTMQTTShadowClient.configureAutoReconnectBackoffTime(1, 32, 20)

myAWSIoTMQTTShadowClient.configureConnectDisconnectTimeout(10) # 10 sec

myAWSIoTMQTTShadowClient.configureMQTTOperationTimeout(5) # 5 sec

# Connect to AWS IoT

myAWSIoTMQTTShadowClient.connect()

# Create a device shadow handler, use this to update and delete shadow document

deviceShadowHandler = myAWSIoTMQTTShadowClient.createShadowHandlerWithName(thingName, True)

# Delete current shadow JSON doc

# deviceShadowHandler.shadowDelete(customShadowCallback_Delete, 5)

if __name__ == '__main__':

main()

This should be work

from __future__ import print_function

import time, sys, signal, atexit, json, seeed_dht, json

from upm import pyupm_veml6070 as veml6070

from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTShadowClient

from grove.grove_air_quality_sensor_v1_3 import GroveAirQualitySensor

# thingName = 'new thing name'

# clientId = 'new clinet id'

# endpoint = 'aws iot endpoint'

# rootCA = 'rootCA location'

# cert = 'cert location'

# key = 'key location'

# port = 8883

def main():

    # Instantiate a Vishay UV Sensor on the I2C bus 0

    veml6070_sensor = veml6070.VEML6070(0)

     # for DHT11/DHT22

    sensor = seeed_dht.DHT("22", 12)

    # for DHT10

    # sensor = seeed_dht.DHT("10")

    sensor = GroveAirQualitySensor(2) //connected to pin2

   ## Exit handlers ##

   # This function stops python from printing a stacktrace when you hit control-C

   def SIGINTHandler(signum, frame):

        raise SystemExit

    # This function lets you run code on exit, including functions from abpdrrt005pg2a5

    def exitHandler():

        print("Exiting")

        sys.exit(0)

    # Register exit handlers

    atexit.register(exitHandler)

    signal.signal(signal.SIGINT, SIGINTHandler)

    # Read the value every second and detect the pressure

    while(1):

        humi, temp = sensor.read()

       if not humi is None:

            print('DHT{0}, humidity {1:.1f}%, temperature {2:.1f}*'.format(sensor.dht_type, humi, temp))

       else:

            print('DHT{0}, humidity & temperature: {1}'.format(sensor.dht_type, temp))

   

        deviceShadowHandler.shadowUpdate(json.dumps(payload), customShadowCallback_Update, 5)

        time.sleep(10)

        UVLevel = veml6070_sensor.getUVIntensity()

        #print("UVLevel: " + str(UVLevel))

        print("UV* Value: {0}".format(veml6070_sensor.getUVIntensity()))

        AirQ = sensor.value   // reading Air Qulaity sensor vallue

        # Create message payload with dht and uv

        payload = {"state":{"reported":{"UVIntensity":int(UVLevel),"humidity":int(humi),"temperature":int(temp),"airQuality":int(AirQ)}}}

        # Update shadow

        deviceShadowHandler.shadowUpdate(json.dumps(payload), 

        customShadowCallback_Update, 5)

        time.sleep(10)

# Function called when a shadow is updated

def customShadowCallback_Update(payload, responseStatus, token):

    # Display status and data from update request

    if responseStatus == "timeout":

        print("Update request " + token + " time out!")

    if responseStatus == "accepted":

        payloadDict = json.loads(payload)

        print("~~~~~~~~~~~~~~~~~~~~~~~")

        print("Update request with token: " + token + " accepted!")

        print("UV Level: " + str(payloadDict["state"]["reported"]["UVIntensity"]))

        print("~~~~~~~~~~~~~~~~~~~~~~~\n\n")

    if responseStatus == "rejected":

        print("Update request " + token + " rejected!")

# Function called when a shadow is deleted

def customShadowCallback_Delete(payload, responseStatus, token):

       # Display status and data from delete request

     if responseStatus == "timeout":

        print("Delete request " + token + " time out!")

    if responseStatus == "accepted":

        print("~~~~~~~~~~~~~~~~~~~~~~~")

        print("Delete request with token: " + token + " accepted!")

        print("~~~~~~~~~~~~~~~~~~~~~~~\n\n")

    if responseStatus == "rejected":

        print("Delete request " + token + " rejected!")

# Configure logging

# AWSIoTMQTTShadowClient writes data to the log

   def configureLogging():

    logger = logging.getLogger("AWSIoTPythonSDK.core")

    logger.setLevel(logging.DEBUG)

    streamHandler = logging.StreamHandler()

    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

    streamHandler.setFormatter(formatter)

    logger.addHandler(streamHandler)

# Init AWSIoTMQTTShadowClient

myAWSIoTMQTTShadowClient = None

myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient(clientId)

myAWSIoTMQTTShadowClient.configureEndpoint(endpoint, port)

myAWSIoTMQTTShadowClient.configureCredentials(rootCA, key, cert)

# AWSIoTMQTTShadowClient connection configuration

myAWSIoTMQTTShadowClient.configureAutoReconnectBackoffTime(1, 32, 20)

myAWSIoTMQTTShadowClient.configureConnectDisconnectTimeout(10) # 10 sec

myAWSIoTMQTTShadowClient.configureMQTTOperationTimeout(5) # 5 sec

# Connect to AWS IoT

myAWSIoTMQTTShadowClient.connect()

# Create a device shadow handler, use this to update and delete shadow document

deviceShadowHandler = 

myAWSIoTMQTTShadowClient.createShadowHandlerWithName(thingName, True)

# Delete current shadow JSON doc

# deviceShadowHandler.shadowDelete(customShadowCallback_Delete, 5)

if __name__ == '__main__':

    main()

The code seems to be working. I am getting all three sensor values in a single payload:

{
“state”: {
“reported”: {
“UVIntensity”: 0,
“humidity”: 45,
“temperature”: 24,
“airQuality”: 505
}
},
“metadata”: {
“reported”: {
“UVIntensity”: {
“timestamp”: 1592437384
},
“humidity”: {
“timestamp”: 1592437384
},
“temperature”: {
“timestamp”: 1592437384
},
“airQuality”: {
“timestamp”: 1592437384
}
}
},
“version”: 2893,
“timestamp”: 1592437384,
“clientToken”: “e8e74280-806f-4c19-85fe-9f36f8e78864”
}

For air quality, it shows as a numeric value. Is there a scale for interpreting it? It is connected to A0 on the Grove Base Hat for Raspberry Pi. Is that the same as Pin 2?

https://wiki.seeedstudio.com/Grove-Air_Quality_Sensor_v1.3/

from grove.grove_air_quality_sensor_v1_3 import GroveAirQualitySensor
AirSensor = GroveAirQualitySensor(2)  # connected to pin2
AirQ = AirSensor.value   # reading Air Qulaity sensor value
print("AQ Value: " + str(AirQ))
payload = {"state":{"reported":{"UVIntensity":int(UVLevel),"humidity":int(humi),"temperature":int(temp),"airQuality":int(AirQ)}}}

That how we need right? You can get more information about the sensor reading here at Air_quality sensor_MP503_English.