The Pysense expansion board from Pycom has many sensors. In Pycom Pytrack Getting Started, I showed how with some Processing code we can see the real-time position of the board in 3D. That project works with the Pysense too. How about we show a real-time display on screen of the light sensor and we push temperature and humidity data out to Adafruit IO to make a nice graph from!
If you're not yet familiar with the Pysense, have a quick look at the Pycom Pysense Overview first to get a grounding. We'll need one of the range of Pycom microcontroller boards. I'm using a WiPy so I don't have to attach an antenna!
You'll need to do a firmware upgrade on your Pysense to ensure you select the region for the LoRa radio. That’s documented that separately in Pycom Pysense & Pytrack: How to Update Firmware. If you haven't set up the Atom IDE with the pymakr package before, have a look at Internet of Things with Pycom and Adafruit IO - From Zero to Hero.
To quick recap of the sensors available on Pysense:
- Temperature
- Humidity
- Light level
- Accelerometer (movement in 3D) including roll and pitch
- Barometric pressure which can be used to infer altitude
- Battery volts
Hardware
To work along with this tutorial, you'll need the following:
- Pycom Pysense
- Any Pycom microcontroller
- Antennae for radios not using on-board antennae
- A micro-USB cable connected to a PC
Code
Sample code for all the Pycom devices is available from Github. You don't need to worry about what Github is or how to use it:
- Click the green Clone or Download button, then click Download ZIP. I'll assume you’ve downloaded this into your Downloads folder.
- Open your Downloads folder and extract the pycom-libraries-master.zip file.
- Now we'll open Atom IDE
- In Atom, from the File menu, click Open Folder and navigate to Downloads\pycom-libraries-master\pycom-libraries-master\pysense
- In between the two panels on the right, there's a More button. Click that, then Get Serial Ports
- Since I haven't yet plugged in my Pycom device there are no serial ports listed. Plug in your Pycom device if you haven't already.
- Get Serial Ports now shows me the serial port number for my Pysense.
- Click Settings then Project Settings and make sure the appropriate COM port number is entered in the "address" field. Then save the settings (Ctrl-S)
- Hit Connect
- If you get the triple greater-than prompt, you're on!
- Hit Upload to get the sample code started on your device.
When I do this I get the following error:
Traceback (most recent call last): File "main.py", line 3, in File "/flash/lib/pysense.py", line 1, in ImportError: no module named 'pycoproc' MicroPython v1.8.6-849-83e2f7f on 2018-03-19; FiPy with ESP32 Type "help()" for more information.
We need to get the file pycoproc.py from Downloads\pycom-libraries-master\pycom-libraries-master\lib and copy it into the lib folder within our Pysense project. That should fix the issue. Upload again. When the upload completes you should get output like this:
MPL3115A2 temperature: 25.875 Altitude: -31.9375 Pressure: 101721.0 Temperature: 28.81557 deg C and Relative Humidity: 51.65915 %RH Dew point: 17.86834 deg C Humidity Ambient for 24.4 deg C is 67.08653%RH Light (channel Blue lux, channel Red lux): (49, 31) Acceleration: (-0.04541016, 0.01843262, 0.9946289) Roll: 2.614045 Pitch: -1.060588 Battery voltage: 4.62147 MicroPython v1.8.6-849-83e2f7f on 2018-03-19; FiPy with ESP32 Type "help()" for more information.
Lovely!
Code Walkthrough
Let's do a quick walk-though of the code. The work is getting done by main.py. Remember that when a Pycom device boots it looks for boot.py. If it finds that it runs that first. Then it looks for main.py and runs that. Since we have code in main.py that is run every time the device resets. Let's look at that file.
At the top of the file we have a list of import statements. These pull in the necessary parts of the Python files in the project's lib folder. As we discovered with the error we had to fix, pysense.py needs procproc.py. Other than that, we can see how files like SI7006A20 are imported. The reason these files have strange names is that they directly control a particular chip on the Pysense and are named after the microchip part numbers.
After importing the libraries we need, we create objects to represent the physical devices with the Pysense() object being first because it's needed to initialize the four other devices. We can then see mp, si, lt and li are used to access the relevant values from those devices. mpp appears a few rows down to access the barometer but using PRESSURE mode instead of ALTITUDE mode.
Most of the following code is self-explanatory. The accelerometer data is covered in detail in Pycom Pytrack Getting Started including an on-screen 3D visualization of the orientation of the Pytrack board. Check that out, it's cool and gives me lots of ideas about what I can build around a 3D accelerometer.
Adding Adafruit IO
To send data to Adafruit IO I went back to my IoT From Scratch video and copied all that code and the libraries into my project. The final code file is a bit messy, but here it is!
from network import WLAN # For operation of WiFi network import time # Allows use of time.sleep() for delays import pycom # Base library for Pycom devices from umqtt import MQTTClient # For use of MQTT protocol to talk to Adafruit IO import ubinascii # Needed to run any MicroPython code import machine # Interfaces with hardware components import micropython # Needed to run any MicroPython code from pysense import Pysense # Needed to operate anything on the Pysense expansion #from LIS2HH12 import LIS2HH12 # Removed to minimise memory consumption from SI7006A20 import SI7006A20 from LTR329ALS01 import LTR329ALS01 # from MPL3115A2 import MPL3115A2,ALTITUDE,PRESSURE # Removed to minimise memory consumption # BEGIN SETTINGS # These need to be change to suit your environment # Randoms were sent to Adafruit IO for testing RANDOMS_INTERVAL = 5000 # milliseconds last_random_sent_ticks = 0 # milliseconds # Wireless network WIFI_SSID = "IoT" WIFI_PASS = "Core123698745+" # No this is not our regular password. :) # Adafruit IO (AIO) configuration AIO_SERVER = "io.adafruit.com" AIO_PORT = 1883 AIO_USER = "CoreChris" AIO_KEY = "7b2d0601a9694589a52fb2d614122cff" AIO_CLIENT_ID = ubinascii.hexlify(machine.unique_id()) # Can be anything AIO_CONTROL_FEED = "CoreChris/feeds/lights" AIO_RANDOMS_FEED = "CoreChris/feeds/randoms" AIO_FEEDS = {'temp': 'CoreChris/feeds/temperature', 'humi': 'CoreChris/feeds/humidity'} do_temp = True py = Pysense() # mp = MPL3115A2(py,mode=ALTITUDE) # Returns height in meters. Mode may also be set to PRESSURE, returning a value in Pascals si = SI7006A20(py) lt = LTR329ALS01(py) # li = LIS2HH12(py) # END SETTINGS # RGBLED # Disable the on-board heartbeat (blue flash every 4 seconds) # We'll use the LED to respond to messages from Adafruit IO pycom.heartbeat(False) time.sleep(0.1) # Workaround for a bug. # Above line is not actioned if another # process occurs immediately afterwards pycom.rgbled(0xff0000) # Status red = not working # WIFI # We need to have a connection to WiFi for Internet access # Code source: https://docs.pycom.io/chapter/tutorials/all/wlan.html wlan = WLAN(mode=WLAN.STA) wlan.connect(WIFI_SSID, auth=(WLAN.WPA2, WIFI_PASS), timeout=5000) while not wlan.isconnected(): # Code waits here until WiFi connects machine.idle() print("Connected to Wifi") pycom.rgbled(0xffd7000) # Status orange: partially working # FUNCTIONS # Function to respond to messages from Adafruit IO # def sub_cb(topic, msg): # sub_cb means "callback subroutine" # print((topic, msg)) # Outputs the message that was received. Debugging use. # if msg == b"ON": # If message says "ON" ... # pycom.rgbled(0xffffff) # ... then LED on # elif msg == b"OFF": # If message says "OFF" ... # pycom.rgbled(0x000000) # ... then LED off # else: # If any other message is received ... # print("Unknown message") # ... do nothing but output that it happened. # def random_integer(upper_bound): # return machine.rng() % upper_bound # # def send_random(): # global last_random_sent_ticks # global RANDOMS_INTERVAL # # if ((time.ticks_ms() - last_random_sent_ticks) < RANDOMS_INTERVAL): # return; # Too soon since last one sent. # # some_number = random_integer(100) # print("Publishing: {0} to {1} ... ".format(some_number, AIO_RANDOMS_FEED), end='') # try: # client.publish(topic=AIO_RANDOMS_FEED, msg=str(some_number)) # print("DONE") # except Exception as e: # print("FAILED") # finally: # last_random_sent_ticks = time.ticks_ms() # # Send temperature and humidity to Adafruit alternately to stay under rate limit def send_sensors(): temp = si.temperature() # Temperature on Pysense humi = si.humid_ambient(temp) # Calculated relative humidity given current temperature global do_temp # Use a variable defined for the whole program try: if do_temp: client.publish(topic=AIO_FEEDS['temp'], msg=str(temp)) # Send to Adafruit IO print("Temp sent") else: client.publish(topic=AIO_FEEDS['humi'], msg=str(humi)) # Send to Adafruit IO print("Humi sent") do_temp = not do_temp # Aternate true/false value except Exception as e: print("FAILED") # Use the MQTT protocol to connect to Adafruit IO client = MQTTClient(AIO_CLIENT_ID, AIO_SERVER, AIO_PORT, AIO_USER, AIO_KEY) # Subscribed messages will be delivered to this callback # client.set_callback(sub_cb) client.connect() # Connects to Adafruit IO using MQTT # client.subscribe(AIO_CONTROL_FEED) # print("Connected to %s, subscribed to %s topic" % (AIO_SERVER, AIO_CONTROL_FEED)) pycom.rgbled(0x00ff00) # Status green: online to Adafruit IO # try: # Code between try: and finally: may cause an error # so ensure the client disconnects the server if # that happens. while 1: # Repeat this loop forever client.check_msg()# Action a message if one is received. Non-blocking. # send_random() # Send a random number to Adafruit IO if it's time. send_sensors() # Send temp and humidity to Adafruit IO for x in range (0, 5): time.sleep_ms(1000) blue, red = lt.light() print('{:5}-Blue {:5}-Red Range(0-65535)'.format(blue, red)) # finally: # If an exception is thrown ... client.disconnect() # ... disconnect the client and clean up. client = None wlan.disconnect() wlan = None pycom.rgbled(0x000022)# Status blue: stopped print("Disconnected from Adafruit IO.")
Good luck on your Pycom project! Hit us up on the Core Electronics Forum for help.