Ampy to transfer files to Pycom microcontroller
Introduction
This guide will demonstrate the use of thge Adafruit MicroPython tool (ampy) to load (put) and download (get) files and run code on a Pycom LoPy4 board connected to the serial port of the Raspberry Pi.
The Things Network and Applications for Devices
The Things Network (TTN) is an open, decentralized, and community-driven Internet of Things (IoT) network that uses the LoRaWAN (Long Range Wide Area Network) protocol. It allows devices, sensors, and other IoT-enabled objects to communicate wirelessly over long distances with low power consumption. The network is designed to be highly scalable and provides connectivity for a wide range of applications, enabling IoT solutions in various industries.
Key components of The Things Network include:
- Gateways: Gateways are devices that receive data from end devices (such as sensors) and forward it to the network server. They act as a bridge between the end devices and the cloud-based network infrastructure.
- Network Server: The network server manages the communication between end devices and applications. It handles device activation, data routing, security, and other network-related tasks.
- Application Server: The application server is where the received data from end devices is processed, analyzed, and stored. It is the backend server responsible for handling the specific application logic and interacting with the IoT devices.
- End Devices: End devices are the actual IoT devices, sensors, or objects that collect data and communicate with the gateways and network server.
Applications for devices on The Things Network refer to software applications created by users or developers to interact with and manage specific IoT devices. These applications define how the data received from the devices is processed, displayed, stored, and utilized for various purposes.
When setting up a new application on The Things Network, users define how they want the data from their IoT devices to be handled. This may include specifying the data format, integration with third-party services, data storage, data visualization, and other relevant functionalities. Each application is associated with one or more IoT devices, and the application server processes the data received from these devices based on the application's configuration.
In summary, The Things Network is a global IoT network based on LoRaWAN, enabling long-range, low-power communication for IoT devices. Applications on The Things Network define how the data from these devices is managed and utilized for specific use cases or applications, adding value to the collected IoT data.
Authors
For more information contact Adam Simankowicz or Edmond Lascaris
References
- Register a Device or Gateway on The Things Network
- Micropython basics- Load and run code
- Ampy GitHub
- rshell alternative - not used
Updating Pycom Device Firmware
- It is very important to always keep the firmware on a microcontroller updated to the most current stable version.
- Firmware is equivalent to a small operating system pre-installed on a device that helps it perform its core functions.
- Updating firmware will help to resolve bugs, give you access to new features and fix security issues.
- In this lesson we will upgrade the firmware on the Pycom4 microcontroller using the Expansion Board version 3.1.
- Full details on how to complete the firmware upgrade are availa- ble at Updating Device Firmware (pycom.io)
- The updated was conducted on a MacOS 10.13.6
Updating Firmware on the Pycom4
- Plug the Pycom4 into the Pycom Expansion Board V3.1.
- Note that if you are using another Pycom device or expansion board you will need to refer to specific instructions for that setup.
- Download the Pycom Firmware updater tool (Mac, PC, Linux) from the URL below and install the software.
https://docs.pycom.io/updatefirmware/device/- Connect the Pycom4/Expansion board to your computer and then start the Pycom Firmware updater software.
- In the Welcome screen ensure only the latest stable release is installed, not developmental releases.
- The latest version at the time of this writing is 1.16.6
- Follow any special setup instructions.
Communications settings
- The software should identify that the Pycom device is connected to the serial port (USB).
- Ensure that the following settings are selected then click Continue.
Pybytes resistration
- The following window will ask for Pybytes registration. Pybytes is an online programming tool for the Pycom microcontrollers.
- We will not be using this feature so press Skip.
Advanced settings
- The next window is the Advanced Settings.
- Include the following selections:
- File System: LittleFS – filing system optimised for microcontrollers
- Erase during update – reformat the memory using this file system
- RESET Config and NVS partitions
- LoRa Region – Australia and frequency AU915
- Then press Continue.
Upgrading
- The upgrade process will take less than one minute.
Successful update
- When completed the following output will be provided.
- It is not important to record these details, however you may wish to note down:
- Firmware version (1.20.2.r6)
- Device ID: 807D3A938770
- LoRa MAC: 70B3D54990CFD875
- Click Done when finished.
- The Pycom board can then be disconnected from the computer.
Register the Pycom LoPy4 to The Things Network
Log into The Things Network
- Go to The Things Network in Australia
https://www.thethingsnetwork.org/country/australia/- Create and account and login.
- Enter the Console
- Select the region Australia
Create an Application
- An Application is specially configured software that relates to a Things device.
- In this case the device is a Pycom Lopy4 microcontroller that will transmit temperature data.
- Click on Goto Applications
- Click on Create Application
- Enter
- Application ID
- Application name
- Description
Register end device
- Click on Register end device to start the registration process.
- Click on Enter device specific manually
- Freq plan - Australia FSB 2
- LoRaWAN version - MAC V1.0.2
- Regional Parameters - PHY V1.0.2 REV B
Advanced Activation settings
- Activation mode - Over the air activation (OTAA)
- Additional LoRaWAN class capabilities - None (Class A only)
- Network defaults - Use network's default MAC settings
JoinEUI - formerly AppEUI
The JoinEUI (Join Extended Unique Identifier), formerly known as APPEUI (Application EUI), is an identifier used in the LoRaWAN protocol and The Things Network (TTN) for device activation and joining the network. It serves as a globally unique identifier for the network or service provider that manages the Join Server.
The JoinEUI is a 64-bit (8-byte) identifier, typically represented in hexadecimal format. It is used during the Over-The-Air Activation (OTAA) process, where an end device joins the LoRaWAN network securely. OTAA is one of the two methods of device activation in LoRaWAN, the other being Activation By Personalization (ABP).
When an end device initiates the join process, it includes its Device EUI (End-Device Unique Identifier) and the JoinEUI in the join request message. The JoinEUI is used by the network server to identify which Join Server should handle the activation process for that particular device.
The Join Server, which is responsible for handling device activations and security-related procedures, uses the JoinEUI and the Device EUI to verify the device's identity and decide whether to allow it to join the network. If the device's identity is confirmed, the Join Server issues the necessary security keys (such as the AppKey) to the device, allowing it to securely communicate with the network.
To summarize, the JoinEUI is an identifier used in LoRaWAN and The Things Network to identify the network or service provider responsible for managing the Join Server during the device activation process. It plays a crucial role in ensuring secure and authenticated joining of end devices to the LoRaWAN network.
- The JoinEUI (formerly called APPEUI) is a 64 bit extended unique identified used to identify a new device during activation.
- Registering a device on The Things Network
- The JoinEUI should be a globally unique identifier for the device.
- It needs to be randomly generated using a python script in the Terminal
import secrets
secrets.token_hex(8)
- Each output will be different because the hexadecimal numbers are ramdomly generated.
- In this case the number is 0430ab1144df0a68
- Enter this number for the JoinEUI
- Click on Confirm
DevEUI
The Device EUI (End-Device Unique Identifier) is a unique identifier assigned to each end device (sensor or device) in The Things Network (TTN) and other LoRaWAN-based networks. It serves as a globally unique address for the device within the LoRaWAN network.
The Device EUI is a 64-bit (8-byte) identifier, typically represented in hexadecimal format. It plays a crucial role in identifying and authenticating the end device on the LoRaWAN network. When an end device wants to join the network or communicate with the network server, it includes its Device EUI in the messages exchanged during the process.
During the onboarding process of an end device to The Things Network, you may need to register the Device EUI in the TTN console or API. The Device EUI is used by the network server to route messages between the device and the appropriate application within the LoRaWAN network.
It's essential to keep the Device EUI unique for each end device, as it ensures that devices can be individually addressed and managed within the network.
- You can run the code below on you Pycom module to retrieve its EUI.
- This code can be executed as a program ampy on the Raspberry Pi.
- See details for using ampy below.
from network import LoRa
import ubinascii
lora = LoRa()
print("DevEUI: %s" % (ubinascii.hexlify(lora.mac()).decode('ascii')))
- Save the python file using Thonny with the file name joinEUI-test.py
- Open the Terminal and enter the following command to upload the python file to the Pycom microcontroller.
ampy -p /dev/ttyACM0 put /home/pi/joinEUI-test.py /flash/joinEUI-test.py
- Run the joinEUI-test.py with the following command.
ampy -p /dev/ttyACM0 run joinEUI-test.py
- The DevEUI: 70b3d549973919c7 will appear in the output.
- Use this number to register your Device.
- Click Confirm
AppKey
On The Things Network (TTN) and other LoRaWAN-based networks, the AppKey, short for Application Key, is a unique and secret key assigned to an application. It is used to establish secure communication between the end devices (sensors or devices) and the application backend.
The AppKey serves as a shared secret between the end device and the application server. When an end device joins the network or sends data, it uses the AppKey to encrypt the data to ensure its privacy and integrity. On the application server side, the received data is decrypted using the same AppKey to access and process the payload.
It is crucial to keep the AppKey confidential, as it plays a significant role in securing the communication between the end devices and the application. If the AppKey were to be compromised, unauthorized parties could potentially access and manipulate the data transmitted between the devices and the application server.
- Click on Generate to create a new AppKey.
- At the bottom of the page click Register new device
New Device Created
- The new device Application will be presented in a Window.
- The status will be listed as No activity yet
- To complete the registration process we need to program the Pycom to send some data to a LoraWAN Gateway.
Details to capture from Device in The Things Network
AppEUI
- The AppEUI (Application Extended Unique Identifier) is another important identifier used in The Things Network (TTN) and other LoRaWAN-based networks. It serves as a unique identifier for an application on the network. The AppEUI, along with the Device EUI (DevEUI) and the AppKey, is used to establish secure communication between the end device (sensor or device) and the application backend. The AppEUI is a 64-bit (8-byte) identifier, represented in hexadecimal format. It helps to uniquely identify an application and allows the LoRaWAN network server to route data from the associated end devices to the correct application server. When setting up an application in The Things Network, you may be required to provide an AppEUI for your application. The AppEUI is typically assigned during the application creation process, and it is used during the activation of the end devices within that application
- For a Pycom module to transmit data to the Things Network, two identifier are required AppEUI and AppKey.
- The AppKey is a unique security key assigned to your application in The Things Network (TTN) platform. It is used to authenticate and secure the communication between your device and the TTN servers. The AppKey should be kept confidential, as it ensures the integrity and privacy of your data.
AppKey
The AppKey (Application Key) is a unique security key used in The Things Network (TTN) and other LoRaWAN-based networks. It is an application-specific key that provides security for the communication between an end device (sensor or device) and the application server.
The AppKey serves as a shared secret between the end device and the application server. It is used for encryption and decryption of data transmitted between the end device and the application server. When the end device sends data to the network, it encrypts the payload using the AppKey to ensure its privacy and integrity during transmission.
On the application server side, the received data is decrypted using the same AppKey to access and process the payload.
To maintain the security of your LoRaWAN communication, it is essential to keep the AppKey confidential and not share it publicly. If the AppKey were to be compromised, unauthorized parties could potentially intercept and manipulate the data exchanged between the end devices and the application server.
Python code - Lora Library
- Both the AppEUI and the AppKey will be added to the Python code running on the Pycom to facilitate communication with the Things Network
- app_eui = binascii.unhexlify('AppEUI-here')
- app_key = binascii.unhexlify('AppKey-here')
Payload Formatters
In The Things Network (TTN) application, a payload formatter is a feature that allows you to decode and format the raw payload data received from your IoT devices into a human-readable and usable format. The payload formatter is an essential part of the application server's functionality and is particularly useful when dealing with data that comes in binary or hexadecimal format.
When an IoT device transmits data to The Things Network, it sends the payload in a raw, binary format. This raw payload typically consists of bits and bytes that represent various sensor readings or device-specific information. However, this raw data is not directly interpretable by humans or application users.
The payload formatter provides a way to parse and transform this raw payload into a meaningful format that can be easily understood by developers or visualized in the application's user interface. The payload formatter allows you to apply custom decoding, parsing, and formatting rules to extract relevant data from the raw payload.
For example, let's say you have a temperature sensor that sends data in binary format, including information about the sensor ID, battery level, and temperature readings. The raw payload might look like a sequence of ones and zeros:
01011011010010100100101001101010
With the payload formatter, you can define how to extract each piece of information from the raw payload. You could specify that the first 8 bits represent the sensor ID, the next 8 bits represent the battery level, and the remaining 16 bits represent the temperature reading.
After applying the payload formatter, the data might be displayed in a human-readable format like this:
Sensor ID: 87
Battery Level: 85%
Temperature: 25.6°C
By using the payload formatter, you can easily interpret and visualize the data from your IoT devices without having to manually decode the raw binary payload each time.
The payload formatter in The Things Network can be configured within the application settings on the TTN console. The exact method and syntax for defining the payload formatter depend on the programming language or format supported by the TTN application server. Common formats used for payload formatting are JavaScript and CayenneLPP (a lightweight binary encoding format).
Payload formatters - Javascript code
- Click on Payload formatters and Uplink
- Select Formatter type and Custom Javascript formatter
- Default formatter code in Javascript will appear.
- Replace this code with the following Javascript.
- Then click on Save
function decodeUplink(input) {
var bytes = input.bytes;
var onewire_temp_data = (bytes[0] * 256 + bytes[1]);
var bat_data = (bytes[2] * 256 + bytes[3]);
var temp_data = (bytes[4] * 256 + bytes[5]);
var humid_data = (bytes[6] * 256 + bytes[7]);
return {
data: {
onewire_temp:onewire_temp_data/100,
bat:bat_data/100,
temp:temp_data/100,
humid:humid_data/100
},
warnings: [],
errors: []
};
}
- Click on Overview to return to the main menu
Sending Data from Pycom Lopy4 to a LoRaWAN Gateway
- Micropython code has been prepared for the Pycom lopy4.
- The files are stored in the following directory structure.
- boot.py
- main.py
- lib - library collection of files
- config.py - configuration file with settings for key parameters
- dth.py - library for the DTH22 temperature sensor. The DTH22 sensor measures both temperature and humidity
- lora.py - library for lora communication with The Things Network
- onewire.py - onewire library for the onewire temperature sensor
boot.py
- Save the following code as boot.py
# boot.py -- run on boot-up
import pycom
from machine import UART
import machine
import os
from network import Bluetooth
from network import WLAN
# test
if pycom.heartbeat() == True:
pycom.heartbeat(False)
if pycom.wifi_on_boot() == True:
pycom.wifi_on_boot(False)
#wlan = WLAN()
#wlan.deinit()
bluetooth = Bluetooth()
bluetooth.deinit()
uart = UART(0, baudrate=115200)
os.dupterm(uart)
machine.main('main.py')
Code Explanation
The code provided is a boot.py script for a Pycom LoPy4 device. Let's go through the code step-by-step to understand its functionality:
Imports: The code imports necessary modules and libraries: pycom, machine, os, Bluetooth, and WLAN. These modules are used for configuring the device's settings and interactions with hardware components.
Heartbeat and Wi-Fi Settings: The code checks and disables the Pycom heartbeat and Wi-Fi on boot. The Pycom heartbeat is a built-in LED animation that indicates the device's status. By setting it to False, the heartbeat animation is turned off. Additionally, it disables Wi-Fi on boot.
WLAN and Bluetooth Deinitialization: Two wireless interfaces, WLAN (Wi-Fi) and Bluetooth, are deinitialized. Deinitialization shuts down these interfaces, preventing the device from attempting to connect to Wi-Fi networks or Bluetooth devices on startup. This can be useful to conserve power if the device does not require these functionalities immediately after booting.
UART Configuration: The code configures UART (Universal Asynchronous Receiver/Transmitter) with UART number 0 and a baud rate of 115200. UART is a standard serial communication interface, and configuring it allows communication with other devices or peripherals using this interface. The line os.dupterm(uart) redirects the device's standard input and output to the UART interface. This means that any print statements or input/output operations will be sent and received via UART.
Run main.py: The last line machine.main('main.py') initiates the execution of the main.py script. This line effectively starts the main application code once the boot.py script has completed its setup.
To summarize, the provided boot.py script disables the heartbeat LED animation, disables Wi-Fi and Bluetooth, configures UART for serial communication, and then proceeds to execute the main.py script. Depending on the specific application and project requirements, these configurations ensure that the device is set up appropriately at boot and is ready to perform its main functionality.
main.py
Code explanation
The provided code is a Python script for the main.py file running on a Pycom LoPy4 device. This script is meant to read data from various sensors (DHT22, DS18B20, and a voltage divider for battery measurement), format the sensor data, and then transmit it over LoRaWAN using the LoRa communication protocol. After data transmission, the device goes into deep sleep mode to conserve power and wake up again after a specific duration.
Let's break down the functionality of the code:
Importing Modules: The code imports various modules necessary for the device's operation, including binascii, config, DTH, gc, LoraAU915, machine, Pin, ADC, DS18X20, OneWire, pycom, network, socket, time, ustruct, and utime.
Sensor and Device Setup: The code initializes and powers up the sensors and other peripherals connected to the device. It configures ADC for battery voltage measurement, sets up the DS18B20 temperature sensor, and initializes LoRa communication using the LoraAU915 module.
Main Loop: The script then enters an infinite loop (while True) where it performs the following steps:
- a. Powers up the sensors for data measurement.
- b. Reads the temperature and humidity data from the DHT22 sensor.
- c. Measures the battery voltage using the voltage divider and ADC.
- d. Reads the temperature from the DS18B20 sensor.
- e. Formats the sensor data into byte values for transmission.
- f. Sends the formatted sensor data to The Things Network (TTN) over LoRaWAN using the s.send() function.
- g. Saves LoRa communication settings using LoraAU915.saveLora() before going to sleep.
- h. Powers down the sensors to conserve power.
- i. Enters a sleep mode (deepsleep or time.sleep) depending on the battery voltage.
If the battery voltage is above 4.0V, the device enters a time delay sleep mode for 10 minutes. If the battery voltage is below 4.0V, the device enters deep sleep mode for 60 minutes.
This script enables the Pycom LoPy4 device to periodically measure and transmit temperature, humidity, and battery voltage data over the LoRaWAN network. By utilizing sleep modes to conserve power, it allows for long-term, low-power operation for IoT applications. The transmitted data can be further processed and visualized on the LoRaWAN application server, such as The Things Network, for monitoring and analysis.
main.py code
# main.py
import binascii #module that makes conversion between binary and Ascii
import config
from dth import DTH
import gc # garbage collection
from lora import LoraAU915
import machine
from machine import Pin
from machine import ADC
from onewire import DS18X20
from onewire import OneWire
import pycom
import network
import socket
import time
import ustruct
import utime
#INIT EVERYTHING test
sleep_duration = 600 # in seconds (10 minutes)
pycom.rgbled(config.GREEN)
# power up sensors
sensor_power = Pin(config.POWER_PIN, mode=Pin.OUT) # power for all sensors
sensor_power.value(1) # power up
time.sleep(2) # wait 2 seconds to stabilise
print("starting main")
pycom.rgbled(config.OFF)
# voltage divider setup to monitor battery voltage
adc = ADC()
time.sleep(2) # wait 2 seconds to stabilise
# garbage collection enabled
gc.enable()
# config LoRa communications
s = LoraAU915.setupLora()
####################################
### LOOP IF NO NEED TO SLEEP #######
####################################
while True:
sensor_power.value(1) # power up
time.sleep(1)
pycom.rgbled(config.ORANGE)
time.sleep(1)
pycom.rgbled(config.OFF)
# TEMPERATURE AND HUMIDITY OF AMBIENT AIR
j=0
th = DTH(config.DHT22_DATA,1)
time.sleep(1)
while j < 5:
result = th.read()
if result.temperature != 0:
print("temperature is not zero - breaking from while loop")
break
time.sleep(1)
j += 1
print("Error code %d" % result.error_code)
print(j)
print("Temperature = = %.2f C" % result.temperature)
print("Humidity = %.2f %%" % result.humidity)
print("Error code final %d" % result.error_code)
# BATTERY
meanbatt=0
j=0
while j < 5:
utime.sleep(0.2)
batt = adc.channel(attn=3, pin=config.VOLTMETER)
meanbatt += batt.voltage()/1000
j += 1
batt_volt = (meanbatt/j)*config.BATCOEFF #multiplcator coef to adjust the real value of the battery voltage
print("Battery voltage = %.2f V" % batt_volt)
#DS18B20 TEMPERATURE SENSOR
#https://docs.pycom.io/tutorials/hardware/owd/#app
#Also note - needed to add a 10k resistor pull up on the board
#Internal pull up resistor was not sufficient
#used this reference for Raspberry Pi circuit which includes 5k to 10k pull up resistor
#https://www.circuitbasics.com/raspberry-pi-ds18b20-temperature-sensor-tutorial/
ow = OneWire(Pin(config.DS18B20_DATA))
temp = DS18X20(ow)
onewire_temp = temp.start_conversion()
time.sleep(1)
onewire_temp = temp.read_temp_async()
time.sleep(1)
if onewire_temp != None:
print("Onewire temperature = %.2f C" % onewire_temp)
else:
print("No data received from onewire temp probe")
pycom.rgbled(config.RED)
time.sleep(1)
pycom.rgbled(config.OFF)
onewire_temp = 0.0
onewire_temp_t = int(onewire_temp*100)
print(onewire_temp_t)
batt_volt_t = int(batt_volt*100)
print(batt_volt_t)
sens_temp = int(result.temperature*100)
print(sens_temp)
sens_humi = int(result.humidity*100)
print(sens_humi)
# converting 2 byte values into single bytes for transmission
onewire1 = int(onewire_temp_t//256)
onewire2 = int(onewire_temp_t%256)
batt1 = int(batt_volt_t//256)
batt2 = int(batt_volt_t%256)
temp1 = int(sens_temp//256)
temp2 = int(sens_temp%256)
humi1 = int(sens_humi//256)
humi2 = int(sens_humi%256)
print("sending to TTN")
s.setblocking(True)
print(onewire1,onewire2,batt1,batt2,temp1,temp2,humi1,humi2)
s.send(bytes([onewire,onewire2,batt1,batt2,temp1,temp2,humi1,humi2]))
s.setblocking(False)
# Save LoRa settings before deep sleep
LoraAU915.saveLora()
print("going to sleep")
sensor_power.value(0) # power down
# note that batt_volt has been multiplied by 100 for transmission
if (batt_volt > 4.0):
# to minimise writes to onboard memory and for teaching
sleep_duration = 600 # sleep for 10 minutes
print("Time delay sleep only")
time.sleep(sleep_duration) # sleep in seconds
else:
sleep_duration = 3600 # sleep for 60 minutes
print("Sleep using deepsleep")
machine.deepsleep(1000 * sleep_duration) # deepsleep in ms
lora.py library
Code explanation
The provided MicroPython code is a custom LoRa library specific to the AU915 region (Australia) for the Pycom LoPy4 device. This library handles LoRa communication, including joining the LoRaWAN network using OTAA (Over-The-Air Activation), setting up LoRa parameters, and creating a LoRa socket for data transmission.
Let's break down the code and understand its functionality:
LoRa Class Definition: The code defines a class named LoraAU915, which contains class-level attributes and methods for LoRa communication.
LoRa Object Initialization: At the class level, the code initializes a single instance of the LoRa object named lora with the specified LoRa mode (LoRa.LORAWAN) and region (LoRa.AU915). setupLora() Method: This method is responsible for setting up the LoRa connection and LoRaWAN parameters. It does the following:
- Initializes the LoRa channels for the AU915 region, removing channels that are not applicable to this region.
- Sets up OTAA authentication parameters, including the app_eui (Application EUI) and app_key (Application Key) needed for joining the LoRaWAN network.
- Checks if the device has previously joined the network (LoraAU915.lora.has_joined()). If it has, the LoRa settings are restored from non-volatile memory (NVRAM) using LoraAU915.lora.nvram_restore().
- If the device has not joined the network (new_connection = True), it attempts to join using the specified OTAA parameters (app_eui and app_key) and waits until it successfully joins the network.
- Creates a LoRa socket (s) for data transmission and sets the data rate (DR) to 0, equivalent to SF12 (Spreading Factor 12), for maximum range but lower data rate.
- Makes the socket non-blocking for asynchronous communication.
saveLora() Method: This method is responsible for saving the LoRa settings to non-volatile memory (NVRAM) using LoraAU915.lora.nvram_save().
The setupLora() method returns the LoRa socket (s) for data transmission after completing the LoRa setup.
Overall, this custom LoRa library simplifies the LoRaWAN configuration process for the AU915 region on the Pycom LoPy4 device. It allows the device to join the LoRaWAN network using OTAA, set up the necessary LoRa parameters, and create a socket for transmitting data over the LoRaWAN network. The library also includes a method for saving the LoRa settings to memory, ensuring that the configuration is retained even after the device powers off or resets.
"""
Lora library for MicroPython
"""
import config
import utime
import time
from network import LoRa
import socket
import binascii
import struct
import machine
class LoraAU915: #Australia AU915
# https://stackoverflow.com/questions/5690888/variable-scopes-in-python-classes
lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.AU915)
def setupLora():
#lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.AU915)
# Initialise LoRa in LORAWAN mode.
# create an OTAA authentication parameters, change them to the provided credentials
# tech-school-temp-1
# Edmond - temperature sensor demonstration
app_eui = binascii.unhexlify('YOUR_APP_EUI_HERE') - keep secret
app_key = binascii.unhexlify('YOUR_APP_KEY_HERE') - keep secret
#Limit channels for AU915
for i in range(0,8):
LoraAU915.lora.remove_channel(i)
for i in range(16,65):
LoraAU915.lora.remove_channel(i)
for i in range(66,72):
LoraAU915.lora.remove_channel(i)
# join a network using OTAA (Over the Air Activation)
#uncomment below to use LoRaWAN application provided dev_eui
#https://docs.pycom.io/tutorials/networks/lora/nvram/
new_connection = True
LoraAU915.lora.nvram_restore()
if(LoraAU915.lora.has_joined() == False):
print("LoRa memory restored but need to join LoRa")
LoraAU915.lora.join(activation=LoRa.OTAA, auth=(app_eui, app_key), timeout=0)
else:
print("Memory restored and have rejoined LoRa with previous Dev Add")
new_connection = False
# wait until the module has joined the network
while not LoraAU915.lora.has_joined():
utime.sleep(1)
if utime.time() > 15:
print("possible timeout")
machine.reset()
pass
print('Joined')
# create a LoRa socket
s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
if new_connection:
# set the LoRaWAN data rate
s.setsockopt(socket.SOL_LORA, socket.SO_DR, 0) # dr0 equiv to SF12
# make the socket non-blocking
s.setblocking(False)
print("LoRa connection set up")
utime.sleep_ms(5)
return s
def saveLora():
LoraAU915.lora.nvram_save()
print("LoRa setting saved to memory")
config.py
########################
# config.py
########################
#test
# PINs test
POWER_PIN = 'P9' # pin that powers enable pin on 5V Pololu regulator
DHT22_DATA = 'P10' # pin that receives data from internal temp/humidity sensor
DS18B20_DATA = 'P12' # pin that receives data from external temp sensor
VOLTMETER='P20' # pin connected to voltage divider
BATCOEFF = 2.0 # to calculate the real voltage
# led colors
OFF = 0x000000
WHITE=0xFFFFCC
RED = 0xff0000
BLUE = 0x0000ff
GREEN = 0x00ff00
YELLOW = 0x7f7f00
PURPLE = 0x7f007f
ORANGE = 0xFF9900
dth.py
Code explanation
The provided dht.py library is a MicroPython implementation to read data from DHT11, DHT21, or DHT22 temperature and humidity sensors. These sensors are popular for measuring temperature and humidity in various projects and applications. The library provides functionality to interact with the sensor and obtain temperature and humidity readings.
Here's an explanation of the key parts of the dht.py library:
DTHResult Class: This class represents the result returned by the DTH.read() method. It contains fields for the error code, temperature, and humidity. The is_valid() method is used to check if the result is valid (no error occurred).
DTH Class: This class is the main class for reading data from the DHT sensors. It has the following methods:
- __init__(self, pin, sensor=0): The class constructor takes the pin to which the DHT sensor is connected and an optional parameter sensor (default value is 0). The sensor parameter is used to identify the type of DHT sensor (0 for DHT11, 1 for DHT21, and 2 for DHT22).
- read(self): This method reads data from the DHT sensor and returns a DTHResult object with the temperature and humidity values. It performs the following steps:
- Pulls down the data line to initiate communication with the sensor.
- Captures the pulses using pycom.pulses_get() to get the data bits from the sensor.
- Converts the data bits into bytes to extract the temperature and humidity values.
- Calculates the checksum and verifies the integrity of the data.
- Returns a DTHResult object with the read data or an error code if there was an issue with the sensor or the data.
- __send_and_sleep(self, output, mysleep): A helper method to set the output value on the data line of the DHT sensor and sleep for a specified duration.
- __bits_to_bytes(self, bits): A helper method to convert a list of bits into bytes.
- __calculate_checksum(self, the_bytes): A helper method to calculate the checksum of the received data and verify its integrity.
The library handles different types of DHT sensors (DHT11, DHT21, DHT22) by adjusting the calculations based on the sensor type. It allows users to retrieve temperature and humidity readings from DHT sensors accurately. The DHT sensor data can be utilized for various applications, such as climate monitoring, home automation, and weather stations.
dht.py library code
"""
DHT22 temperature sensor library for MicroPython
"""
import time
import pycom
from machine import enable_irq, disable_irq, Pin
# DHT22 sensor pins - when sensor lying down
#1 - VCC - red wire Connect to 3.3 - 5V power. Sometime 3.3V power isn't enough in which case try 5V power.
#2 - Data out - white or yellow wire
#3 - Not connected
#4 - Ground - black wire
# Note - 10 Kohm resistor between VCC and the data pin
# current draw 1.5mA - pycom advise up to 6mA per pin
class DTHResult:
'DHT sensor result returned by DHT.read() method'
ERR_NO_ERROR = 0
ERR_MISSING_DATA = 1
ERR_CRC = 2
error_code = ERR_NO_ERROR
temperature = -1
humidity = -1
def __init__(self, error_code, temperature, humidity):
self.error_code = error_code
self.temperature = temperature
self.humidity = humidity
def is_valid(self):
return self.error_code == DTHResult.ERR_NO_ERROR
class DTH:
'DHT sensor (dht11, dht21,dht22) reader class for Pycom'
#__pin = Pin('P3', mode=Pin.OPEN_DRAIN)
__dhttype = 0
def __init__(self, pin, sensor=0):
self.__pin = Pin(pin, mode=Pin.OPEN_DRAIN)
self.__dhttype = sensor
self.__pin(1)
time.sleep(1.0)
def read(self):
# pull down to low
self.__send_and_sleep(0, 0.019)
data = pycom.pulses_get(self.__pin,100)
self.__pin.init(Pin.OPEN_DRAIN)
self.__pin(1)
#print(data)
bits = []
for a,b in data:
if a ==1 and 18 <= b <= 28:
bits.append(0)
if a ==1 and 65 <= b <= 75:
bits.append(1)
#print("longueur bits : %d " % len(bits))
if len(bits) != 40:
return DTHResult(DTHResult.ERR_MISSING_DATA, 0, 0)
#print(bits)
# we have the bits, calculate bytes
the_bytes = self.__bits_to_bytes(bits)
# calculate checksum and check
checksum = self.__calculate_checksum(the_bytes)
if the_bytes[4] != checksum:
return DTHResult(DTHResult.ERR_CRC, 0, 0)
# ok, we have valid data, return it
[int_rh, dec_rh, int_t, dec_t, csum] = the_bytes
if self.__dhttype==0: #dht11
rh = int_rh #dht11 20% ~ 90%
t = int_t #dht11 0..50°C
else: #dht21,dht22
rh = ((int_rh * 256) + dec_rh)/10
t = (((int_t & 0x7F) * 256) + dec_t)/10
if (int_t & 0x80) > 0:
t *= -1
return DTHResult(DTHResult.ERR_NO_ERROR, t, rh)
def __send_and_sleep(self, output, mysleep):
self.__pin(output)
time.sleep(mysleep)
def __bits_to_bytes(self, bits):
the_bytes = []
byte = 0
for i in range(0, len(bits)):
byte = byte << 1
if (bits[i]):
byte = byte | 1
else:
byte = byte | 0
if ((i + 1) % 8 == 0):
the_bytes.append(byte)
byte = 0
#print(the_bytes)
return the_bytes
def __calculate_checksum(self, the_bytes):
return the_bytes[0] + the_bytes[1] + the_bytes[2] + the_bytes[3] & 255
onewire.py
Code explanation
The provided onewire.py library is a MicroPython implementation of the OneWire protocol for interacting with OneWire devices, such as the DS18B20 temperature sensor. OneWire is a simple serial communication protocol that uses only one data wire for communication, making it suitable for connecting multiple devices on a single bus.
Let's go through the different parts of the onewire.py library:
OneWire Class:
- __init__(self, pin): The class constructor takes the pin (GPIO pin) on which the OneWire bus is connected. It initializes the pin as an open-drain output with a pull-up resistor.
- reset(self): Performs the OneWire reset function to check if any device is present on the bus. It returns True if a device is detected (presence pulse), False otherwise.
- read_bit(self): Reads a single bit from the OneWire bus.
- read_byte(self): Reads a byte (8 bits) from the OneWire bus.
- read_bytes(self, count): Reads multiple bytes from the OneWire bus specified by the count.
- write_bit(self, value): Writes a single bit to the OneWire bus. value should be 0 or 1.
- write_byte(self, value): Writes a byte (8 bits) to the OneWire bus.
- write_bytes(self, buf): Writes multiple bytes to the OneWire bus.
- select_rom(self, rom): Selects a specific device on the bus to communicate with, identified by its 8-byte ROM address (rom).
- crc8(self, data): Computes the CRC (Cyclic Redundancy Check) for the provided data.
- scan(self): Scans the OneWire bus and returns a list of ROM addresses of all attached devices.
DS18X20 Class:
- __init__(self, onewire): The class constructor takes an instance of the OneWire class and initializes the DS18X20 temperature sensor.
- isbusy(self): Checks if any DS18X20 device on the bus is busy performing a temperature conversion.
- start_conversion(self, rom=None): Initiates a temperature conversion for the specified DS18X20 device with the provided ROM address. If no ROM address is provided, it starts a conversion for the first device on the bus.
- read_temp_async(self, rom=None): Reads the temperature value of the specified DS18X20 device if the conversion is complete; otherwise, it returns None.
- convert_temp(self, rom0, data): Converts the raw temperature data into degrees Celsius and returns the temperature as a fixed-point value with two decimal places.
The OneWire class handles the low-level communication with OneWire devices on the bus, while the DS18X20 class specifically deals with the DS18B20 temperature sensor using the OneWire protocol. This library allows you to interact with DS18B20 sensors connected to the OneWire bus and retrieve temperature measurements from them.
onewire.py libary code
"""
OneWire library for MicroPython
"""
import time
import machine
class OneWire:
CMD_SEARCHROM = const(0xf0)
CMD_READROM = const(0x33)
CMD_MATCHROM = const(0x55)
CMD_SKIPROM = const(0xcc)
def __init__(self, pin):
self.pin = pin
self.pin.init(pin.OPEN_DRAIN, pin.PULL_UP)
def reset(self):
"""
Perform the onewire reset function.
Returns True if a device asserted a presence pulse, False otherwise.
"""
sleep_us = time.sleep_us
disable_irq = machine.disable_irq
enable_irq = machine.enable_irq
pin = self.pin
pin(0)
sleep_us(480)
i = disable_irq()
pin(1)
sleep_us(60)
status = not pin()
enable_irq(i)
sleep_us(420)
return status
def read_bit(self):
sleep_us = time.sleep_us
enable_irq = machine.enable_irq
pin = self.pin
pin(1) # half of the devices don't match CRC without this line
i = machine.disable_irq()
pin(0)
sleep_us(1)
pin(1)
sleep_us(1)
value = pin()
enable_irq(i)
sleep_us(40)
return value
def read_byte(self):
value = 0
for i in range(8):
value |= self.read_bit() << i
return value
def read_bytes(self, count):
buf = bytearray(count)
for i in range(count):
buf[i] = self.read_byte()
return buf
def write_bit(self, value):
sleep_us = time.sleep_us
pin = self.pin
i = machine.disable_irq()
pin(0)
sleep_us(1)
pin(value)
sleep_us(60)
pin(1)
sleep_us(1)
machine.enable_irq(i)
def write_byte(self, value):
for i in range(8):
self.write_bit(value & 1)
value >>= 1
def write_bytes(self, buf):
for b in buf:
self.write_byte(b)
def select_rom(self, rom):
"""
Select a specific device to talk to. Pass in rom as a bytearray (8 bytes).
"""
self.reset()
self.write_byte(CMD_MATCHROM)
self.write_bytes(rom)
def crc8(self, data):
"""
Compute CRC
"""
crc = 0
for i in range(len(data)):
byte = data[i]
for b in range(8):
fb_bit = (crc ^ byte) & 0x01
if fb_bit == 0x01:
crc = crc ^ 0x18
crc = (crc >> 1) & 0x7f
if fb_bit == 0x01:
crc = crc | 0x80
byte = byte >> 1
return crc
def scan(self):
"""
Return a list of ROMs for all attached devices.
Each ROM is returned as a bytes object of 8 bytes.
"""
devices = []
diff = 65
rom = False
for i in range(0xff):
rom, diff = self._search_rom(rom, diff)
if rom:
devices += [rom]
if diff == 0:
break
return devices
def _search_rom(self, l_rom, diff):
if not self.reset():
return None, 0
self.write_byte(CMD_SEARCHROM)
if not l_rom:
l_rom = bytearray(8)
rom = bytearray(8)
next_diff = 0
i = 64
for byte in range(8):
r_b = 0
for bit in range(8):
b = self.read_bit()
if self.read_bit():
if b: # there are no devices or there is an error on the bus
return None, 0
else:
if not b: # collision, two devices with different bit meaning
if diff > i or ((l_rom[byte] & (1 << bit)) and diff != i):
b = 1
next_diff = i
self.write_bit(b)
if b:
r_b |= 1 << bit
i -= 1
rom[byte] = r_b
return rom, next_diff
class DS18X20(object):
def __init__(self, onewire):
self.ow = onewire
self.roms = [rom for rom in self.ow.scan() if rom[0] == 0x10 or rom[0] == 0x28]
self.fp = True
try:
1/1
except TypeError:
self.fp = False # floatingpoint not supported
def isbusy(self):
"""
Checks wether one of the DS18x20 devices on the bus is busy
performing a temperature convertion
"""
return not self.ow.read_bit()
def start_conversion(self, rom=None):
"""
Start the temp conversion on one DS18x20 device.
Pass the 8-byte bytes object with the ROM of the specific device you want to read.
If only one DS18x20 device is attached to the bus you may omit the rom parameter.
"""
if (rom==None) and (len(self.roms)>0):
rom=self.roms[0]
if rom!=None:
rom = rom or self.roms[0]
ow = self.ow
ow.reset()
ow.select_rom(rom)
ow.write_byte(0x44) # Convert Temp
def read_temp_async(self, rom=None):
"""
Read the temperature of one DS18x20 device if the convertion is complete,
otherwise return None.
"""
if self.isbusy():
return None
if (rom==None) and (len(self.roms)>0):
rom=self.roms[0]
if rom==None:
return None
else:
ow = self.ow
ow.reset()
ow.select_rom(rom)
ow.write_byte(0xbe) # Read scratch
data = ow.read_bytes(9)
return self.convert_temp(rom[0], data)
def convert_temp(self, rom0, data):
"""
Convert the raw temperature data into degrees celsius and return as a fixed point with 2 decimal places.
"""
temp_lsb = data[0]
temp_msb = data[1]
if rom0 == 0x10:
if temp_msb != 0:
# convert negative number
temp_read = temp_lsb >> 1 | 0x80 # truncate bit 0 by shifting, fill high bit with 1.
temp_read = -((~temp_read + 1) & 0xff) # now convert from two's complement
else:
temp_read = temp_lsb >> 1 # truncate bit 0 by shifting
count_remain = data[6]
count_per_c = data[7]
if self.fp:
return temp_read - 25 + (count_per_c - count_remain) / count_per_c
else:
return 100 * temp_read - 25 + (count_per_c - count_remain) // count_per_c
elif rom0 == 0x28:
temp = None
if self.fp:
temp = (temp_msb << 8 | temp_lsb) / 16
else:
temp = (temp_msb << 8 | temp_lsb) * 100 // 16
if (temp_msb & 0xf8) == 0xf8: # for negative temperature
temp -= 0x1000
return temp
else:
assert False
Using Ampy to upload software to the Pycom
Installation of Ampy
sudo pip3 install adafruit-ampy
- To check that ampy has installed correct enter the following in the Terminal.
ampy --help
- To upgrade ampy to the latest version enter the code
sudo pip3 install adafruit-ampy --upgrade
Connecting to Pycom using Ampy
- To connect to the Pycom enter the following command:
ampy -p /dev/ttyACM0 ls
- Commands need to be entered one at a time.
- This command list the main directory on the Pycom /flash
- All files on the Pycom are found within this directory.
- -p the Pycom on the Raspberry Pi is always connected to port /dev/ttyACM0
- To list all files and directories on the Pycom enter the command:
ampy -p /dev/ttyACM0 ls -l /flash
Get files from Pycom using Ampy
- Use the get command to copy files from the pycom.
- The following commands serves as an example to get the boot.py file from the Pycom to the host computer.
- The get command will always overwrite files on the computer without warning!
ampy -p /dev/ttyACM0 get /flash/boot.py /home/pi/boot_backup.py
- The get command without a destination file name will print the contents of the file.
ampy -p /dev/ttyACM0 get /flash/boot.py
Put files onto the Pycom using Ampy
- Put files onto the Pycom with the command:
ampy -p /dev/ttyACM0 put /home/pi/test.py /flash/test.py
Run code using Ampy
- You can copy code to the Pycom and then run the code.
- Save the following code in a file named /home/pi/test.py
print('Hello world! I can count to 100:')
for i in range(1,100):
print(i)






