Enviro+ Featherwing with Airlift Featherwing

I’ve got working code to upload to AdafruitIO, and working code to read PMS2.5 data in seperate instances. I also have code that in theory should work but exhibits a conflict on pin D11. My REPL output as follows :

code.py output:
Traceback (most recent call last):
File "code.py", line 34, in <module>
ValueError: D11 in use

My python code is as follows to upload to AdafruitIO.

# Example of using the Adafruit IO CircuitPython MQTT client
# to subscribe to an Adafruit IO feed and publish random data
# to be received by the feed.
#
# Example by Tony DiCola for Adafruit Industries
# Modified by Brent Rubell for Adafruit Industries, 2019
import time
from random import randint

from pimoroni_pms5003 import PMS5003

import board
import busio
from digitalio import DigitalInOut
from adafruit_esp32spi import adafruit_esp32spi
from adafruit_esp32spi import adafruit_esp32spi_wifimanager
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
import neopixel
from adafruit_io.adafruit_io import IO_MQTT
import adafruit_minimqtt as MQTT

pms5003 = PMS5003()
### WiFi ###

# Get wifi details and more from a secrets.py file
try:
    from secrets import secrets
except ImportError:
    print("WiFi secrets are kept in secrets.py, please add them there!")
    raise

# If you are using a board with pre-defined ESP32 Pins:
esp32_cs = DigitalInOut(board.D13)
esp32_ready = DigitalInOut(board.D11)
esp32_reset = DigitalInOut(board.D12)

# If you have an externally connected ESP32:
# esp32_cs = DigitalInOut(board.D9)
# esp32_ready = DigitalInOut(board.D10)
# esp32_reset = DigitalInOut(board.D5)

spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
"""Use below for Most Boards"""
status_light = neopixel.NeoPixel(
    board.NEOPIXEL, 1, brightness=0.2
)  # Uncomment for Most Boards
"""Uncomment below for ItsyBitsy M4"""
# status_light = dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=0.2)
# Uncomment below for an externally defined RGB LED
# import adafruit_rgbled
# from adafruit_esp32spi import PWMOut
# RED_LED = PWMOut.PWMOut(esp, 26)
# GREEN_LED = PWMOut.PWMOut(esp, 27)
# BLUE_LED = PWMOut.PWMOut(esp, 25)
# status_light = adafruit_rgbled.RGBLED(RED_LED, BLUE_LED, GREEN_LED)
wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)

# Define callback functions which will be called when certain events happen.
# pylint: disable=unused-argument
def connected(client):
    # Connected function will be called when the client is connected to Adafruit IO.
    # This is a good place to subscribe to feed changes.  The client parameter
    # passed to this function is the Adafruit IO MQTT client so you can make
    # calls against it easily.
    print("Connected to Adafruit IO!  Listening for DemoFeed changes...")
    # Subscribe to changes on a feed named DemoFeed.
    client.subscribe("DemoFeed")


def subscribe(client, userdata, topic, granted_qos):
    # This method is called when the client subscribes to a new feed.
    print("Subscribed to {0} with QOS level {1}".format(topic, granted_qos))


def unsubscribe(client, userdata, topic, pid):
    # This method is called when the client unsubscribes from a feed.
    print("Unsubscribed from {0} with PID {1}".format(topic, pid))


# pylint: disable=unused-argument
def disconnected(client):
    # Disconnected function will be called when the client disconnects.
    print("Disconnected from Adafruit IO!")


# pylint: disable=unused-argument
def message(client, feed_id, payload):
    # Message function will be called when a subscribed feed has a new value.
    # The feed_id parameter identifies the feed, and the payload parameter has
    # the new value.
    print("Feed {0} received new value: {1}".format(feed_id, payload))


# Connect to WiFi
print("Connecting to WiFi...")
wifi.connect()
print("Connected!")

# Initialize MQTT interface with the esp interface
MQTT.set_socket(socket, esp)

# Initialize a new MQTT Client object
mqtt_client = MQTT.MQTT(
    broker="https://io.adafruit.com",
    username=secrets["aio_user"],
    password=secrets["aio_key"],
)


# Initialize an Adafruit IO MQTT Client
io = IO_MQTT(mqtt_client)

# Connect the callback methods defined above to Adafruit IO
io.on_connect = connected
io.on_disconnect = disconnected
io.on_subscribe = subscribe
io.on_unsubscribe = unsubscribe
io.on_message = message

# Connect to Adafruit IO
print("Connecting to Adafruit IO...")
io.connect()

# Below is an example of manually publishing a new  value to Adafruit IO.
last = 0
print("Publishing a new message every 10 seconds...")
while True:
    reading = pms5003.read()
    pm2 = reading.data[1]
    # Explicitly pump the message loop.
    io.loop()
    # Send a new message every 10 seconds.
    if (time.monotonic() - last) >= 10:
#        value = randint(0, 100)
        print("Publishing {0} to DemoFeed.".format(pm2))
        io.publish("DemoFeed", pm2)
        last = time.monotonic()

The issue is that esp32_ready conflicts with the pms5003 libraries pin 11 assignation by the look of it. Is there a way to resolve in software ? Thanks in advance.

I’m not sure you’ll be able to fix this in software, both of the boards will be hard wired to that pin. What you can possibly do is wire the PMS5003 separately and assign the pins yourself, but that won’t be as tidy obviously.

Seems like a big oversight if the defacto standard for MQTT uploading via WiFi conflicts with the Pimoroni featherwing.

It does indeed, but you’d have to ask Pimoroni why they chose those pins. Maybe ping them on Twitter? It’s a bit easier to get hold of them there.

E mailed support, raised on discord and also as an issue on the repository for the library.

Adafruit don’t have a library in mpy so you have to resort to putting a full UART routine into Circuitpython to read the sensor, and that because of buffer actions in the MQTT libraries doesn’t play nicely either.

I’ve just watched the most recent Top Secret video by Adafruit on YouTube and they’re in the process of making an i2c version of the PMS5003. It would suck to have to buy an additional board but at least it will be an option.

I know - they shared the library with me yesterday that Limor had produced a couple of days prior.

It is (or was) a little known capability of the PMS5003 that it can do both UART and I2C - Plantower only marketed certain models as I2C, not including PMS5003 at the time.

Problem is that library is a no go for Enviro+ FeatherWing. I’ve had a lot of interaction with Adafruit on this point, but nothing from Pimoroni.

Hah, sounds like you’ve got all of the right connections! I haven’t seen the datasheet but I’d like to imagine it wouldn’t be too hard to put a software module together. Even if Pimoroni don’t make an official Enviro+ library with it included you could always roll your own.

Hi - Just wanted to say thanks for raising this. I’ve had exactly the same issue with the pin conflict when trying to use the Airlift Featherwing to send the Enviro+ readings to Adafruit IO. It does seem to be an annoying design issue (along with the temperature being way too high).
Ideas to solve the pin conflict are:
1 - send the data over Bluetooth to a Raspberry PI then via WiFi to AdafruitIO
2 - use the Adafruit Airlift breakout instead and wire it to different pins
To be honest I have moved on to other projects now but, although I learned a lot, feel a bit frustrated.

Hi Stephen,

I ended up just using the PMS5003 by itself through this - https://shop.pimoroni.com/products/particulate-matter-sensor-breakout and using an ESP8266 with some arduino code to use the Thingspeak API instead of AdafruitIO.

My PMS5003 feed is currently on my site at https://wildestpixel.co.uk/live-pms5003-feed as a result.

I’ve got a BME280 on feather I’m going to airlift to AdafruitIO or Thingspeak in the next few days.

I was equally frustrated too as in consideration of the design, knowing its not going to be of use for IOT data with the defacto airlift, some workaround should have been looked at early on.

I’m also not sure how one is supposed to use for Luftdaten as advertised without jumping through many many hoops.

Wow! Site looks great and that is a neat (and cheap!) solution. Will copy you if that’s OK 😉

I could have saved myself the cost of the featherwing but you only ever learn by doing!

Thanks again

Hi Stephen,

I used this as a base on a Wemos D1 Mini ESP8266 - https://how2electronics.com/iot-air-quality-monitoring-esp8266/

Didn’t need modifying but I did to get better formatted serial data for offline logging (Excel import)

Note that the Pimoroni breakout is diff to most UART boards and you connect TX to TX and RX to RX - I found this by trial and error instead of RTFM.