├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── GoogleIOT ├── .gitignore ├── README.md ├── flash │ ├── cert │ │ └── .gitkeep │ ├── config.example.py │ ├── google_iot_core.py │ ├── main.py │ └── umqtt.py └── genkey.sh ├── Makefile ├── README.md ├── deepsleep └── deepsleep.py ├── examples ├── DS18X20 │ ├── boot.py │ ├── main.py │ └── onewire.py ├── OTA-lorawan │ ├── LoraServer.py │ ├── README.md │ ├── config.py │ ├── diff_match_patch.py │ ├── firmware │ │ ├── 1.17.0 │ │ │ └── flash │ │ │ │ ├── OTA_INFO.py │ │ │ │ ├── diff_match_patch.py │ │ │ │ ├── loranet.py │ │ │ │ ├── main.py │ │ │ │ ├── ota.py │ │ │ │ └── watchdog.py │ │ └── 1.17.1 │ │ │ └── flash │ │ │ ├── OTA_INFO.py │ │ │ ├── diff_match_patch.py │ │ │ ├── loranet.py │ │ │ ├── main.py │ │ │ ├── ota.py │ │ │ └── watchdog.py │ ├── groupUpdater.py │ ├── ota.py │ ├── requirements.txt │ └── updaterService.py ├── OTA │ ├── 1.0.0 │ │ └── flash │ │ │ ├── config.py │ │ │ ├── get_id.py │ │ │ ├── lib │ │ │ └── OTA.py │ │ │ └── main.py │ ├── 1.0.1 │ │ └── flash │ │ │ ├── config.py │ │ │ ├── get_id.py │ │ │ ├── lib │ │ │ └── OTA.py │ │ │ └── main.py │ ├── OTA_server.py │ └── README.md ├── README.md ├── accelerometer_wake │ └── main.py ├── adc │ ├── boot.py │ └── main.py ├── bluetooth │ ├── boot.py │ └── main.py ├── deepsleep │ └── main.py ├── https │ ├── boot.py │ └── main.py ├── i2c │ ├── bh1750fvi.py │ ├── boot.py │ └── main.py ├── lopy-lopy │ ├── lopy-A │ │ ├── boot.py │ │ └── main.py │ └── lopy-B │ │ ├── boot.py │ │ └── main.py ├── loraNanoGateway │ ├── gateway │ │ ├── boot.py │ │ └── main.py │ └── node │ │ ├── boot.py │ │ └── main.py ├── loraabp │ ├── boot.py │ └── main.py ├── loramac │ ├── boot.py │ └── main.py ├── lorawan-nano-gateway │ ├── abp_node.py │ ├── abp_node_US915.py │ ├── config.py │ ├── main.py │ ├── nanogateway.py │ ├── otaa_node.py │ └── otaa_node_US915.py ├── lorawan-regional-examples │ ├── main_AS923.py │ ├── main_AU915.py │ ├── main_EU868.py │ └── main_US915.py ├── mqtt │ ├── boot.py │ ├── main.py │ └── mqtt.py ├── onlineLog │ ├── __init__.py │ ├── boot.py │ ├── main.py │ └── onewire.py ├── pytrack_pysense_accelerometer │ ├── main.py │ └── visualiser │ │ ├── README.md │ │ ├── pycomLogoGoInventGrey800.png │ │ ├── sketch.properties │ │ └── visualiser.pyde ├── sigfoxUplink │ ├── boot.py │ └── main.py └── threading │ ├── boot.py │ └── main.py ├── img └── logo.png ├── lib ├── ADS1115 │ ├── ADS1115.py │ └── boot.py ├── ALSPT19 │ └── ALSPT19.py ├── TB6612FNG │ └── TB6612FNG.py ├── mqtt │ └── mqtt.py ├── mqtt_aws │ ├── MQTTClient.py │ ├── MQTTConst.py │ ├── MQTTDeviceShadow.py │ ├── MQTTLib.py │ ├── MQTTMsgHandler.py │ └── MQTTShadowManager.py ├── onewire │ └── onewire.py └── sqnsupgrade │ ├── README.md │ ├── sqnsbr.py │ ├── sqnsbrz.py │ ├── sqnscodec.py │ ├── sqnscrc.py │ ├── sqnstp.py │ └── sqnsupgrade.py ├── license ├── Pycom CLA Apache v1.pdf ├── Pycom FAQ v2.2.pdf └── Pycom Licences v2.2.pdf ├── pycom-docker-fw-build ├── Dockerfile ├── README.md └── assets │ └── build ├── pymesh ├── mobile_app │ ├── README.md │ └── app-release.apk ├── pymesh_frozen │ ├── README.md │ ├── copy_fw.sh │ ├── lib │ │ ├── ble_rpc.py │ │ ├── ble_services.py │ │ ├── cli.py │ │ ├── gps.py │ │ ├── loramesh.py │ │ ├── mesh_interface.py │ │ ├── mesh_internal.py │ │ ├── meshaging.py │ │ ├── msgpack │ │ │ ├── __init__.py │ │ │ ├── _version.py │ │ │ ├── exceptions.py │ │ │ └── fallback.py │ │ ├── pymesh.py │ │ ├── pymesh_config.py │ │ ├── pymesh_debug.py │ │ └── statistics.py │ ├── lorawan │ │ ├── lorawan.py │ │ └── main.py │ ├── main-pybytes.py │ ├── main.py │ └── main_BR.py └── readme.md └── shields ├── README.md ├── lib ├── L76GNSS.py ├── LIS2HH12.py ├── LTR329ALS01.py ├── MFRC630.mpy ├── MFRC630.mpy-1.18 ├── MFRC630.py ├── MPL3115A2.py ├── SI7006A20.py ├── pycoproc_1.py └── pycoproc_2.py ├── pyscan_1.py ├── pysense_1.py ├── pysense_2.py ├── pytrack_1.py ├── pytrack_2.py └── shield_2.py /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via an issue, email, or any other method with the owners of this repository before making a change. 4 | 5 | *Please note we have a Code of Conduct, please follow it in all your interactions with the project.* 6 | 7 | ## Pull Request Process 8 | 9 | 1. Update the `README.md` with details of changes to the interface, this includes new environment variables, useful file locations and container parameters. 10 | 2. Increase the version numbers in any examples files and the `README.md` to the new version that this Pull Request would represent. The versioning scheme we use is [SemVer](https://semver.org). 11 | 12 | ## Code of Conduct 13 | 14 | ### Our Pledge 15 | 16 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 17 | 18 | ### Our Standards 19 | 20 | Examples of behaviour that contributes to creating a positive environment include: 21 | 22 | * Using welcoming and inclusive language 23 | * Being respectful of differing viewpoints and experiences 24 | * Gracefully accepting constructive criticism 25 | * Focusing on what is best for the community 26 | * Showing empathy towards other community members 27 | 28 | Examples of unacceptable behaviour by participants include: 29 | 30 | * The use of sexualised language or imagery and unwelcome sexual attention or advances 31 | * Trolling, insulting/derogatory comments, and personal or political attacks 32 | * Public or private harassment 33 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 34 | * Other conduct which could reasonably be considered inappropriate in a professional setting 35 | 36 | ### Our Responsibilities 37 | 38 | Project maintainers are responsible for clarifying the standards of acceptable behaviour and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behaviour. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviours that they deem inappropriate, threatening, offensive, or harmful. 41 | 42 | ### Scope 43 | 44 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 45 | 46 | ### Enforcement 47 | 48 | Instances of abusive, harassing, or otherwise unacceptable behaviour may be reported by contacting the project team at `support[at]pycom.io` and adding the `[CoC]` tag to the subject line. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 49 | 50 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 51 | 52 | ### Attribution 53 | 54 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 55 | 56 | [homepage]: http://contributor-covenant.org 57 | [version]: http://contributor-covenant.org/version/1/4 58 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | (Hi! 👋 Thanks for reporting an issue! Please make sure you click the link above to view the issue guidelines, then fill out the blanks below.) 8 | 9 | ## What are the steps to reproduce this issue? 10 | 1. 11 | 2. 12 | 3. 13 | 14 | ## What happens? 15 | 16 | 17 | ## What were you expecting to happen? 18 | 19 | 20 | ## Any logs, error output, etc? 21 | *(If it’s long, please paste to https://gist.github.com and insert the link here)* 22 | 23 | 24 | ## Any other comments? 25 | 26 | 27 | ## What versions of software are you using? 28 | - **Board type and hardware version:** 29 | - **`os.uname()` output:** 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | # Feature Request 🚀 8 | 9 | ## Is your feature request related to a problem? Please describe. 10 | *A clear and concise description of what the problem is. E.g. I have an issue when...* 11 | 12 | 13 | ## Describe the solution you'd like 14 | *A clear and concise description of what you want to happen. Add any considered drawbacks.* 15 | 16 | 17 | ## Describe alternatives you've considered 18 | *A clear and concise description of any alternative solutions or features you've considered.* 19 | 20 | 21 | ## Teachability, Documentation, Adoption, Migration Strategy 22 | *If you can, explain how users will be able to use this and possibly write out a version the docs. Maybe a screenshot or design?* 23 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | (Hi! 👋 Thanks for sending a pull request! Please make sure you click the link above to view the contribution guidelines, then fill out the blanks below.) 2 | 3 | ## What does this implement/fix? Explain your changes. 4 | 5 | 6 | ## Does this close any currently open issues? 7 | 8 | 9 | ## Any relevant logs, error output, etc? 10 | *(If it’s long, please paste to https://gist.github.com and insert the link here)* 11 | 12 | 13 | ## Any other comments? 14 | 15 | 16 | ## Where has this been tested? 17 | - **Board type and hardware version:** 18 | - **`os.uname()` output:** 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.e6t 2 | *.e4q 3 | *.e4p 4 | *.1 5 | *.conf 6 | .DS_Store 7 | 8 | pyscan/* 9 | pysense/* 10 | pysense2/* 11 | pytrack/* 12 | pytrack2/* 13 | 14 | pyscan.zip 15 | pysense.zip 16 | pysense2.zip 17 | pytrack.zip 18 | pytrack2.zip 19 | 20 | -------------------------------------------------------------------------------- /GoogleIOT/.gitignore: -------------------------------------------------------------------------------- 1 | db/* 2 | *.pem 3 | flash/config.py 4 | -------------------------------------------------------------------------------- /GoogleIOT/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | # Google Cloud Iot Core MQTT connection library 4 | 5 | ### requirement 6 | 7 | Pycom Firmware >= 1.20.0.rc11 8 | 9 | You will need to setup a Google IoT core registry as described here: https://cloud.google.com/iot/docs/quickstart#create_a_device_registry 10 | 11 | During the activation please collect the following informations: 'project_id', 12 | 'cloud_region' and 'registry_id'. 13 | 14 | ### usage 15 | 16 | - create a device registry: 17 | https://cloud.google.com/iot/docs/quickstart#create_a_device_registry 18 | - generate a key using the provided tool genkey.sh and add it to the platform 19 | - add the public key to Google IoT Core : 20 | https://cloud.google.com/iot/docs/quickstart#add_a_device_to_the_registry 21 | - copy config.example.py to config.py and edit the variable 22 | - upload the project using pymakr 23 | -------------------------------------------------------------------------------- /GoogleIOT/flash/cert/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pycom/pycom-libraries/75d0e67cb421e0576a3a9677bb0d9d81f27ebdb7/GoogleIOT/flash/cert/.gitkeep -------------------------------------------------------------------------------- /GoogleIOT/flash/config.example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | ''' Set here you setup config in this example 12 | ''' 13 | CONFIG = { 14 | 'wifi_ssid': "somewifi", 15 | 'wifi_password': 'iforgot', 16 | 'ntp_server': 'time.google.com', 17 | 'project_id': 'pybytes-101', # replace with your Google project_id 18 | 'cloud_region': 'us-central1', # replace 19 | 'registry_id': 'goinvent', # replace with your Google registry_id 20 | 'topic': '/devices/pysense2/events', # replace so match your device 21 | 'device_id': 'pysense2' # 22 | } 23 | -------------------------------------------------------------------------------- /GoogleIOT/flash/google_iot_core.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | ''' A MQTT wrapper for Google Cloud IoT MQTT bridge 12 | Extended from umqtt.robust by Paul Sokolovsky with wrap of Google credentials 13 | 14 | https://github.com/micropython/micropython-lib/tree/master/umqtt.robust 15 | https://github.com/micropython/micropython-lib/tree/master/umqtt.simple 16 | 17 | Quick API Reference: 18 | connect(...) - Connect to a server. Returns True if this connection uses 19 | persistent session stored on a server (this will be always 20 | False if clean_session=True argument is used (default)). 21 | disconnect() - Disconnect from a server, release resources. 22 | ping() - Ping server (response is processed automatically by wait_msg()). 23 | publish() - Publish a message. 24 | subscribe() - Subscribe to a topic. 25 | set_callback() - Set callback for received subscription messages. 26 | wait_msg() - Wait for a server message. A subscription message will be 27 | delivered to a callback set with set_callback(), any other 28 | messages will be processed internally. 29 | check_msg() - Check if there's pending message from server. If yes, process 30 | the same way as wait_msg(), if not, return immediately. 31 | ''' 32 | 33 | import json 34 | from binascii import b2a_base64 35 | from binascii import a2b_base64 36 | import ucrypto 37 | import utime 38 | import umqtt 39 | 40 | def _create_unsigned_jwt(project_id, expires=60 * 60 * 24): 41 | header = { 42 | 'alg': "RS256", 43 | 'typ': 'JWT' 44 | } 45 | token = { 46 | 'iat': utime.time(), 47 | 'exp': utime.time() + expires, 48 | 'aud': project_id 49 | } 50 | return b2a_base64(json.dumps(header)) + "." + \ 51 | b2a_base64(json.dumps(token)) 52 | 53 | def _get_google_client_id( 54 | project_id, 55 | cloud_region, 56 | registry_id, 57 | device_id): 58 | return "projects/%s/locations/%s/registries/%s/devices/%s" % ( 59 | project_id, cloud_region, registry_id, device_id) 60 | 61 | def _create_google_jwt(project_id, private_key): 62 | to_sign = _create_unsigned_jwt(project_id) 63 | signed = ucrypto.generate_rsa_signature(to_sign, private_key) 64 | return to_sign + b'.' + b2a_base64(signed) 65 | 66 | 67 | class GoogleMQTTClient(umqtt.MQTTClient): 68 | ''' Instanciate a mqtt client 69 | Args: 70 | var_int (int): An integer. 71 | var_str (str): A string. 72 | project_id (str): your google's project_id 73 | private_key (bytes): private key bytes in pk8s format 74 | cloud_region (str): your google's region 75 | registry_id (str): the name you had given to your registry 76 | device_id: (str): the human friendly device name 77 | ''' 78 | 79 | DELAY = 2 80 | DEBUG = True 81 | GOOGLE_CA = '/flash/cert/google_roots.pem' 82 | GOOGLE_MQTT = 'mqtt.googleapis.com' 83 | 84 | def __init__( 85 | self, 86 | project_id, 87 | private_key, 88 | cloud_region, 89 | registry_id, 90 | device_id): 91 | self.private_key = private_key 92 | self.project_id = project_id 93 | self.jwt = _create_google_jwt(self.project_id, self.private_key) 94 | google_client_id = _get_google_client_id( 95 | project_id, cloud_region, registry_id, device_id) 96 | google_args = self._get_google_mqtt_args(self.jwt) 97 | super().__init__(google_client_id, self.GOOGLE_MQTT, **google_args) 98 | 99 | def delay(self, i): 100 | utime.sleep(self.DELAY + i) 101 | 102 | def log(self, in_reconnect, err): 103 | if self.DEBUG: 104 | if in_reconnect: 105 | print("mqtt reconnect: %r" % err) 106 | else: 107 | print("mqtt: %r" % err) 108 | 109 | def reconnect(self): 110 | i = 0 111 | while True: 112 | if not self.is_jwt_valid(): 113 | self.pswd = self.jwt = _create_google_jwt( 114 | self.project_id, self.private_key) 115 | try: 116 | return super().connect(False) 117 | except OSError as exception: 118 | self.log(True, exception) 119 | i += 1 120 | self.delay(i) 121 | 122 | def publish(self, topic, msg, retain=False, qos=0): 123 | if qos == 2: 124 | raise Exception("qos=2 not supported by mqtt bridge") 125 | 126 | while True: 127 | try: 128 | return super().publish(topic, msg, retain, qos) 129 | except OSError as exception: 130 | self.log(False, exception) 131 | self.reconnect() 132 | 133 | def wait_msg(self): 134 | while True: 135 | try: 136 | return super().wait_msg() 137 | except OSError as exception: 138 | self.log(False, exception) 139 | self.reconnect() 140 | 141 | def _get_google_mqtt_args(self, jwt): 142 | arguments = { 143 | 'user': '', 144 | 'password': jwt, 145 | 'port': 8883, 146 | 'ssl': True, 147 | 'ssl_params': { 148 | 'ca_certs': self.GOOGLE_CA 149 | } 150 | } 151 | return arguments 152 | 153 | def is_jwt_valid(self): 154 | try: 155 | token = json.loads(a2b_base64(self.jwt.decode().split('.')[1])) 156 | except Exception: 157 | return False 158 | return utime.time() - token.get('iat') < 60 * 60 * 24 159 | 160 | def set_last_will(self, topic, msg, retain=False, qos=0): 161 | raise Exception("set_last_will not supported by mqtt bridge") 162 | -------------------------------------------------------------------------------- /GoogleIOT/flash/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | ''' Example Google IoT Core connection 12 | ''' 13 | import utime 14 | import machine 15 | import _thread 16 | from network import WLAN 17 | from google_iot_core import GoogleMQTTClient 18 | from config import CONFIG 19 | 20 | # Connect to Wifi 21 | WLAN_I = WLAN(mode=WLAN.STA, max_tx_pwr=78) 22 | print('Connecting to WiFi %s' % CONFIG.get('wifi_ssid')) 23 | WLAN_I.connect(CONFIG.get('wifi_ssid'), (WLAN.WPA2, CONFIG.get('wifi_password')), timeout=60000) 24 | i = 0 25 | while not WLAN_I.isconnected(): 26 | i = i + 1 27 | # print(".", end="") 28 | utime.sleep(1) 29 | if i > 60: 30 | print("\nWifi not available") 31 | break 32 | 33 | # Syncing time 34 | RTCI = machine.RTC() 35 | print('Syncing time with %s' % CONFIG.get('ntp_server'), end='') 36 | RTCI.ntp_sync(CONFIG.get('ntp_server')) 37 | while not RTCI.synced(): 38 | print('.', end='') 39 | utime.sleep(1) 40 | print('') 41 | 42 | # read the private key 43 | FILE_HANDLE = open("cert/%s-pk8.key" % CONFIG.get('device_id')) 44 | PRIVATE_KEY = FILE_HANDLE.read() 45 | FILE_HANDLE.close() 46 | 47 | # make a mqtt client, connect and publish an empty message 48 | MQTT_CLIENT = GoogleMQTTClient(CONFIG.get('project_id'), 49 | PRIVATE_KEY, 50 | CONFIG.get('cloud_region'), 51 | CONFIG.get('registry_id'), 52 | CONFIG.get('device_id')) 53 | 54 | 55 | MQTT_CLIENT.connect() 56 | MQTT_CLIENT.publish(CONFIG.get('topic'), b'test') 57 | 58 | # make a demo callback 59 | def _sub_cb(topic, msg): 60 | ''' handle your message received here ... 61 | ''' 62 | print('received:', topic, msg) 63 | 64 | # register callback 65 | MQTT_CLIENT.set_callback(_sub_cb) 66 | 67 | # example subscription 68 | MQTT_CLIENT.subscribe('/devices/%s/config' % CONFIG.get('device_id'), qos=1) 69 | while True: 70 | # Non-blocking wait for message 71 | MQTT_CLIENT.check_msg() 72 | # Then need to sleep to avoid 100% CPU usage (in a real 73 | # app other useful actions would be performed instead) 74 | utime.sleep_ms(100) 75 | -------------------------------------------------------------------------------- /GoogleIOT/genkey.sh: -------------------------------------------------------------------------------- 1 | # Google Cloud IoT Core 2 | if [[ $# -eq 0 ]] ; then 3 | echo "Usage: $0 device_id" 4 | exit 0 5 | fi 6 | 7 | DB_DIR='db' 8 | DEVICE_ID=$1 9 | 10 | if [ ! -f $DB_DIR/$DEVICE_ID-priv.pem ]; then 11 | openssl genrsa -out $DB_DIR/$DEVICE_ID-priv.pem 2048 12 | fi 13 | 14 | if [ ! -f $DB_DIR/$DEVICE_ID-pub.pem ]; then 15 | openssl rsa -in $DB_DIR/$DEVICE_ID-priv.pem -pubout -out $DB_DIR/$DEVICE_ID-pub.pem 16 | fi 17 | 18 | if [ ! -f flash/cert/$DEVICE_ID-pk8.key ]; then 19 | openssl pkcs8 -topk8 -nocrypt -in $DB_DIR/$DEVICE_ID-priv.pem -out flash/cert/$DEVICE_ID-pk8.key 20 | fi 21 | 22 | if [ ! -f flash/cert/google_roots.pem ]; then 23 | wget "https://pki.google.com/roots.pem" -O flash/cert/google_roots.pem 24 | fi 25 | 26 | echo "Please add this public key to Google Cloud IoT Core Registry" 27 | cat $DB_DIR/$DEVICE_ID-pub.pem 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | release: pyscan pysense pysense2 pytrack pytrack2 2 | 3 | 4 | pyscan: 5 | rm -rf pyscan 6 | rm -f pyscan.zip 7 | @echo "Making Pyscan" 8 | mkdir pyscan 9 | mkdir pyscan/lib 10 | #sensors 11 | cp shields/lib/LIS2HH12.py pyscan/lib/ 12 | cp shields/lib/MFRC630.py pyscan/lib/ 13 | cp shields/lib/SI7006A20.py pyscan/lib/ 14 | cp shields/lib/LTR329ALS01.py pyscan/lib/ 15 | #pycoproc 16 | cp shields/lib/pycoproc_1.py pyscan/lib/ 17 | #example 18 | cp shields/pyscan_1.py pyscan/main.py 19 | 20 | zip -r pyscan.zip pyscan 21 | 22 | pysense: 23 | rm -rf pysense 24 | rm -f pysense.zip 25 | @echo "Making Pysense" 26 | mkdir pysense 27 | mkdir pysense/lib 28 | # sensors 29 | cp shields/lib/LIS2HH12.py pysense/lib/ 30 | cp shields/lib/LTR329ALS01.py pysense/lib/ 31 | cp shields/lib/MPL3115A2.py pysense/lib/ 32 | cp shields/lib/SI7006A20.py pysense/lib/ 33 | # pycoproc 34 | cp shields/lib/pycoproc_1.py pysense/lib/ 35 | # example 36 | cp shields/pysense_1.py pysense/main.py 37 | 38 | zip -r pysense.zip pysense 39 | 40 | pysense2: 41 | rm -rf pysense2 42 | rm -f pysense2.zip 43 | @echo "Making Pysense 2" 44 | mkdir pysense2 45 | mkdir pysense2/lib 46 | # sensors 47 | cp shields/lib/LIS2HH12.py pysense2/lib/ 48 | cp shields/lib/LTR329ALS01.py pysense2/lib/ 49 | cp shields/lib/MPL3115A2.py pysense2/lib/ 50 | cp shields/lib/SI7006A20.py pysense2/lib/ 51 | # pycoproc 52 | cp shields/lib/pycoproc_2.py pysense2/lib/ 53 | # example 54 | cp shields/pysense_2.py pysense2/main.py 55 | 56 | zip -r pysense2.zip pysense2 57 | 58 | pytrack: 59 | rm -rf pytrack 60 | rm -f pytrack.zip 61 | @echo "Making Pytrack" 62 | mkdir pytrack 63 | mkdir pytrack/lib 64 | #sensors 65 | cp shields/lib/L76GNSS.py pytrack/lib/ 66 | cp shields/lib/LIS2HH12.py pytrack/lib/ 67 | #pycoproc 68 | cp shields/lib/pycoproc_1.py pytrack/lib/ 69 | #example 70 | cp shields/pytrack_1.py pytrack/main.py 71 | 72 | zip -r pytrack.zip pytrack 73 | 74 | pytrack2: 75 | rm -rf pytrack2 76 | rm -f pytrack2.zip 77 | @echo "Making Pytrack2" 78 | mkdir pytrack2 79 | mkdir pytrack2/lib 80 | #sensors 81 | cp shields/lib/L76GNSS.py pytrack2/lib/ 82 | cp shields/lib/LIS2HH12.py pytrack2/lib/ 83 | #pycoproc 84 | cp shields/lib/pycoproc_2.py pytrack2/lib/ 85 | #example 86 | cp shields/pytrack_2.py pytrack2/main.py 87 | 88 | zip -r pytrack2.zip pytrack2 89 | 90 | clean: 91 | @echo "Cleaning up files" 92 | rm -rf pyscan 93 | rm -rf pysense 94 | rm -rf pysense2 95 | rm -rf pytrack 96 | rm -rf pytrack2 97 | 98 | rm -f pyscan.zip 99 | rm -f pysense.zip 100 | rm -f pysense2.zip 101 | rm -f pytrack.zip 102 | rm -f pytrack2.zip -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | # Pycom Libraries and Examples 4 | 5 | ## Introduction 6 | This repository contains libraries and out of the box examples for Pycom devices, including the Shields: Pysense, Pytrack, and Pyscan. 7 | 8 | ## Table of Contents 9 | * [Examples](/examples) 10 | * [Libraries](/lib) 11 | * [Shields](/shields) 12 | 13 | ## Links 14 | * [Pycom](https://pycom.io) 15 | * [Forum](https://forum.pycom.io) 16 | * [Docs](https://docs.pycom.io) 17 | -------------------------------------------------------------------------------- /examples/DS18X20/boot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from machine import UART 12 | import machine 13 | import os 14 | 15 | uart = UART(0, baudrate=115200) 16 | os.dupterm(uart) 17 | 18 | machine.main('main.py') 19 | -------------------------------------------------------------------------------- /examples/DS18X20/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | import time 12 | from machine import Pin 13 | from onewire import DS18X20 14 | from onewire import OneWire 15 | 16 | #DS18B20 data line connected to pin P10 17 | ow = OneWire(Pin('P10')) 18 | temp = DS18X20(ow) 19 | 20 | while True: 21 | temp.start_conversion() 22 | time.sleep(1) 23 | print(temp.read_temp_async()) 24 | time.sleep(1) 25 | -------------------------------------------------------------------------------- /examples/OTA-lorawan/README.md: -------------------------------------------------------------------------------- 1 | Coming soon 2 | 3 | -------------------------------------------------------------------------------- /examples/OTA-lorawan/config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2020, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | #LORASERVER configuration 12 | LORASERVER_IP = "127.0.0.1" 13 | LORASERVER_URL = 'http://localhost' 14 | LORASERVER_MQTT_PORT = 1883 15 | LORASERVER_API_PORT = 8080 16 | LORASERVER_EMAIL = 'admin' 17 | LORASERVER_PASS = 'admin' 18 | 19 | LORASERVER_SERVICE_PROFILE = 'ota_sp' 20 | LORASERVER_DOWNLINK_DR = 5 21 | LORASERVER_DOWNLINK_FREQ = 869525000 22 | LORASERVER_APP_ID = 1 # Read from Web Interface / Applications 23 | 24 | #update configuration 25 | UPDATE_DELAY = 300 26 | -------------------------------------------------------------------------------- /examples/OTA-lorawan/firmware/1.17.0/flash/OTA_INFO.py: -------------------------------------------------------------------------------- 1 | 1.17.0 2 | -------------------------------------------------------------------------------- /examples/OTA-lorawan/firmware/1.17.0/flash/loranet.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from network import LoRa 12 | import socket 13 | import binascii 14 | import struct 15 | import time 16 | import _thread 17 | 18 | class LoraNet: 19 | def __init__(self, frequency, dr, region, device_class=LoRa.CLASS_C, activation = LoRa.OTAA, auth = None): 20 | self.frequency = frequency 21 | self.dr = dr 22 | self.region = region 23 | self.device_class = device_class 24 | self.activation = activation 25 | self.auth = auth 26 | self.sock = None 27 | self._exit = False 28 | self.s_lock = _thread.allocate_lock() 29 | self.lora = LoRa(mode=LoRa.LORAWAN, region = self.region, device_class = self.device_class) 30 | 31 | self._msg_queue = [] 32 | self.q_lock = _thread.allocate_lock() 33 | self._process_ota_msg = None 34 | 35 | def stop(self): 36 | self._exit = True 37 | 38 | def init(self, process_msg_callback): 39 | self._process_ota_msg = process_msg_callback 40 | 41 | def receive_callback(self, lora): 42 | events = lora.events() 43 | if events & LoRa.RX_PACKET_EVENT: 44 | rx, port = self.sock.recvfrom(256) 45 | if rx: 46 | if '$OTA' in rx: 47 | print("OTA msg received: {}".format(rx)) 48 | self._process_ota_msg(rx.decode()) 49 | else: 50 | self.q_lock.acquire() 51 | self._msg_queue.append(rx) 52 | self.q_lock.release() 53 | 54 | def connect(self): 55 | if self.activation != LoRa.OTAA and self.activation != LoRa.ABP: 56 | raise ValueError("Invalid Lora activation method") 57 | if len(self.auth) < 3: 58 | raise ValueError("Invalid authentication parameters") 59 | 60 | self.lora.callback(trigger=LoRa.RX_PACKET_EVENT, handler=self.receive_callback) 61 | 62 | # set the 3 default channels to the same frequency 63 | self.lora.add_channel(0, frequency=self.frequency, dr_min=0, dr_max=5) 64 | self.lora.add_channel(1, frequency=self.frequency, dr_min=0, dr_max=5) 65 | self.lora.add_channel(2, frequency=self.frequency, dr_min=0, dr_max=5) 66 | 67 | # remove all the non-default channels 68 | for i in range(3, 16): 69 | self.lora.remove_channel(i) 70 | 71 | # authenticate with abp or ota 72 | if self.activation == LoRa.OTAA: 73 | self._authenticate_otaa(self.auth) 74 | else: 75 | self._authenticate_abp(self.auth) 76 | 77 | # create socket to server 78 | self._create_socket() 79 | 80 | def _authenticate_otaa(self, auth_params): 81 | 82 | # create an OTAA authentication params 83 | self.dev_eui = binascii.unhexlify(auth_params[0]) 84 | self.app_eui = binascii.unhexlify(auth_params[1]) 85 | self.app_key = binascii.unhexlify(auth_params[2]) 86 | 87 | self.lora.join(activation=LoRa.OTAA, auth=(self.dev_eui, self.app_eui, self.app_key), timeout=0, dr=self.dr) 88 | 89 | while not self.lora.has_joined(): 90 | time.sleep(2.5) 91 | print('Not joined yet...') 92 | 93 | def has_joined(self): 94 | return self.lora.has_joined() 95 | 96 | def _authenticate_abp(self, auth_params): 97 | # create an ABP authentication params 98 | self.dev_addr = struct.unpack(">l", binascii.unhexlify(auth_params[0]))[0] 99 | self.nwk_swkey = binascii.unhexlify(auth_params[1]) 100 | self.app_swkey = binascii.unhexlify(auth_params[2]) 101 | 102 | self.lora.join(activation=LoRa.ABP, auth=(self.dev_addr, self.nwk_swkey, self.app_swkey)) 103 | 104 | def _create_socket(self): 105 | 106 | # create a LoRa socket 107 | self.sock = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 108 | 109 | # set the LoRaWAN data rate 110 | self.sock.setsockopt(socket.SOL_LORA, socket.SO_DR, self.dr) 111 | 112 | # make the socket non blocking 113 | self.sock.setblocking(False) 114 | 115 | time.sleep(2) 116 | 117 | def send(self, packet): 118 | with self.s_lock: 119 | self.sock.send(packet) 120 | 121 | def receive(self, bufsize): 122 | with self.q_lock: 123 | if len(self._msg_queue) > 0: 124 | return self._msg_queue.pop(0) 125 | return '' 126 | 127 | def get_dev_eui(self): 128 | return binascii.hexlify(self.lora.mac()).decode('ascii') 129 | 130 | def change_to_multicast_mode(self, mcAuth): 131 | print('Start listening for firmware updates ...........') 132 | 133 | if self.device_class != LoRa.CLASS_C: 134 | self.lora = LoRa(mode=LoRa.LORAWAN, region = self.region, device_class=LoRa.CLASS_C) 135 | self.connect() 136 | 137 | mcAddr = struct.unpack(">l", binascii.unhexlify(mcAuth[0]))[0] 138 | mcNwkKey = binascii.unhexlify(mcAuth[1]) 139 | mcAppKey = binascii.unhexlify(mcAuth[2]) 140 | 141 | self.lora.join_multicast_group(mcAddr, mcNwkKey, mcAppKey) 142 | -------------------------------------------------------------------------------- /examples/OTA-lorawan/firmware/1.17.0/flash/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from loranet import LoraNet 12 | from ota import LoraOTA 13 | from network import LoRa 14 | import machine 15 | import utime 16 | 17 | def main(): 18 | LORA_FREQUENCY = 868100000 19 | LORA_NODE_DR = 5 20 | LORA_REGION = LoRa.EU868 21 | LORA_DEVICE_CLASS = LoRa.CLASS_C 22 | LORA_ACTIVATION = LoRa.OTAA 23 | LORA_CRED = ('240ac4fffe0bf998', '948c87eff87f04508f64661220f71e3f', '5e6795a5c9abba017d05a2ffef6ba858') 24 | 25 | lora = LoraNet(LORA_FREQUENCY, LORA_NODE_DR, LORA_REGION, LORA_DEVICE_CLASS, LORA_ACTIVATION, LORA_CRED) 26 | lora.connect() 27 | 28 | ota = LoraOTA(lora) 29 | 30 | while True: 31 | rx = lora.receive(256) 32 | if rx: 33 | print('Received user message: {}'.format(rx)) 34 | 35 | utime.sleep(2) 36 | 37 | main() 38 | 39 | #try: 40 | # main() 41 | #except Exception as e: 42 | # print('Firmware exception: Reverting to old firmware') 43 | # LoraOTA.revert() 44 | -------------------------------------------------------------------------------- /examples/OTA-lorawan/firmware/1.17.0/flash/watchdog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from machine import Timer 12 | import _thread 13 | 14 | class Watchdog: 15 | 16 | def __init__(self): 17 | self.failed = False 18 | self.acknowledged = 0 19 | self._alarm = None 20 | self._lock = _thread.allocate_lock() 21 | 22 | def enable(self, timeout = 120): 23 | if self._alarm: 24 | self._alarm.cancel() 25 | self._alarm = None 26 | 27 | self._alarm = Timer.Alarm(self._check, s = timeout, periodic = True) 28 | 29 | def _check(self, alarm): 30 | with self._lock: 31 | if self.acknowledged > 0: 32 | self.failed = False 33 | self.acknowledged = 0 34 | else: 35 | self.failed = True 36 | 37 | def ack(self): 38 | with self._lock: 39 | self.acknowledged += 1 40 | 41 | def update_failed(self): 42 | with self._lock: 43 | return self.failed 44 | -------------------------------------------------------------------------------- /examples/OTA-lorawan/firmware/1.17.1/flash/OTA_INFO.py: -------------------------------------------------------------------------------- 1 | 1.17.1 2 | -------------------------------------------------------------------------------- /examples/OTA-lorawan/firmware/1.17.1/flash/loranet.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from network import LoRa 12 | import socket 13 | import binascii 14 | import struct 15 | import time 16 | import _thread 17 | 18 | class LoraNet: 19 | def __init__(self, frequency, dr, region, device_class=LoRa.CLASS_C, activation = LoRa.OTAA, auth = None): 20 | self.frequency = frequency 21 | self.dr = dr 22 | self.region = region 23 | self.device_class = device_class 24 | self.activation = activation 25 | self.auth = auth 26 | self.sock = None 27 | self._exit = False 28 | self.s_lock = _thread.allocate_lock() 29 | self.lora = LoRa(mode=LoRa.LORAWAN, region = self.region, device_class = self.device_class) 30 | 31 | self._msg_queue = [] 32 | self.q_lock = _thread.allocate_lock() 33 | self._process_ota_msg = None 34 | 35 | def stop(self): 36 | self._exit = True 37 | 38 | def init(self, process_msg_callback): 39 | self._process_ota_msg = process_msg_callback 40 | 41 | def receive_callback(self, lora): 42 | events = lora.events() 43 | if events & LoRa.RX_PACKET_EVENT: 44 | rx, port = self.sock.recvfrom(256) 45 | if rx: 46 | if '$OTA' in rx: 47 | print("OTA msg received: {}".format(rx)) 48 | self._process_ota_msg(rx.decode()) 49 | else: 50 | self.q_lock.acquire() 51 | self._msg_queue.append(rx) 52 | self.q_lock.release() 53 | 54 | def connect(self): 55 | if self.activation != LoRa.OTAA and self.activation != LoRa.ABP: 56 | raise ValueError("Invalid Lora activation method") 57 | if len(self.auth) < 3: 58 | raise ValueError("Invalid authentication parameters") 59 | 60 | self.lora.callback(trigger=LoRa.RX_PACKET_EVENT, handler=self.receive_callback) 61 | 62 | # set the 3 default channels to the same frequency 63 | self.lora.add_channel(0, frequency=self.frequency, dr_min=0, dr_max=5) 64 | self.lora.add_channel(1, frequency=self.frequency, dr_min=0, dr_max=5) 65 | self.lora.add_channel(2, frequency=self.frequency, dr_min=0, dr_max=5) 66 | 67 | # remove all the non-default channels 68 | for i in range(3, 16): 69 | self.lora.remove_channel(i) 70 | 71 | # authenticate with abp or ota 72 | if self.activation == LoRa.OTAA: 73 | self._authenticate_otaa(self.auth) 74 | else: 75 | self._authenticate_abp(self.auth) 76 | 77 | # create socket to server 78 | self._create_socket() 79 | 80 | def _authenticate_otaa(self, auth_params): 81 | 82 | # create an OTAA authentication params 83 | self.dev_eui = binascii.unhexlify(auth_params[0]) 84 | self.app_eui = binascii.unhexlify(auth_params[1]) 85 | self.app_key = binascii.unhexlify(auth_params[2]) 86 | 87 | self.lora.join(activation=LoRa.OTAA, auth=(self.dev_eui, self.app_eui, self.app_key), timeout=0, dr=self.dr) 88 | 89 | while not self.lora.has_joined(): 90 | time.sleep(2.5) 91 | print('Not joined yet...') 92 | 93 | def has_joined(self): 94 | return self.lora.has_joined() 95 | 96 | def _authenticate_abp(self, auth_params): 97 | # create an ABP authentication params 98 | self.dev_addr = struct.unpack(">l", binascii.unhexlify(auth_params[0]))[0] 99 | self.nwk_swkey = binascii.unhexlify(auth_params[1]) 100 | self.app_swkey = binascii.unhexlify(auth_params[2]) 101 | 102 | self.lora.join(activation=LoRa.ABP, auth=(self.dev_addr, self.nwk_swkey, self.app_swkey)) 103 | 104 | def _create_socket(self): 105 | 106 | # create a LoRa socket 107 | self.sock = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 108 | 109 | # set the LoRaWAN data rate 110 | self.sock.setsockopt(socket.SOL_LORA, socket.SO_DR, self.dr) 111 | 112 | # make the socket non blocking 113 | self.sock.setblocking(False) 114 | 115 | time.sleep(2) 116 | 117 | def send(self, packet): 118 | with self.s_lock: 119 | self.sock.send(packet) 120 | 121 | def receive(self, bufsize): 122 | with self.q_lock: 123 | if len(self._msg_queue) > 0: 124 | return self._msg_queue.pop(0) 125 | return '' 126 | 127 | def get_dev_eui(self): 128 | return binascii.hexlify(self.lora.mac()).decode('ascii') 129 | 130 | def change_to_multicast_mode(self, mcAuth): 131 | print('Start listening for firmware updates ...........') 132 | 133 | if self.device_class != LoRa.CLASS_C: 134 | self.lora = LoRa(mode=LoRa.LORAWAN, region = self.region, device_class=LoRa.CLASS_C) 135 | self.connect() 136 | 137 | mcAddr = struct.unpack(">l", binascii.unhexlify(mcAuth[0]))[0] 138 | mcNwkKey = binascii.unhexlify(mcAuth[1]) 139 | mcAppKey = binascii.unhexlify(mcAuth[2]) 140 | 141 | self.lora.join_multicast_group(mcAddr, mcNwkKey, mcAppKey) 142 | -------------------------------------------------------------------------------- /examples/OTA-lorawan/firmware/1.17.1/flash/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from loranet import LoraNet 12 | from ota import LoraOTA 13 | from network import LoRa 14 | import machine 15 | import utime 16 | 17 | def main(): 18 | print('Booting with firmware version 1.17.1') 19 | 20 | LORA_FREQUENCY = 868100000 21 | LORA_NODE_DR = 5 22 | LORA_REGION = LoRa.EU868 23 | LORA_DEVICE_CLASS = LoRa.CLASS_C 24 | LORA_ACTIVATION = LoRa.OTAA 25 | LORA_CRED = ('240ac4fffe0bf998', '948c87eff87f04508f64661220f71e3f', '5e6795a5c9abba017d05a2ffef6ba858') 26 | 27 | lora = LoraNet(LORA_FREQUENCY, LORA_NODE_DR, LORA_REGION, LORA_DEVICE_CLASS, LORA_ACTIVATION, LORA_CRED) 28 | lora.connect() 29 | 30 | ota = LoraOTA(lora) 31 | 32 | while True: 33 | rx = lora.receive(256) 34 | if rx: 35 | print('Received user message: {}'.format(rx)) 36 | 37 | utime.sleep(2) 38 | 39 | main() 40 | 41 | #try: 42 | # main() 43 | #except Exception as e: 44 | # print('Firmware exception: Reverting to old firmware') 45 | # LoraOTA.revert() 46 | -------------------------------------------------------------------------------- /examples/OTA-lorawan/firmware/1.17.1/flash/watchdog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from machine import Timer 12 | import _thread 13 | 14 | class Watchdog: 15 | 16 | def __init__(self): 17 | self.failed = False 18 | self.acknowledged = 0 19 | self._alarm = None 20 | self._lock = _thread.allocate_lock() 21 | 22 | def enable(self, timeout = 120): 23 | if self._alarm: 24 | self._alarm.cancel() 25 | self._alarm = None 26 | 27 | self._alarm = Timer.Alarm(self._check, s = timeout, periodic = True) 28 | 29 | def _check(self, alarm): 30 | with self._lock: 31 | if self.acknowledged > 0: 32 | self.failed = False 33 | self.acknowledged = 0 34 | else: 35 | self.failed = True 36 | 37 | def ack(self): 38 | with self._lock: 39 | self.acknowledged += 1 40 | 41 | def update_failed(self): 42 | with self._lock: 43 | return self.failed 44 | -------------------------------------------------------------------------------- /examples/OTA-lorawan/requirements.txt: -------------------------------------------------------------------------------- 1 | paho_mqtt==1.5.1 2 | -------------------------------------------------------------------------------- /examples/OTA-lorawan/updaterService.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2020, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | import paho.mqtt.client as paho 12 | from ota import OTAHandler 13 | import signal 14 | import time 15 | import config 16 | import sys 17 | 18 | exit = False 19 | client = None 20 | 21 | def sigint_handler(signum, frame): 22 | global exit 23 | exit = True 24 | print("Terminating Lora OTA updater") 25 | 26 | def on_message(mosq, ota, msg): 27 | print("{} {} {}".format(msg.topic, msg.qos, msg.payload)) 28 | ota.process_rx_msg(msg.payload.decode()) 29 | 30 | def on_publish(mosq, obj, mid): 31 | pass 32 | 33 | if __name__ == '__main__': 34 | signal.signal(signal.SIGINT, sigint_handler) 35 | 36 | ota = OTAHandler() 37 | 38 | client = paho.Client(userdata=ota) 39 | client.connect(config.LORASERVER_IP, config.LORASERVER_MQTT_PORT, 60) 40 | 41 | client.on_message = on_message 42 | client.on_publish = on_publish 43 | 44 | client.subscribe("application/+/device/+/event/up", 0) 45 | 46 | ota.set_mqtt_client(client) 47 | 48 | while client.loop() == 0 and not exit: 49 | pass 50 | 51 | ota.stop() 52 | sys.exit(0) 53 | -------------------------------------------------------------------------------- /examples/OTA/1.0.0/flash/config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | WIFI_SSID = "ENTER_ME" 12 | WIFI_PW = "ENTER_ME" 13 | SERVER_IP = "ENTER_ME" 14 | -------------------------------------------------------------------------------- /examples/OTA/1.0.0/flash/get_id.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from network import LoRa 12 | import binascii 13 | lora = LoRa(mode=LoRa.LORAWAN) 14 | print(binascii.hexlify(lora.mac()).upper().decode('utf-8')) 15 | -------------------------------------------------------------------------------- /examples/OTA/1.0.0/flash/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from network import LoRa, WLAN 12 | import socket 13 | import time 14 | from OTA import WiFiOTA 15 | from time import sleep 16 | import pycom 17 | import binascii 18 | 19 | from config import WIFI_SSID, WIFI_PW, SERVER_IP 20 | 21 | # Turn on GREEN LED 22 | pycom.heartbeat(False) 23 | pycom.rgbled(0xff00) 24 | 25 | # Setup OTA 26 | ota = WiFiOTA(WIFI_SSID, 27 | WIFI_PW, 28 | SERVER_IP, # Update server address 29 | 8000) # Update server port 30 | 31 | # Turn off WiFi to save power 32 | w = WLAN() 33 | w.deinit() 34 | 35 | # Initialize LoRa in LORAWAN mode. 36 | lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.EU868) 37 | 38 | app_eui = binascii.unhexlify('ENTER_ME') 39 | app_key = binascii.unhexlify('ENTER_ME') 40 | 41 | # join a network using OTAA (Over the Air Activation) 42 | lora.join(activation=LoRa.OTAA, auth=(app_eui, app_key), timeout=0) 43 | 44 | # wait until the module has joined the network 45 | while not lora.has_joined(): 46 | time.sleep(2.5) 47 | print('Not yet joined...') 48 | 49 | # create a LoRa socket 50 | s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 51 | 52 | # set the LoRaWAN data rate 53 | s.setsockopt(socket.SOL_LORA, socket.SO_DR, 5) 54 | 55 | # make the socket blocking 56 | # (waits for the data to be sent and for the 2 receive windows to expire) 57 | s.setblocking(True) 58 | 59 | while True: 60 | # send some data 61 | s.send(bytes([0x04, 0x05, 0x06])) 62 | 63 | # make the socket non-blocking 64 | # (because if there's no data received it will block forever...) 65 | s.setblocking(False) 66 | 67 | # get any data received (if any...) 68 | data = s.recv(64) 69 | 70 | # Some sort of OTA trigger 71 | if data == bytes([0x01, 0x02, 0x03]): 72 | print("Performing OTA") 73 | # Perform OTA 74 | ota.connect() 75 | ota.update() 76 | 77 | sleep(5) 78 | -------------------------------------------------------------------------------- /examples/OTA/1.0.1/flash/config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2018, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | WIFI_SSID = "ENTER_ME" 12 | WIFI_PW = "ENTER_ME" 13 | SERVER_IP = "ENTER_ME" 14 | -------------------------------------------------------------------------------- /examples/OTA/1.0.1/flash/get_id.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2018, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from network import LoRa 12 | import binascii 13 | lora = LoRa(mode=LoRa.LORAWAN) 14 | print(binascii.hexlify(lora.mac()).upper().decode('utf-8')) 15 | -------------------------------------------------------------------------------- /examples/OTA/1.0.1/flash/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2018, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from network import LoRa, WLAN 12 | import socket 13 | import time 14 | from OTA import WiFiOTA 15 | from time import sleep 16 | import pycom 17 | import binascii 18 | 19 | from config import WIFI_SSID, WIFI_PW, SERVER_IP 20 | 21 | # Turn on GREEN LED 22 | pycom.heartbeat(False) 23 | pycom.rgbled(0xff) 24 | 25 | # Setup OTA 26 | ota = WiFiOTA(WIFI_SSID, 27 | WIFI_PW, 28 | SERVER_IP, # Update server address 29 | 8000) # Update server port 30 | 31 | # Turn off WiFi to save power 32 | w = WLAN() 33 | w.deinit() 34 | 35 | # Initialize LoRa in LORAWAN mode. 36 | lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.EU868) 37 | 38 | app_eui = binascii.unhexlify('ENTER_ME') 39 | app_key = binascii.unhexlify('ENTER_ME') 40 | 41 | # join a network using OTAA (Over the Air Activation) 42 | lora.join(activation=LoRa.OTAA, auth=(app_eui, app_key), timeout=0) 43 | 44 | # wait until the module has joined the network 45 | while not lora.has_joined(): 46 | time.sleep(2.5) 47 | print('Not yet joined...') 48 | 49 | # create a LoRa socket 50 | s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 51 | 52 | # set the LoRaWAN data rate 53 | s.setsockopt(socket.SOL_LORA, socket.SO_DR, 5) 54 | 55 | # make the socket blocking 56 | # (waits for the data to be sent and for the 2 receive windows to expire) 57 | s.setblocking(True) 58 | 59 | while True: 60 | # send some data 61 | s.send(bytes([0x04, 0x05, 0x06])) 62 | 63 | # make the socket non-blocking 64 | # (because if there's no data received it will block forever...) 65 | s.setblocking(False) 66 | 67 | # get any data received (if any...) 68 | data = s.recv(64) 69 | 70 | # Some sort of OTA trigger 71 | if data == bytes([0x01, 0x02, 0x03]): 72 | print("Performing OTA") 73 | # Perform OTA 74 | ota.connect() 75 | ota.update() 76 | 77 | sleep(5) 78 | -------------------------------------------------------------------------------- /examples/OTA/README.md: -------------------------------------------------------------------------------- 1 | Overview 2 | -------- 3 | 4 | This directory contains a example implementation of over the air (OTA) 5 | firmware updates. This consists of two components: 6 | - A server that serves the update files and generates update "manifests" 7 | - A library that allows a Pycom module perform updates from the server 8 | 9 | This directory is laid out such that the update sever can directly run from it. 10 | For a detailed description of how the server expect the directory to be structured please read the comment at the top of `OTA_server.py`. 11 | 12 | Setup 13 | ----- 14 | To start the server simply run the `OTA_server.py` script using python 3. This 15 | will run a HTTP server on port 8000 (this can be changed in the code if 16 | necessary). 17 | 18 | In this project you will find two directories named `1.0.0` and `1.0.1`. These 19 | are both working examples of the OTA procedure, the only difference being the 20 | colour of the on-board LED so that a successful update can be demonstrated. You 21 | should upload version `1.0.0` to the module first and then via the OTA update 22 | procedure it will update to version `1.0.1`. 23 | 24 | In this example the OTA procedure is trigger via LoRaWAN downlink message. In 25 | order to use these examples you will need to enter your own `app_eui` and 26 | `app_key`. As well as this you will need to edit `config.py` to add your WiFi 27 | SSID, password and the address of the update server. Ensure you make these changes in both `1.0.0` and `1.0.1` or the code will stop working after the 28 | OTA update. 29 | 30 | The OTA library can be found in either `1.0.0\lib\OTA.py` or 31 | `1.0.1\lib\OTA.py`. 32 | 33 | For a detailed explanation of how the server works please look in 34 | `OTA_server.py`. The comment at the top of the file explains how it works in 35 | detail. 36 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | This folder contains several samples that demonstrate various Pycom board functions. The samples are currently provided with the boot.py file to copy directly on the board. 3 | 4 | ## Installation 5 | 6 | This section explains the individual steps to run example application using the Pymakr. 7 | Steps: 8 | - Open Pymakr 9 | - In the menu, go to Project > New 10 | - Fill project name, select example folder for Project directory and select project type "Python project" 11 | - On popup add existing files to the project. 12 | -------------------------------------------------------------------------------- /examples/accelerometer_wake/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from pytrack import Pytrack 12 | #from pysense import Pysense 13 | from LIS2HH12 import LIS2HH12 14 | import pycom 15 | import time 16 | 17 | pycom.heartbeat(False) 18 | 19 | py = Pytrack() 20 | # py = Pysense() 21 | 22 | # display the reset reason code and the sleep remaining in seconds 23 | # possible values of wakeup reason are: 24 | # WAKE_REASON_ACCELEROMETER = 1 25 | # WAKE_REASON_PUSH_BUTTON = 2 26 | # WAKE_REASON_TIMER = 4 27 | # WAKE_REASON_INT_PIN = 8 28 | print("Wakeup reason: " + str(py.get_wake_reason()) + "; Aproximate sleep remaining: " + str(py.get_sleep_remaining()) + " sec") 29 | time.sleep(0.5) 30 | 31 | # enable wakeup source from INT pin 32 | py.setup_int_pin_wake_up(False) 33 | 34 | # enable activity and also inactivity interrupts, using the default callback handler 35 | py.setup_int_wake_up(True, True) 36 | 37 | acc = LIS2HH12() 38 | # enable the activity/inactivity interrupts 39 | # set the accelereation threshold to 2000mG (2G) and the min duration to 200ms 40 | acc.enable_activity_interrupt(2000, 200) 41 | 42 | # check if we were awaken due to activity 43 | if acc.activity(): 44 | pycom.rgbled(0xFF0000) 45 | else: 46 | pycom.rgbled(0x00FF00) # timer wake-up 47 | time.sleep(0.1) 48 | 49 | # go to sleep for 5 minutes maximum if no accelerometer interrupt happens 50 | py.setup_sleep(300) 51 | py.go_to_sleep() 52 | -------------------------------------------------------------------------------- /examples/adc/boot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from machine import UART 12 | import machine 13 | import os 14 | 15 | uart = UART(0, baudrate=115200) 16 | os.dupterm(uart) 17 | 18 | machine.main('main.py') 19 | -------------------------------------------------------------------------------- /examples/adc/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from machine import ADC 12 | import time 13 | 14 | adc = ADC(0) 15 | adc_c = adc.channel(pin='P13') 16 | 17 | while True: 18 | value = adc_c.value() 19 | print("ADC value:" + str(value)) 20 | time.sleep(1) 21 | -------------------------------------------------------------------------------- /examples/bluetooth/boot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from machine import UART 12 | import machine 13 | import os 14 | 15 | uart = UART(0, baudrate=115200) 16 | os.dupterm(uart) 17 | 18 | machine.main('main.py') 19 | -------------------------------------------------------------------------------- /examples/bluetooth/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from network import Bluetooth 12 | import binascii 13 | import time 14 | bt = Bluetooth() 15 | bt.start_scan(-1) 16 | 17 | while True: 18 | adv = bt.get_adv() 19 | if adv: 20 | # try to get the complete name 21 | print(bt.resolve_adv_data(adv.data, Bluetooth.ADV_NAME_CMPL)) 22 | 23 | # try to get the manufacturer data (Apple's iBeacon data is sent here) 24 | mfg_data = bt.resolve_adv_data(adv.data, Bluetooth.ADV_MANUFACTURER_DATA) 25 | 26 | if mfg_data: 27 | # try to get the manufacturer data (Apple's iBeacon data is sent here) 28 | print(binascii.hexlify(mfg_data)) 29 | 30 | if bt.resolve_adv_data(adv.data, Bluetooth.ADV_NAME_CMPL) == 'Heart Rate': 31 | conn = bt.connect(adv.mac) 32 | services = conn.services() 33 | for service in services: 34 | time.sleep(0.050) 35 | if type(service.uuid()) == bytes: 36 | print('Reading chars from service = {}'.format(service.uuid())) 37 | else: 38 | print('Reading chars from service = %x' % service.uuid()) 39 | chars = service.characteristics() 40 | for char in chars: 41 | if (char.properties() & Bluetooth.PROP_READ): 42 | print('char {} value = {}'.format(char.uuid(), char.read())) 43 | conn.disconnect() 44 | break 45 | else: 46 | time.sleep(0.050) 47 | -------------------------------------------------------------------------------- /examples/deepsleep/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from deepsleep import DeepSleep 12 | import deepsleep 13 | 14 | ds = DeepSleep() 15 | 16 | # get the wake reason and the value of the pins during wake up 17 | wake_s = ds.get_wake_status() 18 | print(wake_s) 19 | 20 | if wake_s['wake'] == deepsleep.PIN_WAKE: 21 | print("Pin wake up") 22 | elif wake_s['wake'] == deepsleep.TIMER_WAKE: 23 | print("Timer wake up") 24 | else: # deepsleep.POWER_ON_WAKE: 25 | print("Power ON reset") 26 | 27 | ds.enable_pullups('P17') # can also do ds.enable_pullups(['P17', 'P18']) 28 | ds.enable_wake_on_fall('P17') # can also do ds.enable_wake_on_fall(['P17', 'P18']) 29 | 30 | ds.go_to_sleep(60) # go to sleep for 60 seconds 31 | -------------------------------------------------------------------------------- /examples/https/boot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from machine import UART 12 | import machine 13 | import os 14 | 15 | uart = UART(0, baudrate=115200) 16 | os.dupterm(uart) 17 | 18 | machine.main('main.py') 19 | -------------------------------------------------------------------------------- /examples/https/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | import socket 12 | import ssl 13 | 14 | # Connect without a certificate 15 | s = socket() 16 | ss = ssl.wrap_socket(s) 17 | ss.connect(socket.getaddrinfo('www.google.com', 443)[0][-1]) 18 | 19 | # Connect with a certificate 20 | s = socket.socket() 21 | ss = ssl.wrap_socket(s, cert_reqs=ssl.CERT_REQUIRED, ca_certs='/flash/cert/ca.pem') 22 | ss.connect(socket.getaddrinfo('cloud.blynk.cc', 8441)[0][-1]) -------------------------------------------------------------------------------- /examples/i2c/bh1750fvi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | # Simple driver for the BH1750FVI digital light sensor 12 | 13 | class BH1750FVI: 14 | MEASUREMENT_TIME = const(120) 15 | 16 | def __init__(self, i2c, addr=0x23, period=150): 17 | self.i2c = i2c 18 | self.period = period 19 | self.addr = addr 20 | self.time = 0 21 | self.value = 0 22 | self.i2c.writeto(addr, bytes([0x10])) # start continuos 1 Lux readings every 120ms 23 | 24 | def read(self): 25 | self.time += self.period 26 | if self.time >= MEASUREMENT_TIME: 27 | self.time = 0 28 | data = self.i2c.readfrom(self.addr, 2) 29 | self.value = (((data[0] << 8) + data[1]) * 1200) // 1000 30 | return self.value -------------------------------------------------------------------------------- /examples/i2c/boot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from machine import UART 12 | import machine 13 | import os 14 | 15 | uart = UART(0, baudrate=115200) 16 | os.dupterm(uart) 17 | 18 | machine.main('main.py') 19 | -------------------------------------------------------------------------------- /examples/i2c/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | import socket 12 | import time 13 | import pycom 14 | import struct 15 | from network import LoRa 16 | from machine import I2C 17 | import bh1750fvi 18 | 19 | LORA_PKG_FORMAT = "!BH" 20 | LORA_CONFIRM_FORMAT = "!BB" 21 | 22 | DEVICE_ID = 1 23 | 24 | pycom.heartbeat(False) 25 | 26 | lora = LoRa(mode=LoRa.LORA, tx_iq=True, frequency = 863000000) 27 | lora_sock = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 28 | lora_sock.setblocking(False) 29 | 30 | i2c = I2C(0, I2C.MASTER, baudrate=100000) 31 | light_sensor = bh1750fvi.BH1750FVI(i2c, addr=i2c.scan()[0]) 32 | 33 | while(True): 34 | msg = struct.pack(LORA_PKG_FORMAT, DEVICE_ID, light_sensor.read()) 35 | lora_sock.send(msg) 36 | 37 | pycom.rgbled(0x150000) 38 | 39 | wait = 5 40 | while (wait > 0): 41 | wait = wait - 0.1 42 | time.sleep(0.1) 43 | recv_data = lora_sock.recv(64) 44 | 45 | if (len (recv_data) >= 2): 46 | status, device_id = struct.unpack(LORA_CONFIRM_FORMAT, recv_data) 47 | 48 | if (device_id == DEVICE_ID and status == 200): 49 | pycom.rgbled(0x001500) 50 | wait = 0 51 | 52 | time.sleep(1) -------------------------------------------------------------------------------- /examples/lopy-lopy/lopy-A/boot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from machine import UART 12 | import machine 13 | import os 14 | 15 | uart = UART(0, baudrate=115200) 16 | os.dupterm(uart) 17 | 18 | machine.main('main.py') 19 | -------------------------------------------------------------------------------- /examples/lopy-lopy/lopy-A/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from network import LoRa 12 | import socket 13 | import time 14 | 15 | lora = LoRa(mode=LoRa.LORA, frequency=863000000) 16 | s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 17 | s.setblocking(False) 18 | 19 | while True: 20 | if s.recv(64) == b'Ping': 21 | s.send('Pong') 22 | time.sleep(5) 23 | -------------------------------------------------------------------------------- /examples/lopy-lopy/lopy-B/boot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from machine import UART 12 | import machine 13 | import os 14 | 15 | uart = UART(0, baudrate=115200) 16 | os.dupterm(uart) 17 | 18 | machine.main('main.py') 19 | -------------------------------------------------------------------------------- /examples/lopy-lopy/lopy-B/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from network import LoRa 12 | import socket 13 | import time 14 | 15 | lora = LoRa(mode=LoRa.LORA, frequency=863000000) 16 | s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 17 | s.setblocking(False) 18 | 19 | while True: 20 | s.send('Ping') 21 | time.sleep(5) 22 | -------------------------------------------------------------------------------- /examples/loraNanoGateway/gateway/boot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from machine import UART 12 | import machine 13 | import os 14 | 15 | uart = UART(0, baudrate=115200) 16 | os.dupterm(uart) 17 | 18 | machine.main('main.py') 19 | -------------------------------------------------------------------------------- /examples/loraNanoGateway/gateway/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | import socket 12 | import struct 13 | from network import LoRa 14 | 15 | # A basic package header, B: 1 byte for the deviceId, B: 1 byte for the pkg size, %ds: Formated string for string 16 | _LORA_PKG_FORMAT = "!BB%ds" 17 | # A basic ack package, B: 1 byte for the deviceId, B: 1 bytes for the pkg size, B: 1 byte for the Ok (200) or error messages 18 | _LORA_PKG_ACK_FORMAT = "BBB" 19 | 20 | # Open a LoRa Socket, use rx_iq to avoid listening to our own messages 21 | # Please pick the region that matches where you are using the device: 22 | # Asia = LoRa.AS923 23 | # Australia = LoRa.AU915 24 | # Europe = LoRa.EU868 25 | # United States = LoRa.US915 26 | lora = LoRa(mode=LoRa.LORA, rx_iq=True, region=LoRa.EU868) 27 | lora_sock = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 28 | lora_sock.setblocking(False) 29 | 30 | while (True): 31 | recv_pkg = lora_sock.recv(512) 32 | if (len(recv_pkg) > 2): 33 | recv_pkg_len = recv_pkg[1] 34 | 35 | device_id, pkg_len, msg = struct.unpack(_LORA_PKG_FORMAT % recv_pkg_len, recv_pkg) 36 | 37 | # If the uart = machine.UART(0, 115200) and os.dupterm(uart) are set in the boot.py this print should appear in the serial port 38 | print('Device: %d - Pkg: %s' % (device_id, msg)) 39 | 40 | ack_pkg = struct.pack(_LORA_PKG_ACK_FORMAT, device_id, 1, 200) 41 | lora_sock.send(ack_pkg) 42 | -------------------------------------------------------------------------------- /examples/loraNanoGateway/node/boot.py: -------------------------------------------------------------------------------- 1 | from machine import UART 2 | import machine 3 | import os 4 | 5 | uart = UART(0, baudrate=115200) 6 | os.dupterm(uart) 7 | 8 | machine.main('main.py') 9 | -------------------------------------------------------------------------------- /examples/loraNanoGateway/node/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import socket 3 | import time 4 | import struct 5 | from network import LoRa 6 | 7 | # A basic package header, B: 1 byte for the deviceId, B: 1 bytes for the pkg size 8 | _LORA_PKG_FORMAT = "BB%ds" 9 | _LORA_PKG_ACK_FORMAT = "BBB" 10 | DEVICE_ID = 0x01 11 | 12 | 13 | # Open a Lora Socket, use tx_iq to avoid listening to our own messages 14 | # Please pick the region that matches where you are using the device: 15 | # Asia = LoRa.AS923 16 | # Australia = LoRa.AU915 17 | # Europe = LoRa.EU868 18 | # United States = LoRa.US915 19 | lora = LoRa(mode=LoRa.LORA, tx_iq=True, region=LoRa.EU868) 20 | lora_sock = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 21 | lora_sock.setblocking(False) 22 | 23 | while(True): 24 | # Package send containing a simple string 25 | msg = "Device 1 Here" 26 | pkg = struct.pack(_LORA_PKG_FORMAT % len(msg), DEVICE_ID, len(msg), msg) 27 | lora_sock.send(pkg) 28 | 29 | # Wait for the response from the gateway. NOTE: For this demo the device does an infinite loop for while waiting the response. Introduce a max_time_waiting for you application 30 | waiting_ack = True 31 | while(waiting_ack): 32 | recv_ack = lora_sock.recv(256) 33 | 34 | if (len(recv_ack) > 0): 35 | device_id, pkg_len, ack = struct.unpack(_LORA_PKG_ACK_FORMAT, recv_ack) 36 | if (device_id == DEVICE_ID): 37 | if (ack == 200): 38 | waiting_ack = False 39 | # If the uart = machine.UART(0, 115200) and os.dupterm(uart) are set in the boot.py this print should appear in the serial port 40 | print("ACK") 41 | else: 42 | waiting_ack = False 43 | # If the uart = machine.UART(0, 115200) and os.dupterm(uart) are set in the boot.py this print should appear in the serial port 44 | print("Message Failed") 45 | 46 | time.sleep(5) 47 | 48 | -------------------------------------------------------------------------------- /examples/loraabp/boot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from machine import UART 12 | import machine 13 | import os 14 | 15 | uart = UART(0, baudrate=115200) 16 | os.dupterm(uart) 17 | 18 | machine.main('main.py') 19 | -------------------------------------------------------------------------------- /examples/loraabp/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from network import LoRa 12 | import socket 13 | import binascii 14 | import struct 15 | 16 | # Initialize LoRa in LORAWAN mode. 17 | # Please pick the region that matches where you are using the device: 18 | # Asia = LoRa.AS923 19 | # Australia = LoRa.AU915 20 | # Europe = LoRa.EU868 21 | # United States = LoRa.US915 22 | lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.EU868) 23 | 24 | # create an ABP authentication params 25 | dev_addr = struct.unpack(">l", binascii.unhexlify('00000005'))[0] 26 | nwk_swkey = binascii.unhexlify('2B7E151628AED2A6ABF7158809CF4F3C') 27 | app_swkey = binascii.unhexlify('2B7E151628AED2A6ABF7158809CF4F3C') 28 | 29 | # join a network using ABP (Activation By Personalization) 30 | lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey)) 31 | 32 | # create a LoRa socket 33 | s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 34 | 35 | # set the LoRaWAN data rate 36 | s.setsockopt(socket.SOL_LORA, socket.SO_DR, 5) 37 | 38 | # make the socket non-blocking 39 | s.setblocking(False) 40 | 41 | # send some data 42 | s.send(bytes([0x01, 0x02, 0x03])) 43 | 44 | # get any data received... 45 | data = s.recv(64) 46 | print(data) 47 | -------------------------------------------------------------------------------- /examples/loramac/boot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from machine import UART 12 | import machine 13 | import os 14 | 15 | uart = UART(0, baudrate=115200) 16 | os.dupterm(uart) 17 | 18 | machine.main('main.py') 19 | -------------------------------------------------------------------------------- /examples/loramac/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from network import LoRa 12 | import socket 13 | 14 | # Initialize LoRa in LORA mode. 15 | 16 | # More params can be given, like frequency, tx power and spreading factor. 17 | # Please pick the region that matches where you are using the device: 18 | # Asia = LoRa.AS923 19 | # Australia = LoRa.AU915 20 | # Europe = LoRa.EU868 21 | # United States = LoRa.US915 22 | lora = LoRa(mode=LoRa.LORA, region=LoRa.EU868) 23 | 24 | # create a raw LoRa socket 25 | s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 26 | s.setblocking(False) 27 | 28 | # send some data 29 | s.send(bytes([0x01, 0x02, 0x03]) 30 | 31 | # get any data received... 32 | data = s.recv(64) 33 | print(data) 34 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/abp_node.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from network import LoRa 12 | import socket 13 | import binascii 14 | import struct 15 | import time 16 | import config 17 | 18 | # initialize LoRa in LORAWAN mode. 19 | # Please pick the region that matches where you are using the device: 20 | # Asia = LoRa.AS923 21 | # Australia = LoRa.AU915 22 | # Europe = LoRa.EU868 23 | # United States = LoRa.US915 24 | lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.EU868) 25 | 26 | # create an ABP authentication params 27 | dev_addr = struct.unpack(">l", binascii.unhexlify('2601147D'))[0] 28 | nwk_swkey = binascii.unhexlify('3C74F4F40CAEA021303BC24284FCF3AF') 29 | app_swkey = binascii.unhexlify('0FFA7072CC6FF69A102A0F39BEB0880F') 30 | 31 | # remove all the non-default channels 32 | for i in range(3, 16): 33 | lora.remove_channel(i) 34 | 35 | # set the 3 default channels to the same frequency 36 | lora.add_channel(0, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=5) 37 | lora.add_channel(1, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=5) 38 | lora.add_channel(2, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=5) 39 | 40 | # join a network using ABP (Activation By Personalization) 41 | lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey)) 42 | 43 | # create a LoRa socket 44 | s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 45 | 46 | # set the LoRaWAN data rate 47 | s.setsockopt(socket.SOL_LORA, socket.SO_DR, config.LORA_NODE_DR) 48 | 49 | # make the socket non-blocking 50 | s.setblocking(False) 51 | 52 | for i in range (200): 53 | pkt = b'PKT #' + bytes([i]) 54 | print('Sending:', pkt) 55 | s.send(pkt) 56 | time.sleep(4) 57 | rx, port = s.recvfrom(256) 58 | if rx: 59 | print('Received: {}, on port: {}'.format(rx, port)) 60 | time.sleep(6) 61 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/abp_node_US915.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from network import LoRa 12 | import socket 13 | import binascii 14 | import struct 15 | import time 16 | import config 17 | 18 | # initialize LoRa in LORAWAN mode. 19 | # Please pick the region that matches where you are using the device: 20 | # Asia = LoRa.AS923 21 | # Australia = LoRa.AU915 22 | # Europe = LoRa.EU868 23 | # United States = LoRa.US915 24 | lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.US915) 25 | 26 | # create an ABP authentication params 27 | dev_addr = struct.unpack(">l", binascii.unhexlify('2601147D'))[0] 28 | nwk_swkey = binascii.unhexlify('3C74F4F40CAEA021303BC24284FCF3AF') 29 | app_swkey = binascii.unhexlify('0FFA7072CC6FF69A102A0F39BEB0880F') 30 | 31 | # remove all the channels 32 | for channel in range(0, 72): 33 | lora.remove_channel(channel) 34 | 35 | # set all channels to the same frequency (must be before sending the OTAA join request) 36 | for channel in range(0, 72): 37 | lora.add_channel(channel, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=3) 38 | 39 | # join a network using ABP (Activation By Personalization) 40 | lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey)) 41 | 42 | # create a LoRa socket 43 | s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 44 | 45 | # set the LoRaWAN data rate 46 | s.setsockopt(socket.SOL_LORA, socket.SO_DR, config.LORA_NODE_DR) 47 | 48 | # make the socket non-blocking 49 | s.setblocking(False) 50 | 51 | for i in range (200): 52 | pkt = b'PKT #' + bytes([i]) 53 | print('Sending:', pkt) 54 | s.send(pkt) 55 | time.sleep(4) 56 | rx, port = s.recvfrom(256) 57 | if rx: 58 | print('Received: {}, on port: {}'.format(rx, port)) 59 | time.sleep(6) 60 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | """ LoPy LoRaWAN Nano Gateway configuration options """ 12 | 13 | import machine 14 | import ubinascii 15 | 16 | WIFI_MAC = ubinascii.hexlify(machine.unique_id()).upper() 17 | # Set the Gateway ID to be the first 3 bytes of MAC address + 'FFFE' + last 3 bytes of MAC address 18 | GATEWAY_ID = WIFI_MAC[:6] + "FFFE" + WIFI_MAC[6:12] 19 | 20 | SERVER = 'router.eu.thethings.network' 21 | PORT = 1700 22 | 23 | NTP = "pool.ntp.org" 24 | NTP_PERIOD_S = 3600 25 | 26 | WIFI_SSID = 'my-wifi' 27 | WIFI_PASS = 'my-wifi-password' 28 | 29 | # for EU868 30 | LORA_FREQUENCY = 868100000 31 | LORA_GW_DR = "SF7BW125" # DR_5 32 | LORA_NODE_DR = 5 33 | 34 | # for US915 35 | # LORA_FREQUENCY = 903900000 36 | # LORA_GW_DR = "SF10BW125" # DR_0 37 | # LORA_NODE_DR = 0 38 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | """ LoPy LoRaWAN Nano Gateway example usage """ 12 | 13 | import config 14 | from nanogateway import NanoGateway 15 | 16 | if __name__ == '__main__': 17 | nanogw = NanoGateway( 18 | id=config.GATEWAY_ID, 19 | frequency=config.LORA_FREQUENCY, 20 | datarate=config.LORA_GW_DR, 21 | ssid=config.WIFI_SSID, 22 | password=config.WIFI_PASS, 23 | server=config.SERVER, 24 | port=config.PORT, 25 | ntp_server=config.NTP, 26 | ntp_period=config.NTP_PERIOD_S 27 | ) 28 | 29 | nanogw.start() 30 | nanogw._log('You may now press ENTER to enter the REPL') 31 | input() 32 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/otaa_node.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | """ OTAA Node example compatible with the LoPy Nano Gateway """ 12 | 13 | from network import LoRa 14 | import socket 15 | import binascii 16 | import struct 17 | import time 18 | import config 19 | 20 | # initialize LoRa in LORAWAN mode. 21 | # Please pick the region that matches where you are using the device: 22 | # Asia = LoRa.AS923 23 | # Australia = LoRa.AU915 24 | # Europe = LoRa.EU868 25 | # United States = LoRa.US915 26 | lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.EU868) 27 | 28 | # create an OTA authentication params 29 | dev_eui = binascii.unhexlify('AABBCCDDEEFF7778') 30 | app_eui = binascii.unhexlify('70B3D57EF0003BFD') 31 | app_key = binascii.unhexlify('36AB7625FE770B6881683B495300FFD6') 32 | 33 | # set the 3 default channels to the same frequency (must be before sending the OTAA join request) 34 | lora.add_channel(0, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=5) 35 | lora.add_channel(1, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=5) 36 | lora.add_channel(2, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=5) 37 | 38 | # join a network using OTAA 39 | lora.join(activation=LoRa.OTAA, auth=(dev_eui, app_eui, app_key), timeout=0, dr=config.LORA_NODE_DR) 40 | 41 | # wait until the module has joined the network 42 | while not lora.has_joined(): 43 | time.sleep(2.5) 44 | print('Not joined yet...') 45 | 46 | # remove all the non-default channels 47 | for i in range(3, 16): 48 | lora.remove_channel(i) 49 | 50 | # create a LoRa socket 51 | s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 52 | 53 | # set the LoRaWAN data rate 54 | s.setsockopt(socket.SOL_LORA, socket.SO_DR, config.LORA_NODE_DR) 55 | 56 | # make the socket non-blocking 57 | s.setblocking(False) 58 | 59 | time.sleep(5.0) 60 | 61 | for i in range (200): 62 | pkt = b'PKT #' + bytes([i]) 63 | print('Sending:', pkt) 64 | s.send(pkt) 65 | time.sleep(4) 66 | rx, port = s.recvfrom(256) 67 | if rx: 68 | print('Received: {}, on port: {}'.format(rx, port)) 69 | time.sleep(6) 70 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/otaa_node_US915.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | """ OTAA Node example compatible with the LoPy Nano Gateway """ 12 | 13 | from network import LoRa 14 | import socket 15 | import binascii 16 | import struct 17 | import time 18 | import config 19 | 20 | # initialize LoRa in LORAWAN mode. 21 | # Please pick the region that matches where you are using the device: 22 | # Asia = LoRa.AS923 23 | # Australia = LoRa.AU915 24 | # Europe = LoRa.EU868 25 | # United States = LoRa.US915 26 | lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.US915) 27 | 28 | # create an OTA authentication params 29 | dev_eui = binascii.unhexlify('AABBCCDDEEFF7778') 30 | app_eui = binascii.unhexlify('70B3D57EF0003BFD') 31 | app_key = binascii.unhexlify('36AB7625FE770B6881683B495300FFD6') 32 | 33 | # remove all the channels 34 | for channel in range(0, 72): 35 | lora.remove_channel(channel) 36 | 37 | # set all channels to the same frequency (must be before sending the OTAA join request) 38 | for channel in range(0, 72): 39 | lora.add_channel(channel, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=3) 40 | 41 | # join a network using OTAA 42 | lora.join(activation=LoRa.OTAA, auth=(dev_eui, app_eui, app_key), timeout=0, dr=config.LORA_NODE_DR) 43 | 44 | # wait until the module has joined the network 45 | join_wait = 0 46 | while True: 47 | time.sleep(2.5) 48 | if not lora.has_joined(): 49 | print('Not joined yet...') 50 | join_wait += 1 51 | if join_wait == 5: 52 | lora.join(activation=LoRa.OTAA, auth=(dev_eui, app_eui, app_key), timeout=0, dr=config.LORA_NODE_DR) 53 | join_wait = 0 54 | else: 55 | break 56 | 57 | # create a LoRa socket 58 | s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 59 | 60 | # set the LoRaWAN data rate 61 | s.setsockopt(socket.SOL_LORA, socket.SO_DR, config.LORA_NODE_DR) 62 | 63 | # make the socket non-blocking 64 | s.setblocking(False) 65 | 66 | time.sleep(5.0) 67 | 68 | for i in range (200): 69 | pkt = b'PKT #' + bytes([i]) 70 | print('Sending:', pkt) 71 | s.send(pkt) 72 | time.sleep(4) 73 | rx, port = s.recvfrom(256) 74 | if rx: 75 | print('Received: {}, on port: {}'.format(rx, port)) 76 | time.sleep(6) 77 | -------------------------------------------------------------------------------- /examples/lorawan-regional-examples/main_AS923.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | """ 12 | OTAA Node example as per LoRaWAN AS923 regional specification 13 | - compatible with the LoPy Nano Gateway and all other LoraWAN gateways 14 | - tested works with a LoRaServer, shall works on TTN servers 15 | """ 16 | 17 | from network import LoRa 18 | import socket 19 | import binascii 20 | import struct 21 | import time 22 | 23 | LORA_CHANNEL = 1 24 | LORA_NODE_DR = 4 25 | 26 | ''' 27 | utility function to setup the lora channels 28 | ''' 29 | def prepare_channels(lora, channel, data_rate): 30 | 31 | AS923_FREQUENCIES = [ 32 | { "chan": 1, "fq": "923200000" }, 33 | { "chan": 2, "fq": "923400000" }, 34 | { "chan": 3, "fq": "922200000" }, 35 | { "chan": 4, "fq": "922400000" }, 36 | { "chan": 5, "fq": "922600000" }, 37 | { "chan": 6, "fq": "922800000" }, 38 | { "chan": 7, "fq": "923000000" }, 39 | { "chan": 8, "fq": "922000000" }, 40 | ] 41 | 42 | if not channel in range(0, 9): 43 | raise RuntimeError("channels should be in 1-8 for AS923") 44 | 45 | if channel == 0: 46 | import uos 47 | channel = (struct.unpack('B',uos.urandom(1))[0] % 7) + 1 48 | 49 | for i in range(0, 8): 50 | lora.remove_channel(i) 51 | 52 | upstream = (item for item in AS923_FREQUENCIES if item["chan"] == channel).__next__() 53 | 54 | # set default channels frequency 55 | lora.add_channel(int(upstream.get('chan')), frequency=int(upstream.get('fq')), dr_min=0, dr_max=data_rate) 56 | 57 | return lora 58 | 59 | ''' 60 | call back for handling RX packets 61 | ''' 62 | def lora_cb(lora): 63 | events = lora.events() 64 | if events & LoRa.RX_PACKET_EVENT: 65 | if lora_socket is not None: 66 | frame, port = lora_socket.recvfrom(512) # longuest frame is +-220 67 | print(port, frame) 68 | if events & LoRa.TX_PACKET_EVENT: 69 | print("tx_time_on_air: {} ms @dr {}", lora.stats().tx_time_on_air, lora.stats().sftx) 70 | 71 | ''' 72 | Main operations: this is sample code for LoRaWAN on AS923 73 | ''' 74 | 75 | lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.AS923, device_class=LoRa.CLASS_C, adr=False, tx_power=20) 76 | 77 | # create an OTA authentication params 78 | dev_eui = binascii.unhexlify('0000000000000000') 79 | app_key = binascii.unhexlify('a926e5bb85271f2d') # not used leave empty loraserver.io 80 | nwk_key = binascii.unhexlify('a926e5bb85271f2da0440f2f4200afe3') 81 | 82 | # join a network using OTAA 83 | lora.join(activation=LoRa.OTAA, auth=(dev_eui, app_key, nwk_key), timeout=0, dr=2) # AS923 always joins at DR2 84 | 85 | prepare_channels(lora, LORA_CHANNEL, LORA_NODE_DR) 86 | 87 | # wait until the module has joined the network 88 | print('Over the air network activation ... ', end='') 89 | while not lora.has_joined(): 90 | time.sleep(2.5) 91 | print('.', end='') 92 | print('') 93 | 94 | # create a LoRa socket 95 | lora_socket = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 96 | 97 | # set the LoRaWAN data rate 98 | lora_socket.setsockopt(socket.SOL_LORA, socket.SO_DR, LORA_NODE_DR) 99 | 100 | # msg are confirmed at the FMS level 101 | lora_socket.setsockopt(socket.SOL_LORA, socket.SO_CONFIRMED, 0) 102 | 103 | # make the socket non blocking y default 104 | lora_socket.setblocking(False) 105 | 106 | lora.callback(trigger=( LoRa.RX_PACKET_EVENT | 107 | LoRa.TX_PACKET_EVENT | 108 | LoRa.TX_FAILED_EVENT ), handler=lora_cb) 109 | 110 | time.sleep(4) # this timer is important and caused me some trouble ... 111 | 112 | for i in range(0, 1000): 113 | pkt = struct.pack('>H', i) 114 | print('Sending:', pkt) 115 | lora_socket.send(pkt) 116 | time.sleep(300) 117 | -------------------------------------------------------------------------------- /examples/lorawan-regional-examples/main_AU915.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | """ 12 | OTAA Node example as per LoRaWAN AU915 regional specification 13 | - tested works with a LoRaServer, shall works on TTN servers 14 | - This example uses 8 channels so you will need an 8 channel GW 15 | (not a 1 channel GW like the NanoGateway) 16 | """ 17 | 18 | from network import LoRa 19 | import socket 20 | import binascii 21 | import struct 22 | import time 23 | 24 | LORA_FREQUENCY = 915200000 # start of the 1st subband 25 | LORA_NODE_DR = 4 26 | ''' 27 | utility function to setup the lora channels 28 | ''' 29 | def prepare_channels(lora, channel, data_rate): 30 | 31 | AU915_FREQUENCIES = [ 32 | { "chan": 64, "fq": "915200000" } 33 | ] 34 | if not channel in range(64,65): 35 | raise RuntimeError("only channel 64 is implemented in this example)") 36 | upstream = (item for item in AU915_FREQUENCIES if item["chan"] == channel).__next__() 37 | 38 | lora.add_channel(int(upstream.get('chan')), frequency=int(upstream.get('fq')), dr_min=0, dr_max=int(data_rate)) 39 | print("*** Adding channel up %s %s" % (upstream.get('chan'), upstream.get('fq'))) 40 | 41 | for index in range(0, 71): 42 | if index != upstream.get('chan'): 43 | lora.remove_channel(index) 44 | 45 | return lora 46 | 47 | ''' 48 | call back for handling RX packets 49 | ''' 50 | def lora_cb(lora): 51 | events = lora.events() 52 | if events & LoRa.RX_PACKET_EVENT: 53 | if lora_socket is not None: 54 | frame, port = lora_socket.recvfrom(512) # longuest frame is +-220 55 | print(port, frame) 56 | if events & LoRa.TX_PACKET_EVENT: 57 | print("tx_time_on_air: {} ms @dr {}", lora.stats().tx_time_on_air, lora.stats().sftx) 58 | 59 | 60 | ''' 61 | Main operations: this is sample code for LoRaWAN on AU915 62 | ''' 63 | 64 | lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.AU915, device_class=LoRa.CLASS_C) 65 | 66 | # create an OTA authentication params 67 | dev_eui = binascii.unhexlify('0000000000000000') 68 | app_key = binascii.unhexlify('a926e5bb85271f2d') # not used leave empty loraserver.io 69 | nwk_key = binascii.unhexlify('a926e5bb85271f2da0440f2f4200afe3') 70 | 71 | prepare_channels(lora, 64, 5) 72 | 73 | # join a network using OTAA 74 | lora.join(activation=LoRa.OTAA, auth=(dev_eui, app_key, nwk_key), timeout=0, dr=0) # DR is 2 in v1.1rb but 0 worked for ne 75 | 76 | # wait until the module has joined the network 77 | print('Over the air network activation ... ', end='') 78 | while not lora.has_joined(): 79 | time.sleep(2.5) 80 | print('.', end='') 81 | print('') 82 | 83 | for i in range(0, 8): 84 | fq = LORA_FREQUENCY + (i * 200000) 85 | lora.add_channel(i, frequency=fq, dr_min=0, dr_max=LORA_NODE_DR) 86 | print("AU915 Adding channel up %s %s" % (i, fq)) 87 | 88 | 89 | # create a LoRa socket 90 | lora_socket = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 91 | 92 | # set the LoRaWAN data rate 93 | lora_socket.setsockopt(socket.SOL_LORA, socket.SO_DR, LORA_NODE_DR) 94 | 95 | # msg are confirmed at the FMS level 96 | lora_socket.setsockopt(socket.SOL_LORA, socket.SO_CONFIRMED, 0) 97 | 98 | # make the socket non blocking y default 99 | lora_socket.setblocking(False) 100 | 101 | lora.callback(trigger=( LoRa.RX_PACKET_EVENT | 102 | LoRa.TX_PACKET_EVENT | 103 | LoRa.TX_FAILED_EVENT ), handler=lora_cb) 104 | 105 | time.sleep(4) # this timer is important and caused me some trouble ... 106 | 107 | for i in range(0, 1000): 108 | pkt = struct.pack('>H', i) 109 | print('Sending:', pkt) 110 | lora_socket.send(pkt) 111 | time.sleep(300) 112 | -------------------------------------------------------------------------------- /examples/lorawan-regional-examples/main_EU868.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | """ 12 | OTAA Node example as per LoRaWAN EU868 regional specification 13 | - compatible with the LoPy Nano Gateway and all other LoraWAN gateways 14 | - tested works with a LoRaServer and TTN servers 15 | """ 16 | 17 | from network import LoRa 18 | import socket 19 | import binascii 20 | import struct 21 | import time 22 | 23 | LORA_CHANNEL = 0 # zero = random 24 | LORA_NODE_DR = 4 25 | ''' 26 | utility function to setup the lora channels 27 | ''' 28 | def prepare_channels(lora, channel, data_rate): 29 | EU868_FREQUENCIES = [ 30 | { "chan": 1, "fq": "868100000" }, 31 | { "chan": 2, "fq": "868300000" }, 32 | { "chan": 3, "fq": "868500000" }, 33 | { "chan": 4, "fq": "867100000" }, 34 | { "chan": 5, "fq": "867300000" }, 35 | { "chan": 6, "fq": "867500000" }, 36 | { "chan": 7, "fq": "867700000" }, 37 | { "chan": 8, "fq": "867900000" }, 38 | ] 39 | if not channel in range(0, 9): 40 | raise RuntimeError("channels should be in 0-8 for EU868") 41 | 42 | if channel == 0: 43 | import uos 44 | channel = (struct.unpack('B',uos.urandom(1))[0] % 7) + 1 45 | 46 | upstream = (item for item in EU868_FREQUENCIES if item["chan"] == channel).__next__() 47 | 48 | # set the 3 default channels to the same frequency 49 | lora.add_channel(0, frequency=int(upstream.get('fq')), dr_min=0, dr_max=5) 50 | lora.add_channel(1, frequency=int(upstream.get('fq')), dr_min=0, dr_max=5) 51 | lora.add_channel(2, frequency=int(upstream.get('fq')), dr_min=0, dr_max=5) 52 | 53 | for i in range(3, 16): 54 | lora.remove_channel(i) 55 | 56 | return lora 57 | 58 | ''' 59 | call back for handling RX packets 60 | ''' 61 | def lora_cb(lora): 62 | events = lora.events() 63 | if events & LoRa.RX_PACKET_EVENT: 64 | if lora_socket is not None: 65 | frame, port = lora_socket.recvfrom(512) # longuest frame is +-220 66 | print(port, frame) 67 | if events & LoRa.TX_PACKET_EVENT: 68 | print("tx_time_on_air: {} ms @dr {}", lora.stats().tx_time_on_air, lora.stats().sftx) 69 | 70 | 71 | ''' 72 | Main operations: this is sample code for LoRaWAN on EU868 73 | ''' 74 | 75 | lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.EU868, device_class=LoRa.CLASS_C) 76 | 77 | # create an OTA authentication params 78 | dev_eui = binascii.unhexlify('0000000000000000') 79 | app_key = binascii.unhexlify('a926e5bb85271f2d') # not used leave empty loraserver.io 80 | nwk_key = binascii.unhexlify('a926e5bb85271f2da0440f2f4200afe3') 81 | 82 | prepare_channels(lora, 1, LORA_NODE_DR) 83 | 84 | # join a network using OTAA 85 | lora.join(activation=LoRa.OTAA, auth=(dev_eui, app_key, nwk_key), timeout=0, dr=LORA_NODE_DR) # DR is 2 in v1.1rb but 0 worked for ne 86 | 87 | # wait until the module has joined the network 88 | print('Over the air network activation ... ', end='') 89 | while not lora.has_joined(): 90 | time.sleep(2.5) 91 | print('.', end='') 92 | print('') 93 | 94 | # create a LoRa socket 95 | lora_socket = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 96 | 97 | # set the LoRaWAN data rate 98 | lora_socket.setsockopt(socket.SOL_LORA, socket.SO_DR, LORA_NODE_DR) 99 | 100 | # msg are confirmed at the FMS level 101 | lora_socket.setsockopt(socket.SOL_LORA, socket.SO_CONFIRMED, 0) 102 | 103 | # make the socket non blocking y default 104 | lora_socket.setblocking(False) 105 | 106 | lora.callback(trigger=( LoRa.RX_PACKET_EVENT | 107 | LoRa.TX_PACKET_EVENT | 108 | LoRa.TX_FAILED_EVENT ), handler=lora_cb) 109 | 110 | time.sleep(4) # this timer is important and caused me some trouble ... 111 | 112 | for i in range(0, 1000): 113 | pkt = struct.pack('>H', i) 114 | print('Sending:', pkt) 115 | lora_socket.send(pkt) 116 | time.sleep(300) 117 | -------------------------------------------------------------------------------- /examples/lorawan-regional-examples/main_US915.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | """ 12 | OTAA Node example as per LoRaWAN US915 regional specification 13 | - tested works with a LoRaServer, shall works on TTN servers 14 | - This example uses 8 channels so you will need an 8 channel GW 15 | (not a 1 channel GW like the NanoGateway) 16 | """ 17 | 18 | from network import LoRa 19 | import socket 20 | import binascii 21 | import struct 22 | import time 23 | 24 | LORA_FREQUENCY = 916800000 # start of the 1st subband 25 | LORA_NODE_DR = 4 26 | ''' 27 | utility function to setup the lora channels 28 | ''' 29 | def prepare_channels(lora, channel, data_rate): 30 | 31 | US915_FREQUENCIES = [ 32 | { "chan": 64, "fq": "902300000" } 33 | ] 34 | if not channel in range(64,65): 35 | raise RuntimeError("channels should be in 64 for US915 (only subband 1 is implemented in this example)") 36 | upstream = (item for item in US915_FREQUENCIES if item["chan"] == channel).__next__() 37 | 38 | lora.add_channel(int(upstream.get('chan')), frequency=int(upstream.get('fq')), dr_min=0, dr_max=int(data_rate)) 39 | print("*** Adding channel up %s %s" % (upstream.get('chan'), upstream.get('fq'))) 40 | 41 | for index in range(0, 71): 42 | if index != upstream.get('chan'): 43 | lora.remove_channel(index) 44 | 45 | return lora 46 | 47 | ''' 48 | call back for handling RX packets 49 | ''' 50 | def lora_cb(lora): 51 | events = lora.events() 52 | if events & LoRa.RX_PACKET_EVENT: 53 | if lora_socket is not None: 54 | frame, port = lora_socket.recvfrom(512) # longuest frame is +-220 55 | print(port, frame) 56 | if events & LoRa.TX_PACKET_EVENT: 57 | print("tx_time_on_air: {} ms @dr {}", lora.stats().tx_time_on_air, lora.stats().sftx) 58 | 59 | 60 | ''' 61 | Main operations: this is sample code for LoRaWAN on US915 62 | ''' 63 | 64 | lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.US915, device_class=LoRa.CLASS_C) 65 | 66 | # create an OTA authentication params 67 | dev_eui = binascii.unhexlify('0000000000000000') 68 | app_key = binascii.unhexlify('a926e5bb85271f2d') # not used leave empty loraserver.io 69 | nwk_key = binascii.unhexlify('a926e5bb85271f2da0440f2f4200afe3') 70 | 71 | prepare_channels(lora, 64, 0) 72 | 73 | # join a network using OTAA 74 | lora.join(activation=LoRa.OTAA, auth=(dev_eui, app_key, nwk_key), timeout=0, dr=0) # US915 always joins at DR2 75 | 76 | for i in range(0, 8): 77 | fq = LORA_FREQUENCY + (i * 200000) 78 | lora.add_channel(i, frequency=fq, dr_min=0, dr_max=LORA_NODE_DR) 79 | print("US915 Adding channel up %s %s" % (i, fq)) 80 | 81 | # wait until the module has joined the network 82 | print('Over the air network activation ... ', end='') 83 | while not lora.has_joined(): 84 | time.sleep(2.5) 85 | print('.', end='') 86 | print('') 87 | 88 | lora_socket = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 89 | 90 | # set the LoRaWAN data rate 91 | # create a LoRa socket 92 | lora_socket.setsockopt(socket.SOL_LORA, socket.SO_DR, LORA_NODE_DR) 93 | 94 | # msg are confirmed at the FMS level 95 | lora_socket.setsockopt(socket.SOL_LORA, socket.SO_CONFIRMED, 0) 96 | 97 | # make the socket non blocking y default 98 | lora_socket.setblocking(False) 99 | 100 | lora.callback(trigger=( LoRa.RX_PACKET_EVENT | 101 | LoRa.TX_PACKET_EVENT | 102 | LoRa.TX_FAILED_EVENT ), handler=lora_cb) 103 | 104 | time.sleep(4) # this timer is important and caused me some trouble ... 105 | 106 | for i in range(0, 1000): 107 | pkt = struct.pack('>H', i) 108 | print('Sending:', pkt) 109 | lora_socket.send(pkt) 110 | time.sleep(300) 111 | -------------------------------------------------------------------------------- /examples/mqtt/boot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from machine import UART 12 | import machine 13 | import os 14 | 15 | uart = UART(0, baudrate=115200) 16 | os.dupterm(uart) 17 | 18 | machine.main('main.py') 19 | -------------------------------------------------------------------------------- /examples/mqtt/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from network import WLAN 12 | from mqtt import MQTTClient 13 | import machine 14 | import time 15 | 16 | def settimeout(duration): 17 | pass 18 | 19 | wlan = WLAN(mode=WLAN.STA) 20 | wlan.antenna(WLAN.EXT_ANT) 21 | wlan.connect("yourwifinetwork", auth=(WLAN.WPA2, "wifipassword"), timeout=5000) 22 | 23 | while not wlan.isconnected(): 24 | machine.idle() 25 | 26 | print("Connected to Wifi\n") 27 | client = MQTTClient("demo", "broker.hivemq.com", port=1883) 28 | client.settimeout = settimeout 29 | client.connect() 30 | 31 | while True: 32 | print("Sending ON") 33 | client.publish("/lights", "ON") 34 | time.sleep(1) 35 | print("Sending OFF") 36 | client.publish("/lights", "OFF") 37 | time.sleep(1) 38 | -------------------------------------------------------------------------------- /examples/onlineLog/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pycom/pycom-libraries/75d0e67cb421e0576a3a9677bb0d9d81f27ebdb7/examples/onlineLog/__init__.py -------------------------------------------------------------------------------- /examples/onlineLog/boot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from machine import UART 12 | import machine 13 | import os 14 | from network import WLAN 15 | 16 | uart = UART(0, baudrate=115200) 17 | os.dupterm(uart) 18 | 19 | wifi_ssid = 'YOURWIFISSID' 20 | wifi_pass = 'YOURWIFIPASSWORD' 21 | 22 | if machine.reset_cause() != machine.SOFT_RESET: 23 | 24 | wlan = WLAN(mode=WLAN.STA) 25 | 26 | wlan.connect(wifi_ssid, auth=(WLAN.WPA2, wifi_pass), timeout=5000) 27 | 28 | while not wlan.isconnected(): 29 | machine.idle() 30 | 31 | 32 | machine.main('main.py') 33 | -------------------------------------------------------------------------------- /examples/onlineLog/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | import time 12 | import machine 13 | from onewire import DS18X20 14 | from onewire import OneWire 15 | 16 | import usocket as socket 17 | 18 | publicKey = 'SPARKFUNCHANNELPUBLICKEY' 19 | privateKey = 'SPARKFUNCAHNNELPRIVATEKEY' 20 | 21 | 22 | #DS18B20 data line connected to pin P10 23 | ow = OneWire(machine.Pin('P10')) 24 | temp = DS18X20(ow) 25 | 26 | while True: 27 | temp.start_conversion() 28 | time.sleep(1) 29 | tempValue = temp.read_temp_async()/100.0 30 | u = 'POST /input/%s?private_key=%s&temp=%f HTTP/1.0\n\n'%( publicKey, privateKey, tempValue) 31 | 32 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 33 | ai = socket.getaddrinfo("data.sparkfun.com", 80) 34 | addr = ai[0][4] 35 | s.connect(addr) 36 | s.sendall(u) 37 | status = s.recv(4096) 38 | s.close() 39 | print('POST temp=%f'%tempValue) 40 | time.sleep(10) 41 | -------------------------------------------------------------------------------- /examples/pytrack_pysense_accelerometer/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | import time 12 | 13 | # From:https://github.com/pycom/pycom-libraries 14 | from LIS2HH12 import LIS2HH12 15 | from pytrack import Pytrack 16 | 17 | py = Pytrack() 18 | acc = LIS2HH12() 19 | while True: 20 | pitch = acc.pitch() 21 | roll = acc.roll() 22 | print('{},{}'.format(pitch,roll)) 23 | time.sleep_ms(100) 24 | -------------------------------------------------------------------------------- /examples/pytrack_pysense_accelerometer/visualiser/README.md: -------------------------------------------------------------------------------- 1 | **About** 2 | 3 | This folder contains a [Processing](https://processing.org/) sketch that takes in roll and pitch values from a serial port in comma separated value (CSV) format, and tilts a 3D model accordingly. 4 | 5 | **Requriements** 6 | 7 | - Processing (tested on v3.3.6) 8 | - Python Mode for Processing 9 | 10 | **Setup** 11 | 12 | Once you have Processing with python mode installed, you will need to open this sketch and change the SERIAL_DEVICE variable to match the serial port of your device. Once you have done this just run the sketch and a 3D model should appear that tilts with the board. 13 | -------------------------------------------------------------------------------- /examples/pytrack_pysense_accelerometer/visualiser/pycomLogoGoInventGrey800.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pycom/pycom-libraries/75d0e67cb421e0576a3a9677bb0d9d81f27ebdb7/examples/pytrack_pysense_accelerometer/visualiser/pycomLogoGoInventGrey800.png -------------------------------------------------------------------------------- /examples/pytrack_pysense_accelerometer/visualiser/sketch.properties: -------------------------------------------------------------------------------- 1 | mode=Python 2 | mode.id=jycessing.mode.PythonMode 3 | -------------------------------------------------------------------------------- /examples/pytrack_pysense_accelerometer/visualiser/visualiser.pyde: -------------------------------------------------------------------------------- 1 | add_library('serial') 2 | SERIAL_DEVICE='/dev/tty.usbmodemPy343431' 3 | 4 | def setup(): 5 | global my_port 6 | 7 | #Connect to serial device 8 | for dev in Serial.list(): 9 | if dev == SERIAL_DEVICE: 10 | my_port = Serial(this, dev, 115200) 11 | print("Connected to '{}'".format(SERIAL_DEVICE)) 12 | break 13 | else: 14 | msg = "Could not find serial port '{}'" 15 | raise Exception(msg.format(SERIAL_DEVICE)) 16 | 17 | # Clear the serial buffer and consume first 18 | # line incase we missed the start of it 19 | my_port.clear() 20 | my_port.readStringUntil(10) 21 | 22 | #Setup window 23 | size(400, 400, P3D) 24 | 25 | lastRoll = 0 26 | lastPitch = 0 27 | 28 | def draw(): 29 | global lastRoll, lastPitch 30 | 31 | #Get new reading from serial port 32 | line = my_port.readStringUntil(10) 33 | if line != None: 34 | line = line.split(',') 35 | if len(line) == 2: 36 | pitch, roll = line 37 | try: 38 | pitch = float(pitch) 39 | roll = float(roll) 40 | lastPitch = pitch 41 | lastRoll = roll 42 | except Exception: 43 | pass 44 | 45 | background(0) 46 | noStroke() 47 | 48 | # Put view in the middle of the screen 49 | # and far enough away to see properly 50 | translate(width/2, height/2, -100) 51 | 52 | # Default to last proper result 53 | pitch = lastPitch 54 | roll = lastRoll 55 | 56 | # Rotate view 57 | rotateX(-radians(pitch)) 58 | rotateZ(radians(roll)) 59 | 60 | # Zoom 61 | scale(190) 62 | 63 | # Draw the box 64 | drawBox(0.6, 0.1, 1) 65 | 66 | def drawBox(w, h, d): 67 | # Front 68 | beginShape(QUADS) 69 | fill(255,0,0) 70 | vertex(-w, -h, d) 71 | vertex( w, -h, d) 72 | vertex( w, h, d) 73 | vertex(-w, h, d) 74 | endShape() 75 | 76 | # Back 77 | beginShape(QUADS) 78 | fill(255,255,0) 79 | vertex( w, -h, -d) 80 | vertex(-w, -h, -d) 81 | vertex(-w, h, -d) 82 | vertex( w, h, -d) 83 | endShape() 84 | 85 | # Bottom 86 | beginShape(QUADS) 87 | fill( 255,0,255) 88 | vertex(-w, h, d) 89 | vertex( w, h, d) 90 | vertex( w, h, -d) 91 | vertex(-w, h, -d) 92 | endShape() 93 | 94 | # Top 95 | img = loadImage("pycomLogoGoInventGrey800.png"); 96 | blendMode(REPLACE) 97 | textureMode(NORMAL) 98 | textureWrap(CLAMP) 99 | beginShape(QUADS) 100 | texture(img) 101 | tint(255,255,255) 102 | vertex(-w, -h, -d, -1, -0.2) 103 | vertex( w, -h, -d, 2, -0.2) 104 | vertex( w, -h, d, 2, 1.2) 105 | vertex(-w, -h, d, -1, 1.2) 106 | endShape() 107 | 108 | # Right 109 | beginShape(QUADS) 110 | fill(0,0,255) 111 | vertex( w, -h, d) 112 | vertex( w, -h, -d) 113 | vertex( w, h, -d) 114 | vertex( w, h, d) 115 | endShape() 116 | 117 | # Left 118 | beginShape(QUADS) 119 | fill(0,255,0) 120 | vertex(-w, -h, -d) 121 | vertex(-w, -h, d) 122 | vertex(-w, h, d) 123 | vertex(-w, h, -d) 124 | endShape() 125 | -------------------------------------------------------------------------------- /examples/sigfoxUplink/boot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from machine import UART 12 | import machine 13 | import os 14 | 15 | uart = UART(0, baudrate=115200) 16 | os.dupterm(uart) 17 | 18 | machine.main('main.py') 19 | -------------------------------------------------------------------------------- /examples/sigfoxUplink/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from network import Sigfox 12 | import socket 13 | 14 | # init Sigfox for RCZ1 (Europe) 15 | sigfox = Sigfox(mode=Sigfox.SIGFOX, rcz=Sigfox.RCZ1) 16 | 17 | # create a Sigfox socket 18 | s = socket.socket(socket.AF_SIGFOX, socket.SOCK_RAW) 19 | 20 | # make the socket blocking 21 | s.setblocking(True) 22 | 23 | # configure it as uplink only 24 | s.setsockopt(socket.SOL_SIGFOX, socket.SO_RX, False) 25 | 26 | # send some bytes 27 | s.send(bytes([0x01, 0x02, 0x03])) 28 | -------------------------------------------------------------------------------- /examples/threading/boot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from machine import UART 12 | import machine 13 | import os 14 | 15 | uart = UART(0, baudrate=115200) 16 | os.dupterm(uart) 17 | 18 | machine.main('main.py') 19 | -------------------------------------------------------------------------------- /examples/threading/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | import _thread 12 | import time 13 | 14 | def th_func(delay, id): 15 | while True: 16 | time.sleep(delay) 17 | print('Running thread %d' % id) 18 | 19 | for i in range(2): 20 | _thread.start_new_thread(th_func, (i + 1, i)) -------------------------------------------------------------------------------- /img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pycom/pycom-libraries/75d0e67cb421e0576a3a9677bb0d9d81f27ebdb7/img/logo.png -------------------------------------------------------------------------------- /lib/ADS1115/ADS1115.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | # Interface for ADS1115 16-bit I2C ADC 12 | 13 | class ADS1115: 14 | 15 | def __init__(self, i2c, address=0x49, gain=0): 16 | # Modified to work in Wipy2.0 17 | self.i2c = i2c 18 | self.address = address 19 | self.gain = gain # 0 --> 2/3 6.144V 20 | 21 | def _write_register(self, register, value): 22 | data = ustruct.pack('>BH', register, value) 23 | self.i2c.writeto(self.address, data) 24 | 25 | def _read_register(self, register): 26 | data = self.i2c.readfrom_mem(self.address, register, 2 ) 27 | return ustruct.unpack('>h', data)[0] 28 | -------------------------------------------------------------------------------- /lib/ADS1115/boot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from machine import UART 12 | import machine 13 | import os 14 | 15 | uart = UART(0, baudrate=115200) 16 | os.dupterm(uart) 17 | 18 | machine.main('main.py') 19 | -------------------------------------------------------------------------------- /lib/ALSPT19/ALSPT19.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from machine import ADC 12 | import time 13 | 14 | class ALSPT19(object): 15 | def __init__(self, pin_name): 16 | adc = ADC() 17 | self.pin = adc.channel(pin=pin_name, attn=ADC.ATTN_11DB, bits=12) 18 | self.threshold = None 19 | 20 | def calibrate(self, samples=300): 21 | max_val = 0 22 | for _ in range(samples): 23 | val = self.pin() 24 | if val > max_val: 25 | max_val = val 26 | time.sleep_ms(10) 27 | 28 | self.threshold = max_val * 1.2 29 | 30 | def is_on(self): 31 | if self.pin() > self.threshold: 32 | return True 33 | return False 34 | -------------------------------------------------------------------------------- /lib/TB6612FNG/TB6612FNG.py: -------------------------------------------------------------------------------- 1 | from machine import PWM, Pin 2 | 3 | class _TB6612FNG_channel(object): 4 | _pwm_id = 0 5 | _pwm = PWM(0, frequency=5000) 6 | 7 | @classmethod 8 | def id(cls): 9 | if cls._pwm_id > 7: 10 | raise Exception("Cannot create more pwm channels") 11 | 12 | temp = cls._pwm_id 13 | cls._pwm_id += 1 14 | return temp 15 | 16 | def __init__(self, pin_1, pin_2, pwm_pin): 17 | self.pin_1 = Pin(pin_1, mode=Pin.OUT, pull=None) 18 | self.pin_1.value(0) 19 | 20 | self.pin_2 = Pin(pin_2, mode=Pin.OUT, pull=None) 21 | self.pin_1.value(0) 22 | 23 | self.pwm = TB6612FNG_channel._pwm.channel(self.id(), pin=pwm_pin, duty_cycle=1) 24 | self.pwm.duty_cycle(0) 25 | 26 | def clockwise(self): 27 | self.pin_1.value(1) 28 | self.pin_2.value(0) 29 | 30 | def anticlockwise(self): 31 | self.pin_1.value(0) 32 | self.pin_2.value(1) 33 | 34 | def short_break(self): 35 | self.pin_1.value(1) 36 | self.pin_2.value(1) 37 | 38 | def freewheel(self): 39 | self.pin_1.value(0) 40 | self.pin_2.value(0) 41 | 42 | def duty_cycle(self, *args, **kwargs): 43 | return self.pwm.duty_cycle(*args, **kwargs) 44 | 45 | 46 | class TB6612FNG(object): 47 | 48 | def __init__(self, a_1, a_2, a_pwm, b_1, b_2, b_pwm, standby_pin): 49 | self._standby = Pin(standby_pin, mode=Pin.OUT, pull=None) 50 | self._standby.value(1) 51 | self.channelA = _TB6612FNG_channel(a_1, a_2, a_pwm) 52 | self.channelB = _TB6612FNG_channel(b_1, b_2, b_pwm) 53 | 54 | def standby(self, *args): 55 | return self._standby.value(*args) 56 | -------------------------------------------------------------------------------- /lib/mqtt_aws/MQTTConst.py: -------------------------------------------------------------------------------- 1 | 2 | # - Protocol types 3 | MQTTv3_1 = 3 4 | MQTTv3_1_1 = 4 5 | 6 | # - OfflinePublishQueueing drop behavior 7 | DROP_OLDEST = 0 8 | DROP_NEWEST = 1 9 | 10 | # Message types 11 | MSG_CONNECT = 0x10 12 | MSG_CONNACK = 0x20 13 | MSG_PUBLISH = 0x30 14 | MSG_PUBACK = 0x40 15 | MSG_PUBREC = 0x50 16 | MSG_PUBREL = 0x60 17 | MSG_PUBCOMP = 0x70 18 | MSG_SUBSCRIBE = 0x80 19 | MSG_SUBACK = 0x90 20 | MSG_UNSUBSCRIBE = 0xA0 21 | MSG_UNSUBACK = 0xB0 22 | MSG_PINGREQ = 0xC0 23 | MSG_PINGRESP = 0xD0 24 | MSG_DISCONNECT = 0xE0 25 | 26 | # Connection state 27 | STATE_CONNECTED = 0x01 28 | STATE_CONNECTING = 0x02 29 | STATE_DISCONNECTED = 0x03 30 | 31 | class UUID: 32 | int_ = int 33 | bytes_ = bytes 34 | 35 | def __init__(self, bytes=None, version=None): 36 | 37 | self._int = UUID.int_.from_bytes(bytes, 'big') 38 | 39 | self._int &= ~(0xc000 << 48) 40 | self._int |= 0x8000 << 48 41 | self._int &= ~(0xf000 << 64) 42 | self._int |= version << 76 43 | 44 | @property 45 | def urn(self): 46 | return 'urn:uuid:' + str(self) 47 | -------------------------------------------------------------------------------- /lib/mqtt_aws/MQTTLib.py: -------------------------------------------------------------------------------- 1 | import MQTTConst as mqttConst 2 | import MQTTClient as mqttClient 3 | import MQTTShadowManager as shadowManager 4 | import MQTTDeviceShadow as deviceShadow 5 | 6 | class AWSIoTMQTTClient: 7 | 8 | def __init__(self, clientID, protocolType=mqttConst.MQTTv3_1_1, useWebsocket=False, cleanSession=True): 9 | self._mqttClient = mqttClient.MQTTClient(clientID, cleanSession, protocolType) 10 | 11 | # Configuration APIs 12 | def configureLastWill(self, topic, payload, QoS): 13 | self._mqttClient.setLastWill(topic, payload, QoS) 14 | 15 | def clearLastWill(self): 16 | self._mqttClient.clearLastWill() 17 | 18 | def configureEndpoint(self, hostName, portNumber): 19 | self._mqttClient.configEndpoint(hostName, portNumber) 20 | 21 | def configureIAMCredentials(self, AWSAccessKeyID, AWSSecretAccessKey, AWSSessionToken=""): 22 | self._mqttClient.configIAMCredentials(AWSAccessKeyID, AWSSecretAccessKey, AWSSessionToken) 23 | 24 | def configureCredentials(self, CAFilePath, KeyPath="", CertificatePath=""): # Should be good for MutualAuth certs config and Websocket rootCA config 25 | self._mqttClient.configCredentials(CAFilePath, KeyPath, CertificatePath) 26 | 27 | def configureAutoReconnectBackoffTime(self, baseReconnectQuietTimeSecond, maxReconnectQuietTimeSecond, stableConnectionTimeSecond): 28 | self._mqttClient.setBackoffTiming(baseReconnectQuietTimeSecond, maxReconnectQuietTimeSecond, stableConnectionTimeSecond) 29 | 30 | def configureOfflinePublishQueueing(self, queueSize, dropBehavior=mqttConst.DROP_NEWEST): 31 | self._mqttClient.setOfflinePublishQueueing(queueSize, dropBehavior) 32 | 33 | def configureDrainingFrequency(self, frequencyInHz): 34 | self._mqttClient.setDrainingIntervalSecond(1/float(frequencyInHz)) 35 | 36 | def configureConnectDisconnectTimeout(self, timeoutSecond): 37 | self._mqttClient.setConnectDisconnectTimeoutSecond(timeoutSecond) 38 | 39 | def configureMQTTOperationTimeout(self, timeoutSecond): 40 | self._mqttClient.setMQTTOperationTimeoutSecond(timeoutSecond) 41 | 42 | # MQTT functionality APIs 43 | def connect(self, keepAliveIntervalSecond=30): 44 | return self._mqttClient.connect(keepAliveIntervalSecond) 45 | 46 | def disconnect(self): 47 | return self._mqttClient.disconnect() 48 | 49 | def publish(self, topic, payload, QoS): 50 | return self._mqttClient.publish(topic, payload, QoS, False) # Disable retain for publish by now 51 | 52 | def subscribe(self, topic, QoS, callback): 53 | return self._mqttClient.subscribe(topic, QoS, callback) 54 | 55 | def unsubscribe(self, topic): 56 | return self._mqttClient.unsubscribe(topic) 57 | 58 | 59 | class AWSIoTMQTTShadowClient: 60 | 61 | def __init__(self, clientID, protocolType=mqttConst.MQTTv3_1_1, useWebsocket=False, cleanSession=True): 62 | # AWSIOTMQTTClient instance 63 | self._AWSIoTMQTTClient = AWSIoTMQTTClient(clientID, protocolType, useWebsocket, cleanSession) 64 | # Configure it to disable offline Publish Queueing 65 | self._AWSIoTMQTTClient.configureOfflinePublishQueueing(0) # Disable queueing, no queueing for time-sentive shadow messages 66 | self._AWSIoTMQTTClient.configureDrainingFrequency(10) 67 | # Now retrieve the configured mqttCore and init a shadowManager instance 68 | self._shadowManager = shadowManager.shadowManager(self._AWSIoTMQTTClient._mqttClient) 69 | 70 | # Configuration APIs 71 | def configureLastWill(self, topic, payload, QoS): 72 | self._AWSIoTMQTTClient.configureLastWill(topic, payload, QoS) 73 | 74 | def clearLastWill(self): 75 | self._AWSIoTMQTTClient.clearLastWill() 76 | 77 | def configureEndpoint(self, hostName, portNumber): 78 | self._AWSIoTMQTTClient.configureEndpoint(hostName, portNumber) 79 | 80 | def configureIAMCredentials(self, AWSAccessKeyID, AWSSecretAccessKey, AWSSTSToken=""): 81 | # AWSIoTMQTTClient.configureIAMCredentials 82 | self._AWSIoTMQTTClient.configureIAMCredentials(AWSAccessKeyID, AWSSecretAccessKey, AWSSTSToken) 83 | 84 | def configureCredentials(self, CAFilePath, KeyPath="", CertificatePath=""): # Should be good for MutualAuth and Websocket 85 | self._AWSIoTMQTTClient.configureCredentials(CAFilePath, KeyPath, CertificatePath) 86 | 87 | def configureAutoReconnectBackoffTime(self, baseReconnectQuietTimeSecond, maxReconnectQuietTimeSecond, stableConnectionTimeSecond): 88 | self._AWSIoTMQTTClient.configureAutoReconnectBackoffTime(baseReconnectQuietTimeSecond, maxReconnectQuietTimeSecond, stableConnectionTimeSecond) 89 | 90 | def configureConnectDisconnectTimeout(self, timeoutSecond): 91 | self._AWSIoTMQTTClient.configureConnectDisconnectTimeout(timeoutSecond) 92 | 93 | def configureMQTTOperationTimeout(self, timeoutSecond): 94 | self._AWSIoTMQTTClient.configureMQTTOperationTimeout(timeoutSecond) 95 | 96 | # Start the MQTT connection 97 | def connect(self, keepAliveIntervalSecond=30): 98 | return self._AWSIoTMQTTClient.connect(keepAliveIntervalSecond) 99 | 100 | # End the MQTT connection 101 | def disconnect(self): 102 | return self._AWSIoTMQTTClient.disconnect() 103 | 104 | # Shadow management API 105 | def createShadowHandlerWithName(self, shadowName, isPersistentSubscribe): 106 | # Create and return a deviceShadow instance 107 | return deviceShadow.deviceShadow(shadowName, isPersistentSubscribe, self._shadowManager) 108 | # Shadow APIs are accessible in deviceShadow instance": 109 | ### 110 | # deviceShadow.shadowGet 111 | # deviceShadow.shadowUpdate 112 | # deviceShadow.shadowDelete 113 | # deviceShadow.shadowRegisterDelta 114 | # deviceShadow.shadowUnregisterDelta 115 | 116 | # MQTT connection management API 117 | def getMQTTConnection(self): 118 | # Return the internal AWSIoTMQTTClient instance 119 | return self._AWSIoTMQTTClient 120 | -------------------------------------------------------------------------------- /lib/mqtt_aws/MQTTShadowManager.py: -------------------------------------------------------------------------------- 1 | import _thread 2 | import time 3 | 4 | class shadowManager: 5 | 6 | def __init__(self, MQTTClient): 7 | if MQTTClient is None: 8 | raise ValueError("MQTT Client is none") 9 | 10 | self._mqttClient = MQTTClient 11 | self._subscribe_mutex = _thread.allocate_lock() 12 | 13 | def getClientID(self): 14 | return self._mqttClient.getClientID() 15 | 16 | def _getDeltaTopic(self, shadowName): 17 | return "$aws/things/" + str(shadowName) + "/shadow/update/delta" 18 | 19 | def _getNonDeltaTopics(self, shadowName, actionName): 20 | generalTopic = "$aws/things/" + str(shadowName) + "/shadow/" + str(actionName) 21 | acceptTopic = "$aws/things/" + str(shadowName) + "/shadow/" + str(actionName) + "/accepted" 22 | rejectTopic = "$aws/things/" + str(shadowName) + "/shadow/" + str(actionName) + "/rejected" 23 | 24 | return (generalTopic, acceptTopic, rejectTopic) 25 | 26 | def shadowPublish(self, shadowName, shadowAction, payload): 27 | (generalTopic, acceptTopic, rejectTopic) = self._getNonDeltaTopics(shadowName, shadowAction) 28 | self._mqttClient.publish(generalTopic, payload, 0, False) 29 | 30 | def shadowSubscribe(self, shadowName, shadowAction, callback): 31 | self._subscribe_mutex.acquire() 32 | if shadowAction == "delta": 33 | deltaTopic = self._getDeltaTopic(shadowName) 34 | self._mqttClient.subscribe(deltaTopic, 0, callback) 35 | else: 36 | (generalTopic, acceptTopic, rejectTopic) = self._getNonDeltaTopics(shadowName, shadowAction) 37 | self._mqttClient.subscribe(acceptTopic, 0, callback) 38 | self._mqttClient.subscribe(rejectTopic, 0, callback) 39 | time.sleep(2) 40 | self._subscribe_mutex.release() 41 | 42 | def shadowUnsubscribe(self, srcShadowName, srcShadowAction): 43 | self._subscribe_mutex.acquire() 44 | currentShadowAction = _shadowAction(srcShadowName, srcShadowAction) 45 | if shadowAction == "delta": 46 | deltaTopic = self._getDeltaTopic(shadowName) 47 | self._mqttClient.unsubscribe(deltaTopic) 48 | else: 49 | (generalTopic, acceptTopic, rejectTopic) = self._getNonDeltaTopics(shadowName, shadowAction) 50 | self._mqttClient.unsubscribe(acceptTopic) 51 | self._mqttClient.unsubscribe(rejectTopic) 52 | self._subscribe_mutex.release() 53 | 54 | def insertShadowCallback(self, callback, payload, status, token): 55 | self._mqttClient.insertShadowCallback(callback, payload, status, token) 56 | -------------------------------------------------------------------------------- /lib/sqnsupgrade/sqnscodec.py: -------------------------------------------------------------------------------- 1 | # -*- python -*- 2 | ################################################################# 3 | # 4 | # Module : CODEC 5 | # Purpose: Base encoders/decoders 6 | # 7 | ################################################################# 8 | # 9 | # Copyright (c) 2011 SEQUANS Communications. 10 | # All rights reserved. 11 | # 12 | # This is confidential and proprietary source code of SEQUANS 13 | # Communications. The use of the present source code and all 14 | # its derived forms is exclusively governed by the restricted 15 | # terms and conditions set forth in the SEQUANS 16 | # Communications' EARLY ADOPTER AGREEMENT and/or LICENCE 17 | # AGREEMENT. The present source code and all its derived 18 | # forms can ONLY and EXCLUSIVELY be used with SEQUANS 19 | # Communications' products. The distribution/sale of the 20 | # present source code and all its derived forms is EXCLUSIVELY 21 | # RESERVED to regular LICENCE holder and otherwise STRICTLY 22 | # PROHIBITED. 23 | # 24 | ################################################################# 25 | import struct, array 26 | 27 | LITTLE_ENDIAN = "<" 28 | NATIVE_ENDIAN = "=" 29 | BIG_ENDIAN = ">" 30 | 31 | # -------------------------------------------------// Utility /__________________________________ 32 | class encode: 33 | @staticmethod 34 | def u32 (value, endian = BIG_ENDIAN): 35 | return array.array("c", struct.pack(endian + "I", value)) 36 | 37 | @staticmethod 38 | def s32 (value, endian = BIG_ENDIAN): 39 | if value < 0: 40 | value = 0x100000000 + value 41 | return encode.u32(value, endian) 42 | 43 | @staticmethod 44 | def u16 (value, endian = BIG_ENDIAN): 45 | return array.array("c", struct.pack(endian + "H", value)) 46 | 47 | @staticmethod 48 | def u8 (value, endian = None): 49 | return array.array("c", chr(value)) 50 | 51 | @staticmethod 52 | def string (value, endian = None): 53 | return array.array("c", value + "\x00") 54 | 55 | class decode: 56 | @staticmethod 57 | def u32 (value, endian = BIG_ENDIAN): 58 | return struct.unpack(endian + "I", value)[0] 59 | 60 | @staticmethod 61 | def s32 (value, endian = BIG_ENDIAN): 62 | v = decode.u32(value, endian) 63 | if v & (1 << 31): 64 | return v - 0x100000000 65 | return v 66 | 67 | @staticmethod 68 | def u16 (value, endian = BIG_ENDIAN): 69 | return struct.unpack(endian + "H", value)[0] 70 | 71 | @staticmethod 72 | def u8 (value, endian = None): 73 | return ord(value) 74 | 75 | @staticmethod 76 | def string (value, endian = None): 77 | offset = 0 78 | str = "" 79 | c = value[offset] 80 | while c != '\x00': 81 | offset += 1 82 | str += c 83 | c = value[offset] 84 | 85 | return str 86 | 87 | -------------------------------------------------------------------------------- /lib/sqnsupgrade/sqnscrc.py: -------------------------------------------------------------------------------- 1 | # -*- python -*- 2 | ################################################################# 3 | # 4 | # Module : CRC 5 | # Purpose: CRC calculation 6 | # 7 | ################################################################# 8 | # 9 | # Copyright (c) 2011 SEQUANS Communications. 10 | # All rights reserved. 11 | # 12 | # This is confidential and proprietary source code of SEQUANS 13 | # Communications. The use of the present source code and all 14 | # its derived forms is exclusively governed by the restricted 15 | # terms and conditions set forth in the SEQUANS 16 | # Communications' EARLY ADOPTER AGREEMENT and/or LICENCE 17 | # AGREEMENT. The present source code and all its derived 18 | # forms can ONLY and EXCLUSIVELY be used with SEQUANS 19 | # Communications' products. The distribution/sale of the 20 | # present source code and all its derived forms is EXCLUSIVELY 21 | # RESERVED to regular LICENCE holder and otherwise STRICTLY 22 | # PROHIBITED. 23 | # 24 | ################################################################# 25 | import sqnscodec as codec 26 | 27 | # -------------------------------------------------// Fletcher /_________________________________ 28 | def fletcher32 (data): 29 | l = len(data) 30 | 31 | index = 0 32 | s1 = s2 = 0xFFFF 33 | while l > 1: 34 | qty = 720 if l > 720 else (l & ~1) 35 | l -= qty 36 | 37 | qty += index 38 | while index < qty: 39 | word = codec.decode.u16(data[index:index+2]) 40 | s1 += word 41 | s2 += s1 42 | 43 | index += 2 44 | 45 | s1 = (s1 & 0xFFFF) + (s1 >> 16) 46 | s2 = (s2 & 0xFFFF) + (s2 >> 16) 47 | 48 | if (l & 1): 49 | s1 += ord(data[index]) << 8 50 | s2 += s1 51 | 52 | s1 = (s1 & 0xFFFF) + (s1 >> 16) 53 | s2 = (s2 & 0xFFFF) + (s2 >> 16) 54 | 55 | s1 = (s1 & 0xFFFF) + (s1 >> 16) 56 | s2 = (s2 & 0xFFFF) + (s2 >> 16) 57 | 58 | return (s2 << 16) | s1 59 | -------------------------------------------------------------------------------- /license/Pycom CLA Apache v1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pycom/pycom-libraries/75d0e67cb421e0576a3a9677bb0d9d81f27ebdb7/license/Pycom CLA Apache v1.pdf -------------------------------------------------------------------------------- /license/Pycom FAQ v2.2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pycom/pycom-libraries/75d0e67cb421e0576a3a9677bb0d9d81f27ebdb7/license/Pycom FAQ v2.2.pdf -------------------------------------------------------------------------------- /license/Pycom Licences v2.2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pycom/pycom-libraries/75d0e67cb421e0576a3a9677bb0d9d81f27ebdb7/license/Pycom Licences v2.2.pdf -------------------------------------------------------------------------------- /pycom-docker-fw-build/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:bionic 2 | 3 | RUN apt-get update && apt-get -y install wget git build-essential python python-serial && \ 4 | mkdir /opt/frozen/ && cd /opt && \ 5 | wget -q https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz && \ 6 | tar -xzvf xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz && \ 7 | git clone --recursive https://github.com/pycom/pycom-esp-idf.git && \ 8 | cd pycom-esp-idf && git submodule update --init && cd .. && \ 9 | git clone --recursive https://github.com/pycom/pycom-micropython-sigfox.git 10 | 11 | ADD assets/build /usr/bin/build 12 | -------------------------------------------------------------------------------- /pycom-docker-fw-build/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | # Tool for adding your MicroPython code frozen in the flash 4 | 5 | ### usage: 6 | ``` 7 | sudo docker run -v `pwd`:/opt/frozen -it goinvent/pycom-fw build board-type your-project-version-code [micropython-sigfox-git-tag] [esp-idf-git-tag] 8 | ``` 9 | where board in `WIPY LOPY SIPY GPY FIPY LOPY4` and your-project-version-code is a tag for output file: `LOPY4-your-project-version-code.tar.gz` 10 | 11 | 12 | ### example: 13 | 14 | If you have your MicroPython project in the current directory `.` just type: 15 | 16 | ``` 17 | sudo docker run -v `pwd`:/opt/frozen -it goinvent/pycom-fw build LOPY4 myproject 18 | ``` 19 | For building against a specific revision (ex:v1.20.0.rc0 idf_v3.1) you can use: 20 | ``` 21 | sudo docker run -v `pwd`:/opt/frozen -it goinvent/pycom-fw build FIPY myproject v1.20.0.rc0 idf_v3.1 22 | ``` 23 | 24 | 25 | ### note: 26 | 27 | The Frozen code implementation might not support sub-directories in MicroPython code. 28 | 29 | The Frozen code implementation does not support adding assets (ex: db files, json,) 30 | 31 | ### re-generate this goinvent/pycom-fw docker image: 32 | 33 | ``` 34 | sudo docker build -t goinvent/pycom-fw . 35 | ``` 36 | -------------------------------------------------------------------------------- /pycom-docker-fw-build/assets/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | boards="WIPY LOPY SIPY GPY FIPY LOPY4" 4 | 5 | function usage() { 6 | echo "Usage:" 7 | echo " $0 board-type version-code [micropython-sigfox-git-tag] [esp-idf-git-tag]" 8 | echo "" 9 | echo "where board-type in $boards and version-code is a tag for output file" 10 | echo "" 11 | exit 1 12 | } 13 | 14 | function prepare_dev() { 15 | echo "Checkout $1 $2 board version-code" 16 | 17 | if [ ! -z "$1" ] 18 | then 19 | cd /opt/pycom-micropython-sigfox 20 | # fetch all branches 21 | git branch -r | grep -v '\->' | while read remote; do git branch --track "${remote#origin/}" "$remote"; done 22 | # try to find branch revision $1 23 | if git pull ; git rev-list $1.. >/dev/null 24 | 25 | then 26 | echo "Checkout " 27 | else 28 | exit 1 29 | fi 30 | cd /opt/pycom-micropython-sigfox 31 | git checkout $1 32 | cd mpy-cross && make clean && make && cd .. 33 | git submodule update --init 34 | fi 35 | 36 | if [ ! -z "$2" ] 37 | then 38 | cd /opt/pycom-esp-idf; 39 | # fetch all branches 40 | git branch -r | grep -v '\->' | while read remote; do git branch --track "${remote#origin/}" "$remote"; done 41 | # try to find branch revision $2 42 | if git rev-list $2.. >/dev/null 43 | 44 | then 45 | echo "" 46 | # branch exists 47 | else 48 | # branch does not exists 49 | exit 50 | fi 51 | cd /opt/pycom-esp-idf 52 | git checkout $2 53 | git submodule update --init 54 | fi 55 | } 56 | 57 | function build() { 58 | BOARD=$1 59 | PROJECT=$2 60 | export PATH=$PATH:/opt/xtensa-esp32-elf/bin/ 61 | export IDF_PATH=/opt/pycom-esp-idf 62 | board=`echo "$BOARD" | tr '[:upper:]' '[:lower:]'` 63 | cd /opt/pycom-micropython-sigfox/esp32/ && 64 | make clean BOARD=$BOARD && 65 | cd ../mpy-cross && make clean && make && cd ../esp32 && 66 | cp /opt/frozen/*.py ./frozen/Base/ && 67 | mv ./frozen/Base/main.py ./frozen/Base/_main.py 68 | make release BOARD=$BOARD RELEASE_DIR=/tmp 69 | mv /tmp/*.tar.gz /opt/frozen/$board-firmware-$PROJECT.tar.gz 70 | echo "Firmware $board-firmware-$PROJECT.tar.gz ready !" 71 | } 72 | 73 | [[ $boards =~ (^|[[:space:]])$1($|[[:space:]]) ]] && 74 | echo "#goinvent " || 75 | usage 76 | prepare_dev $3 $4 77 | build $1 $2 78 | -------------------------------------------------------------------------------- /pymesh/mobile_app/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Pymesh mobile application 3 | 4 | For demonstrating **Pymesh** features, a mobile application was created. 5 | 6 | It is available for both Android and iOS platforms. 7 | 8 | ## Android application 9 | 10 | It is available as `.apk` packet in this directory. 11 | 12 | # iOS mobile application 13 | 14 | Pymesh mobile application is released currently (v0.) as Beta testing, on Apple TestFlight (to be installed for free from App Store). 15 | 16 | Pymesh app can be added afterwards, using the link: https://testflight.apple.com/join/PIxwbTKp 17 | 18 | Requirements: iOS minimum version v12.1 19 | -------------------------------------------------------------------------------- /pymesh/mobile_app/app-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pycom/pycom-libraries/75d0e67cb421e0576a3a9677bb0d9d81f27ebdb7/pymesh/mobile_app/app-release.apk -------------------------------------------------------------------------------- /pymesh/pymesh_frozen/README.md: -------------------------------------------------------------------------------- 1 | # Pymesh micropython code 2 | 3 | This project exemplifies the use of Pycom's proprietary LoRa Mesh network - **Pymesh**. 4 | These scripts were created and tested on Lopy4 and Fipy, using the Pymesh binary release. 5 | 6 | Official Pymesh docs: https://docs.pycom.io/pymesh/ 7 | 8 | Forum Pymesh announcements: https://forum.pycom.io/topic/4449/pymesh-updates 9 | -------------------------------------------------------------------------------- /pymesh/pymesh_frozen/copy_fw.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | #set -x 4 | SOURCE="$(dirname $0)" 5 | if [ -z $1 ]; then 6 | echo "usage: $0 micropython_firmware_directory" 7 | exit 1 8 | fi 9 | if [ ! -d $1/esp32/frozen/Common ]; then 10 | echo "Need to specify valid micropython firmware directory!" 11 | exit 1 12 | fi 13 | if [ ! -d $SOURCE ]; then 14 | echo "Can't find source directory $SOURCE" 15 | exit 1 16 | fi 17 | 18 | # moving main 19 | # if [ -d $1/esp32/frozen/Pybytes ]; then 20 | # cp $SOURCE/main.py $1/esp32/frozen/Pybytes/_main.py 21 | # cp $1/esp32/frozen/Base/_boot.py $1/esp32/frozen/Pybytes/ 22 | # elif [ -d $1/esp32/frozen/Base ]; then 23 | # cp $SOURCE/main.py $1/esp32/frozen/Base/_main.py 24 | # else 25 | # cp $SOURCE/main.py $1/esp32/frozen/_main.py 26 | # fi 27 | 28 | for i in $SOURCE/lib/*.py; do 29 | SRC=$i 30 | FN=$(basename $i) 31 | cp $SRC $1/esp32/frozen/Common/_$FN 32 | done 33 | 34 | cp -r $SOURCE/lib/msgpack $1/esp32/frozen/Common/ 35 | echo "Done copying Pymesh library to $1/esp32/frozen/Common/" -------------------------------------------------------------------------------- /pymesh/pymesh_frozen/lib/ble_services.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020, Pycom Limited. 2 | # 3 | # This software is licensed under the GNU GPL version 3 or any 4 | # later version, with permitted additional terms. For more information 5 | # see the Pycom Licence v1.0 document supplied with this file, or 6 | # available at https://www.pycom.io/opensource/licensing 7 | 8 | from network import Bluetooth 9 | import time 10 | import msgpack 11 | 12 | VERSION = "1.0.0" 13 | 14 | class BleServices: 15 | 16 | def __init__(self, ble_name): 17 | # self.mesh_mac = mesh_mac 18 | self.ble_name = ble_name 19 | self.on_disconnect = None 20 | self._init() 21 | 22 | def _init(self): 23 | self.status = { 24 | 'connected' : False 25 | } 26 | 27 | bluetooth = Bluetooth(modem_sleep=False) 28 | adv_name = self.ble_name 29 | bluetooth.set_advertisement(name=adv_name, service_uuid=0xec00) 30 | print("BLE name:", adv_name) 31 | 32 | bluetooth.callback(trigger=Bluetooth.CLIENT_CONNECTED | Bluetooth.CLIENT_DISCONNECTED, handler=self.conn_cb) 33 | bluetooth.advertise(True) 34 | 35 | srv_rx = bluetooth.service(uuid=0xec00, isprimary=True) 36 | self.chr_rx = srv_rx.characteristic(uuid=0xec0e, value=0) 37 | 38 | srv_tx = bluetooth.service(uuid=0xed00, isprimary=True) 39 | self.chr_tx = srv_tx.characteristic(uuid=0xed0e, value=0) 40 | 41 | self.unpacker = None 42 | 43 | def conn_cb(self, bt_o): 44 | #global ble_connected 45 | events = bt_o.events() 46 | if events & Bluetooth.CLIENT_CONNECTED: 47 | self.status['connected'] = True 48 | print("Client connected") 49 | elif events & Bluetooth.CLIENT_DISCONNECTED: 50 | self.status['connected'] = False 51 | 52 | if self.on_disconnect: 53 | self.on_disconnect() 54 | 55 | print("Client disconnected") 56 | pass 57 | 58 | def unpacker_set(self, unpacker): 59 | self.unpacker = unpacker 60 | 61 | def close(self): 62 | bluetooth = Bluetooth() 63 | bluetooth.disconnect_client() 64 | bluetooth.deinit() 65 | pass 66 | 67 | def restart(self): 68 | print("BLE disconnnect client") 69 | bluetooth = Bluetooth() 70 | bluetooth.disconnect_client() 71 | time.sleep(2) 72 | self.status['connected'] = False 73 | if self.on_disconnect: 74 | self.on_disconnect() 75 | 76 | # bluetooth.deinit() 77 | # time.sleep(1) 78 | # self._init() 79 | pass 80 | 81 | -------------------------------------------------------------------------------- /pymesh/pymesh_frozen/lib/gps.py: -------------------------------------------------------------------------------- 1 | 2 | # Copyright (c) 2020, Pycom Limited. 3 | # 4 | # This software is licensed under the GNU GPL version 3 or any 5 | # later version, with permitted additional terms. For more information 6 | # see the Pycom Licence v1.0 document supplied with this file, or 7 | # available at https://www.pycom.io/opensource/licensing 8 | 9 | try: 10 | from pymesh_debug import print_debug 11 | except: 12 | from _pymesh_debug import print_debug 13 | 14 | import time 15 | # from pytrack import Pytrack 16 | # from L76GNSS import L76GNSS 17 | from machine import Timer 18 | 19 | __version__ = '1' 20 | """ 21 | * initial version 22 | """ 23 | 24 | class Gps: 25 | # Pycom office GPS coordinates 26 | lat = 51.45 27 | lon = 5.45313 28 | 29 | l76 = None 30 | _timer = None 31 | #is_set = False 32 | 33 | @staticmethod 34 | def set_location(latitude, longitude): 35 | dlat = str(type(latitude)) 36 | dlon = str(type(longitude)) 37 | if dlat == dlon == "": 38 | Gps.lat = latitude 39 | Gps.lon = longitude 40 | is_set = True 41 | else: 42 | print_debug(3, "Error parsing ", latitude, longitude) 43 | 44 | @staticmethod 45 | def get_location(): 46 | return (Gps.lat, Gps.lon) 47 | 48 | # @staticmethod 49 | # def init_static(): 50 | # is_pytrack = True 51 | # try: 52 | # py = Pytrack() 53 | # Gps.l76 = L76GNSS(py, timeout=30) 54 | # #l76.coordinates() 55 | # Gps._timer = Timer.Alarm(Gps.gps_periodic, 30, periodic=True) 56 | # print_debug(3, "Pytrack detected") 57 | # except: 58 | # is_pytrack = False 59 | # print_debug(3, "Pytrack NOT detected") 60 | # #TODO: how to check if GPS is conencted 61 | # return is_pytrack 62 | 63 | # @staticmethod 64 | # def gps_periodic(alarm): 65 | # t0 = time.ticks_ms() 66 | # coord = Gps.l76.coordinates() 67 | # if coord[0] != None: 68 | # Gps.lat, Gps.lon = coord 69 | # print_debug(3, "New coord ", coord) 70 | # dt = time.ticks_ms() - t0 71 | # print_debug(3, " =====>>>> gps_periodic ", dt) 72 | 73 | # @staticmethod 74 | # def terminate(): 75 | # if Gps._timer is not None: 76 | # Gps._timer.cancel() 77 | # pass 78 | 79 | """ 80 | from pytrack import Pytrack 81 | from L76GNSS import L76GNSS 82 | py = Pytrack() 83 | l76 = L76GNSS(py, timeout=30) 84 | t0 = time.ticks_ms() 85 | l76.coordinates() 86 | y = time.ticks_ms() - t0 87 | y 88 | """ 89 | -------------------------------------------------------------------------------- /pymesh/pymesh_frozen/lib/msgpack/__init__.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from msgpack._version import version 3 | from msgpack.exceptions import * 4 | 5 | from ucollections import namedtuple 6 | 7 | 8 | class ExtType(namedtuple('ExtType', 'code data')): 9 | """ExtType represents ext type in msgpack.""" 10 | def __new__(cls, code, data): 11 | if not isinstance(code, int): 12 | raise TypeError("code must be int") 13 | if not isinstance(data, bytes): 14 | raise TypeError("data must be bytes") 15 | if not 0 <= code <= 127: 16 | raise ValueError("code must be 0~127") 17 | return super(ExtType, cls).__new__(cls, code, data) 18 | 19 | 20 | import os 21 | from msgpack.fallback import Packer, unpackb, Unpacker 22 | 23 | 24 | def pack(o, stream, **kwargs): 25 | """ 26 | Pack object `o` and write it to `stream` 27 | 28 | See :class:`Packer` for options. 29 | """ 30 | packer = Packer(**kwargs) 31 | stream.write(packer.pack(o)) 32 | 33 | 34 | def packb(o, **kwargs): 35 | """ 36 | Pack object `o` and return packed bytes 37 | 38 | See :class:`Packer` for options. 39 | """ 40 | return Packer(**kwargs).pack(o) 41 | 42 | 43 | def unpack(stream, **kwargs): 44 | """ 45 | Unpack an object from `stream`. 46 | 47 | Raises `ExtraData` when `stream` contains extra bytes. 48 | See :class:`Unpacker` for options. 49 | """ 50 | data = stream.read() 51 | return unpackb(data, **kwargs) 52 | 53 | 54 | # alias for compatibility to simplejson/marshal/pickle. 55 | load = unpack 56 | loads = unpackb 57 | 58 | dump = pack 59 | dumps = packb 60 | -------------------------------------------------------------------------------- /pymesh/pymesh_frozen/lib/msgpack/_version.py: -------------------------------------------------------------------------------- 1 | version = (0, 5, 6) 2 | -------------------------------------------------------------------------------- /pymesh/pymesh_frozen/lib/msgpack/exceptions.py: -------------------------------------------------------------------------------- 1 | class UnpackException(Exception): 2 | """Deprecated. Use Exception instead to catch all exception during unpacking.""" 3 | 4 | 5 | class BufferFull(UnpackException): 6 | pass 7 | 8 | 9 | class OutOfData(UnpackException): 10 | pass 11 | 12 | 13 | class UnpackValueError(UnpackException): 14 | """Deprecated. Use ValueError instead.""" 15 | 16 | 17 | class ExtraData(UnpackValueError): 18 | def __init__(self, unpacked, extra): 19 | self.unpacked = unpacked 20 | self.extra = extra 21 | 22 | def __str__(self): 23 | return "unpack(b) received extra data." 24 | 25 | 26 | class PackException(Exception): 27 | """Deprecated. Use Exception instead to catch all exception during packing.""" 28 | 29 | 30 | class PackValueError(PackException): 31 | """PackValueError is raised when type of input data is supported but it's value is unsupported. 32 | 33 | Deprecated. Use ValueError instead. 34 | """ 35 | 36 | 37 | class PackOverflowError(PackValueError): 38 | """PackOverflowError is raised when integer value is out of range of msgpack support [-2**31, 2**32). 39 | 40 | Deprecated. Use ValueError instead. 41 | """ 42 | -------------------------------------------------------------------------------- /pymesh/pymesh_frozen/lib/pymesh_config.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Copyright (c) 2020, Pycom Limited. 3 | This software is licensed under the GNU GPL version 3 or any 4 | later version, with permitted additional terms. For more information 5 | see the Pycom Licence v1.0 document supplied with this file, or 6 | available at https://www.pycom.io/opensource/licensing 7 | ''' 8 | import json 9 | 10 | from network import LoRa 11 | import machine 12 | import time 13 | 14 | try: 15 | from pymesh_debug import print_debug 16 | except: 17 | from _pymesh_debug import print_debug 18 | 19 | __version__ = '2' 20 | """ 21 | __version__ = '2' 22 | * added BR_ena, BR_prio 23 | 24 | __version__ = '' 25 | * first release 26 | 27 | """ 28 | 29 | class PymeshConfig: 30 | 31 | CONFIG_FILENAME = "/flash/pymesh_config.json" 32 | 33 | ############################################################ 34 | # DEFAULT SETTINGS 35 | 36 | # LoRa region is one of LoRa.US915, LoRa.EU868, LoRa.AS923, LoRa.AU915 37 | LORA_REGION = LoRa.EU868 38 | 39 | # frequency expressed in Hz, for EU868 863000000 Hz, for US915 904600000 Hz 40 | LORA_FREQ = const(869000000) 41 | 42 | # bandwidth options are: LoRa.BW_125KHZ, LoRa.BW_250KHZ or LoRa.BW_500KHZ 43 | LORA_BW = LoRa.BW_500KHZ 44 | 45 | # spreading factor options are 7 to 12 46 | LORA_SF = const(7) 47 | 48 | # Pymesh 128b key, used for auth. and encryption 49 | KEY = "112233" 50 | 51 | # if true, Pymesh is auto-started 52 | AUTOSTART = True 53 | DEBUG_LEVEL = 5 54 | 55 | # if true, it will start as BLE Server, to be connected with mobile app 56 | BLE_API = False 57 | BLE_NAME_PREFIX = "PyGo " 58 | 59 | ############################################################ 60 | # Border router preference priority 61 | BR_PRIORITY_NORM = const(0) 62 | BR_PRIORITY_LOW = const(-1) 63 | BR_PRIORITY_HIGH = const(1) 64 | 65 | # if true then this node is Border Router 66 | BR_ENABLE = False 67 | # the default BR priority 68 | BR_PRIORITY = BR_PRIORITY_NORM 69 | 70 | def write_config(pymesh_config, force_restart = False): 71 | cf = open(PymeshConfig.CONFIG_FILENAME, 'w') 72 | cf.write(json.dumps(pymesh_config)) 73 | cf.close() 74 | 75 | if force_restart: 76 | print_debug(3, "write_config force restart") 77 | time.sleep(1) 78 | machine.deepsleep(1000) 79 | 80 | def check_mac(pymesh_config, MAC): 81 | if pymesh_config.get('MAC') is None: 82 | # if MAC config unspecified, set it to LoRa MAC 83 | print_debug(3, "Set MAC in config file as " + str(MAC)) 84 | pymesh_config['MAC'] = MAC 85 | PymeshConfig.write_config(pymesh_config, False) 86 | else: 87 | mac_from_config = pymesh_config.get('MAC') 88 | if mac_from_config != MAC: 89 | print_debug(3, "MAC different"+ str(mac_from_config) + str(MAC)) 90 | pymesh_config['MAC'] = MAC 91 | # if MAC in config different than LoRa MAC, set LoRa MAC as in config file 92 | fo = open("/flash/sys/lpwan.mac", "wb") 93 | mac_write=bytes([(MAC >> 56) & 0xFF, (MAC >> 48) & 0xFF, (MAC >> 40) & 0xFF, (MAC >> 32) & 0xFF, (MAC >> 24) & 0xFF, (MAC >> 16) & 0xFF, (MAC >> 8) & 0xFF, MAC & 0xFF]) 94 | fo.write(mac_write) 95 | fo.close() 96 | # print_debug(3, "reset") 97 | PymeshConfig.write_config(pymesh_config, False) 98 | 99 | print_debug(3, "MAC ok" + str(MAC)) 100 | 101 | def read_config(MAC): 102 | file = PymeshConfig.CONFIG_FILENAME 103 | pymesh_config = {} 104 | error_file = True 105 | 106 | try: 107 | import json 108 | f = open(file, 'r') 109 | jfile = f.read() 110 | f.close() 111 | try: 112 | pymesh_config = json.loads(jfile.strip()) 113 | # pymesh_config['cfg_msg'] = "Pymesh configuration read from {}".format(file) 114 | error_file = False 115 | except Exception as ex: 116 | print_debug(1, "Error reading {} file!\n Exception: {}".format(file, ex)) 117 | except Exception as ex: 118 | print_debug(1, "Final error reading {} file!\n Exception: {}".format(file, ex)) 119 | 120 | if error_file: 121 | # config file can't be read, so it needs to be created and saved 122 | pymesh_config = {} 123 | print_debug(3, "Can't find" +str(file) + ", or can't be parsed as json; Set default settings and reset") 124 | # don't write MAC, just to use the hardware one 125 | pymesh_config['LoRa'] = {"region": PymeshConfig.LORA_REGION, 126 | "freq": PymeshConfig.LORA_FREQ, 127 | "bandwidth": PymeshConfig.LORA_BW, 128 | "sf": PymeshConfig.LORA_SF} 129 | pymesh_config['Pymesh'] = {"key": PymeshConfig.KEY} 130 | pymesh_config['autostart'] = PymeshConfig.AUTOSTART 131 | pymesh_config['debug'] = PymeshConfig.DEBUG_LEVEL 132 | pymesh_config['ble_api'] = PymeshConfig.BLE_API 133 | pymesh_config['ble_name_prefix'] = PymeshConfig.BLE_NAME_PREFIX 134 | pymesh_config['br_ena'] = PymeshConfig.BR_ENABLE 135 | pymesh_config['br_prio'] = PymeshConfig.BR_PRIORITY 136 | 137 | PymeshConfig.check_mac(pymesh_config, MAC) 138 | print_debug(3, "Default settings:" + str(pymesh_config)) 139 | PymeshConfig.write_config(pymesh_config, True) 140 | 141 | PymeshConfig.check_mac(pymesh_config, MAC) 142 | print_debug(3, "Settings:" + str(pymesh_config)) 143 | return pymesh_config 144 | -------------------------------------------------------------------------------- /pymesh/pymesh_frozen/lib/pymesh_debug.py: -------------------------------------------------------------------------------- 1 | 2 | # Copyright (c) 2020, Pycom Limited. 3 | # 4 | # This software is licensed under the GNU GPL version 3 or any 5 | # later version, with permitted additional terms. For more information 6 | # see the Pycom Licence v1.0 document supplied with this file, or 7 | # available at https://www.pycom.io/opensource/licensing 8 | 9 | import pycom 10 | 11 | # recommended debug levels, from the most verbose to off 12 | DEBUG_DEBG = const(5) 13 | DEBUG_INFO = const(4) 14 | DEBUG_NOTE = const(3) 15 | DEBUG_WARN = const(2) 16 | DEBUG_CRIT = const(1) 17 | DEBUG_NONE = const(0) 18 | 19 | try: 20 | DEBUG = pycom.nvs_get('pymesh_debug') 21 | except: 22 | DEBUG = None 23 | 24 | 25 | def print_debug(level, msg): 26 | """Print log messages into console.""" 27 | if DEBUG is not None and level <= DEBUG: 28 | print(msg) 29 | 30 | def debug_level(level): 31 | global DEBUG 32 | if level is None: 33 | try: 34 | ret = pycom.nvs_get('pymesh_debug') 35 | except: 36 | ret = None 37 | return ret 38 | try: 39 | ret = int(level) 40 | except: 41 | return 42 | 43 | DEBUG = ret 44 | pycom.nvs_set('pymesh_debug', DEBUG) 45 | -------------------------------------------------------------------------------- /pymesh/pymesh_frozen/lorawan/lorawan.py: -------------------------------------------------------------------------------- 1 | """ OTAA Node example compatible with the LoPy Nano Gateway """ 2 | 3 | from network import LoRa 4 | from network import WLAN 5 | import socket 6 | import binascii 7 | import struct 8 | import time 9 | from machine import RTC 10 | 11 | class Lorawan: 12 | 13 | def __init__(self): 14 | # create an OTA authentication params 15 | self.dev_eui = binascii.unhexlify('007926C9EAE4C922') 16 | self.app_eui = binascii.unhexlify('70B3D57ED001D8C8') 17 | self.app_key = binascii.unhexlify('2C4D6AE9CEBA8B0EB4430C33C17750CB') 18 | 19 | def send(self): 20 | t0 = time.time() 21 | print("LoRaWAN start") 22 | lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.EU868) 23 | 24 | # set the 3 default channels to the same frequency (must be before sending the OTAA join request) 25 | lora.add_channel(0, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=5) 26 | lora.add_channel(1, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=5) 27 | lora.add_channel(2, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=5) 28 | 29 | # join a network using OTAA 30 | lora.join(activation=LoRa.OTAA, auth=(self.dev_eui, self.app_eui, self.app_key), timeout=0, dr=config.LORA_NODE_DR) 31 | 32 | # wait until the module has joined the network 33 | while not lora.has_joined(): 34 | time.sleep(2.5) 35 | print('Not joined yet...', time.localtime()) 36 | 37 | # remove all the non-default channels 38 | for i in range(3, 16): 39 | lora.remove_channel(i) 40 | 41 | # create a LoRa socket 42 | s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 43 | 44 | # set the LoRaWAN data rate 45 | s.setsockopt(socket.SOL_LORA, socket.SO_DR, config.LORA_NODE_DR) 46 | 47 | # make the socket non-blocking 48 | s.setblocking(False) 49 | 50 | pkt = b'PKT #' + bytes([i]) 51 | print('Sending:', pkt) 52 | s.send(pkt) 53 | print("LoRaWAN done in ", time.time() - t0, " seconds") 54 | 55 | class config: 56 | # for EU868 57 | LORA_FREQUENCY = 867500000 58 | LORA_GW_DR = "SF7BW125" # DR_5 59 | LORA_NODE_DR = 5 60 | -------------------------------------------------------------------------------- /pymesh/pymesh_frozen/lorawan/main.py: -------------------------------------------------------------------------------- 1 | import time 2 | import ubinascii 3 | import pycom 4 | from network import LoRa 5 | 6 | try: 7 | from pymesh_config import PymeshConfig 8 | except: 9 | from _pymesh_config import PymeshConfig 10 | 11 | try: 12 | from pymesh import Pymesh 13 | except: 14 | from _pymesh import Pymesh 15 | 16 | def new_message_cb(rcv_ip, rcv_port, rcv_data): 17 | ''' callback triggered when a new packet arrived ''' 18 | print('Incoming %d bytes from %s (port %d):' % 19 | (len(rcv_data), rcv_ip, rcv_port)) 20 | print(rcv_data) 21 | 22 | # user code to be inserted, to send packet to the designated Mesh-external interface 23 | for _ in range(3): 24 | pycom.rgbled(0x888888) 25 | time.sleep(.2) 26 | pycom.rgbled(0) 27 | time.sleep(.1) 28 | return 29 | 30 | 31 | pycom.heartbeat(False) 32 | 33 | lora = LoRa(mode=LoRa.LORA, region= LoRa.EU868) 34 | lora_mac = int(str(ubinascii.hexlify(lora.mac()))[2:-1], 16) 35 | 36 | # read config file, or set default values 37 | pymesh_config = PymeshConfig.read_config(lora_mac) 38 | 39 | #initialize Pymesh 40 | pymesh = Pymesh(pymesh_config, new_message_cb) 41 | 42 | while not pymesh.is_connected(): 43 | print(pymesh.status_str()) 44 | time.sleep(3) 45 | 46 | # send message to the Node having MAC address 5 47 | pymesh.send_mess(20, "Hello World") 48 | 49 | print("done Pymesh init, forever loop, exit/stop with Ctrl+C multiple times") 50 | 51 | from lorawan import Lorawan 52 | lorawan = Lorawan() 53 | t0 = time.time() 54 | 55 | while True: 56 | if time.time() - t0 > 60: 57 | pymesh.pause() 58 | 59 | lorawan.send() 60 | 61 | pymesh.resume() 62 | t0 = time.time() 63 | if time.time() - t0 > 35: 64 | pymesh.send_mess(20, "heloo again, #22 here, " + str(time.time())) 65 | 66 | time.sleep(5) 67 | -------------------------------------------------------------------------------- /pymesh/pymesh_frozen/main-pybytes.py: -------------------------------------------------------------------------------- 1 | import time 2 | import pycom 3 | 4 | # todo: add try/except for checking pybytes object exists 5 | pymesh = pybytes.__pymesh.__pymesh 6 | 7 | print("Set maximum debug level, disable debug using pymesh.debug_level(0)") 8 | pymesh.debug_level(5) 9 | 10 | while not pymesh.is_connected(): 11 | print(pymesh.status_str()) 12 | time.sleep(3) 13 | 14 | print(pymesh.status_str()) 15 | # send message to the Node having MAC address 5 16 | pymesh.send_mess(18, "Hello World") 17 | 18 | print("done Pymesh init, CLI is started, h - help/command list, stop - CLI will be stopped") 19 | pymesh.cli_start() 20 | 21 | # send a packet to Pybytes, thru the BR (if any enabled) 22 | # pybytes.send_signal(100, "Hello from device" + str(pymesh.mac())) 23 | -------------------------------------------------------------------------------- /pymesh/pymesh_frozen/main.py: -------------------------------------------------------------------------------- 1 | import time 2 | import ubinascii 3 | import pycom 4 | from network import LoRa 5 | 6 | try: 7 | from pymesh_config import PymeshConfig 8 | except: 9 | from _pymesh_config import PymeshConfig 10 | 11 | try: 12 | from pymesh import Pymesh 13 | except: 14 | from _pymesh import Pymesh 15 | 16 | def new_message_cb(rcv_ip, rcv_port, rcv_data): 17 | ''' callback triggered when a new packet arrived ''' 18 | print('Incoming %d bytes from %s (port %d):' % 19 | (len(rcv_data), rcv_ip, rcv_port)) 20 | print(rcv_data) 21 | 22 | # user code to be inserted, to send packet to the designated Mesh-external interface 23 | for _ in range(3): 24 | pycom.rgbled(0x888888) 25 | time.sleep(.2) 26 | pycom.rgbled(0) 27 | time.sleep(.1) 28 | return 29 | 30 | 31 | pycom.heartbeat(False) 32 | 33 | lora = LoRa(mode=LoRa.LORA, region= LoRa.EU868) 34 | lora_mac = int(str(ubinascii.hexlify(lora.mac()))[2:-1], 16) 35 | 36 | # read config file, or set default values 37 | pymesh_config = PymeshConfig.read_config(lora_mac) 38 | 39 | #initialize Pymesh 40 | pymesh = Pymesh(pymesh_config, new_message_cb) 41 | 42 | # mac = pymesh.mac() 43 | # if mac > 10: 44 | # pymesh.end_device(True) 45 | # elif mac == 5: 46 | # pymesh.leader_priority(255) 47 | 48 | while not pymesh.is_connected(): 49 | print(pymesh.status_str()) 50 | time.sleep(3) 51 | 52 | # send message to the Node having MAC address 5 53 | pymesh.send_mess(5, "Hello World") 54 | 55 | # def new_br_message_cb(rcv_ip, rcv_port, rcv_data, dest_ip, dest_port): 56 | # ''' callback triggered when a new packet arrived for the current Border Router, 57 | # having destination an IP which is external from Mesh ''' 58 | # print('Incoming %d bytes from %s (port %d), to external IPv6 %s (port %d)' % 59 | # (len(rcv_data), rcv_ip, rcv_port, dest_ip, dest_port)) 60 | # print(rcv_data) 61 | 62 | # # user code to be inserted, to send packet to the designated Mesh-external interface 63 | # # ... 64 | # return 65 | 66 | # add current node as Border Router, with a priority and a message handler callback 67 | # pymesh.br_set(PymeshConfig.BR_PRIORITY_NORM, new_br_message_cb) 68 | 69 | # remove Border Router function from current node 70 | # pymesh.br_remove() 71 | 72 | # send data for Mesh-external, basically to the BR 73 | # ip = "1:2:3::4" 74 | # port = 5555 75 | # pymesh.send_mess_external(ip, port, "Hello World") 76 | 77 | print("done Pymesh init, CLI is started, h - help/command list, stop - CLI will be stopped") 78 | pymesh.cli_start() 79 | 80 | # while True: 81 | # time.sleep(3) 82 | -------------------------------------------------------------------------------- /pymesh/pymesh_frozen/main_BR.py: -------------------------------------------------------------------------------- 1 | import time 2 | import ubinascii 3 | import pycom 4 | from network import LoRa 5 | 6 | # 2 = test pybytes OTA feature 7 | # 4 = added device_id (pybytes token) in the packets to BR 8 | __VERSION__ = 4 9 | 10 | try: 11 | from pymesh_config import PymeshConfig 12 | except: 13 | from _pymesh_config import PymeshConfig 14 | 15 | try: 16 | from pymesh import Pymesh 17 | except: 18 | from _pymesh import Pymesh 19 | 20 | PACK_TOCKEN_PREFIX = "tkn" 21 | PACK_TOCKEN_SEP = "#" 22 | 23 | print("Scripts version ", __VERSION__) 24 | 25 | if 'pybytes' not in globals(): 26 | pybytes = None 27 | 28 | def new_message_cb(rcv_ip, rcv_port, rcv_data): 29 | ''' callback triggered when a new packet arrived ''' 30 | print('Incoming %d bytes from %s (port %d):' % 31 | (len(rcv_data), rcv_ip, rcv_port)) 32 | print(rcv_data) 33 | 34 | # user code to be inserted, to send packet to the designated Mesh-external interface 35 | for _ in range(3): 36 | pycom.rgbled(0x888888) 37 | time.sleep(.2) 38 | pycom.rgbled(0) 39 | time.sleep(.1) 40 | return 41 | 42 | def new_br_message_cb(rcv_ip, rcv_port, rcv_data, dest_ip, dest_port): 43 | ''' callback triggered when a new packet arrived for the current Border Router, 44 | having destination an IP which is external from Mesh ''' 45 | print('Incoming %d bytes from %s (port %d), to external IPv6 %s (port %d)' % 46 | (len(rcv_data), rcv_ip, rcv_port, dest_ip, dest_port)) 47 | print(rcv_data) 48 | 49 | for _ in range(2): 50 | pycom.rgbled(0x0) 51 | time.sleep(.1) 52 | # pycom.rgbled(0x001010) 53 | pycom.rgbled(0x663300) 54 | # time.sleep(.2) 55 | 56 | if pybytes is not None and pybytes.isconnected(): 57 | # try to find Pybytes Token if include in rcv_data 58 | token = "" 59 | if rcv_data.startswith(PACK_TOCKEN_PREFIX): 60 | x = rcv_data.split(PACK_TOCKEN_SEP.encode()) 61 | if len(x)>2: 62 | token = x[1] 63 | rcv_data = rcv_data[len(PACK_TOCKEN_PREFIX) + len(token) + len(PACK_TOCKEN_SEP):] 64 | pkt = 'BR %d B from %s (%s), to %s ( %d): %s'%(len(rcv_data), token, rcv_ip, dest_ip, dest_port, str(rcv_data)) 65 | pybytes.send_signal(1, pkt) 66 | 67 | return 68 | 69 | pycom.heartbeat(False) 70 | 71 | lora = LoRa(mode=LoRa.LORA, region= LoRa.EU868) 72 | lora_mac = int(str(ubinascii.hexlify(lora.mac()))[2:-1], 16) 73 | 74 | # read config file, or set default values 75 | pymesh_config = PymeshConfig.read_config(lora_mac) 76 | 77 | #initialize Pymesh 78 | pymesh = Pymesh(pymesh_config, new_message_cb) 79 | 80 | # mac = pymesh.mac() 81 | # if mac > 10: 82 | # pymesh.end_device(True) 83 | # elif mac == 5: 84 | # pymesh.leader_priority(255) 85 | 86 | while not pymesh.is_connected(): 87 | print(pymesh.status_str()) 88 | time.sleep(3) 89 | 90 | # send message to the Node having MAC address 5 91 | pymesh.send_mess(2, "Hello World") 92 | 93 | 94 | print("done Pymesh init, forever loop, exit/stop with Ctrl+C multiple times") 95 | # set BR with callback 96 | if pybytes is not None and pybytes.isconnected(): 97 | pybytes.send_signal(1, "RESTART") 98 | 99 | pyb_port = pymesh.mac() & 0xFFFF 100 | pyb_ip = '1:2:3::' + hex(pyb_port)[2:] 101 | pkt_start = "" 102 | # add pybytes token 103 | if pybytes is not None: 104 | pyb_dev_id = pybytes.get_config().get("device_id", "None") 105 | pkt_start = PACK_TOCKEN_PREFIX + PACK_TOCKEN_SEP + pyb_dev_id + PACK_TOCKEN_SEP 106 | pkt_start = pkt_start + "Hello, from " + str(pymesh.mac()) + ", time " 107 | 108 | 109 | br_enabled = False 110 | 111 | while True: 112 | # add current node as Border Router, with a priority and a message handler callback 113 | 114 | free_mem = pycom.get_free_heap() 115 | 116 | if pymesh_config.get("br_ena", False): 117 | if pybytes is not None and pybytes.isconnected(): 118 | if not br_enabled: 119 | br_enabled = True 120 | print("Set as BR") 121 | pymesh.br_set(PymeshConfig.BR_PRIORITY_NORM, new_br_message_cb) 122 | 123 | pybytes.send_signal(1, str(pymesh.mac()) +" : " + str(time.time()) + "s, "+ str(free_mem)) 124 | print("Send to Pyb,", free_mem) 125 | else: # not connected anymore to pybytes 126 | if br_enabled: 127 | br_enabled = False 128 | print("Remove as BR") 129 | pymesh.br_remove() 130 | else: # not MAC_BR 131 | pkt = pkt_start + str(time.time()) + ", mem " + str(free_mem) 132 | pymesh.send_mess_external(pyb_ip, pyb_port, pkt) 133 | print("Sending to BR: ", pkt) 134 | 135 | time.sleep(20) 136 | -------------------------------------------------------------------------------- /pymesh/readme.md: -------------------------------------------------------------------------------- 1 | # For reference only 2 | 3 | This library is the open-source part of the Pymesh LoRa Firmware which can be provisioned through Pybytes and cannot be used as stand-alone on a Pybytes or Pygate type firmware. Please check the [Pycom Documentation](https://docs.pycom.io/pybytes/pymeshintegration/provisioning/) on how to get Pymesh type firmware provisioned to your device. You will not have to use this library in your project or flash it to your device separately, as it is already included in the firmware. 4 | -------------------------------------------------------------------------------- /shields/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | # Pytrack, Pysense and Pyscan libraries 4 | 5 | ## Introduction 6 | This directory contains libraries and out of the box examples for Pysense, Pytrack, and Pyscan, both versions 1 and 2.0x of these shields. 7 | 8 | 1. Upload all the files to your module 9 | 2. Open Pymakr 10 | 3. Run an example that matches to your shield 11 | 12 | Note: For using Pyscan, you need to upload either MFRC630.mpy or MFRC630.py. 13 | It is always recommended to use MFRC630.mpy as it takes less space on your module. 14 | -------------------------------------------------------------------------------- /shields/lib/L76GNSS.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | from machine import Timer 12 | import time 13 | import gc 14 | import binascii 15 | 16 | 17 | class L76GNSS: 18 | 19 | GPS_I2CADDR = const(0x10) 20 | 21 | def __init__(self, pytrack=None, sda='P22', scl='P21', timeout=None, buffer=64): 22 | if pytrack is not None: 23 | self.i2c = pytrack.i2c 24 | else: 25 | from machine import I2C 26 | self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) 27 | 28 | self.chrono = Timer.Chrono() 29 | 30 | self.timeout = timeout 31 | self.timeout_status = True 32 | self.buffer = buffer 33 | 34 | self.reg = bytearray(1) 35 | self.i2c.writeto(GPS_I2CADDR, self.reg) 36 | 37 | def _read(self): 38 | self.reg = self.i2c.readfrom(GPS_I2CADDR, self.buffer) 39 | return self.reg 40 | 41 | def _convert_coords(self, gngll_s): 42 | lat = gngll_s[1] 43 | lat_d = (float(lat) // 100) + ((float(lat) % 100) / 60) 44 | lon = gngll_s[3] 45 | lon_d = (float(lon) // 100) + ((float(lon) % 100) / 60) 46 | if gngll_s[2] == 'S': 47 | lat_d *= -1 48 | if gngll_s[4] == 'W': 49 | lon_d *= -1 50 | return(lat_d, lon_d) 51 | 52 | def coordinates(self, debug=False): 53 | lat_d, lon_d, debug_timeout = None, None, False 54 | if self.timeout is not None: 55 | self.chrono.reset() 56 | self.chrono.start() 57 | nmea = b'' 58 | while True: 59 | if self.timeout is not None and self.chrono.read() >= self.timeout: 60 | self.chrono.stop() 61 | chrono_timeout = self.chrono.read() 62 | self.chrono.reset() 63 | self.timeout_status = False 64 | debug_timeout = True 65 | if not self.timeout_status: 66 | gc.collect() 67 | break 68 | nmea += self._read().lstrip(b'\n\n').rstrip(b'\n\n') 69 | gngll_idx = nmea.find(b'GNGLL') 70 | gpgll_idx = nmea.find(b'GPGLL') 71 | if gngll_idx < 0 and gpgll_idx >= 0: 72 | gngll_idx = gpgll_idx 73 | if gngll_idx >= 0: 74 | gngll = nmea[gngll_idx:] 75 | e_idx = gngll.find(b'\r\n') 76 | if e_idx >= 0: 77 | try: 78 | gngll = gngll[:e_idx].decode('ascii') 79 | gngll_s = gngll.split(',') 80 | lat_d, lon_d = self._convert_coords(gngll_s) 81 | except Exception: 82 | pass 83 | finally: 84 | nmea = nmea[(gngll_idx + e_idx):] 85 | gc.collect() 86 | break 87 | else: 88 | gc.collect() 89 | if len(nmea) > 410: # i suppose it can be safely changed to 82, which is longest NMEA frame 90 | nmea = nmea[-5:] # $GNGL without last L 91 | time.sleep(0.1) 92 | self.timeout_status = True 93 | if debug and debug_timeout: 94 | print('GPS timed out after %f seconds' % (chrono_timeout)) 95 | return(None, None) 96 | else: 97 | return(lat_d, lon_d) 98 | 99 | def dump_nmea(self): 100 | nmea = b'' 101 | while True: 102 | nmea = self._read().lstrip(b'\n\n').rstrip(b'\n\n') 103 | start_idx = nmea.find(b'$') 104 | #print('raw[{}]: {}'.format(start_idx, nmea)) 105 | if nmea is not None and len(nmea) > 0: 106 | if start_idx != 0: 107 | if len(nmea[:start_idx]) > 1: 108 | print('{}'.format(nmea[:start_idx].decode('ASCII')), end='') 109 | if len(nmea[start_idx:]) > 1: 110 | print('{}'.format(nmea[start_idx:].decode('ASCII')), end='') 111 | 112 | def _checksum(self, nmeadata): 113 | calc_cksum = 0 114 | for s in nmeadata: 115 | calc_cksum ^= ord(s) 116 | return('{:02X}'.format(calc_cksum)) 117 | 118 | def write(self, data): 119 | self.i2c.writeto(GPS_I2CADDR, '${}*{}\r\n'.format(data, self._checksum(data)) ) 120 | -------------------------------------------------------------------------------- /shields/lib/LIS2HH12.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2020, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | import math 12 | import time 13 | import struct 14 | from machine import Pin 15 | 16 | 17 | FULL_SCALE_2G = const(0) 18 | FULL_SCALE_4G = const(2) 19 | FULL_SCALE_8G = const(3) 20 | 21 | ODR_POWER_DOWN = const(0) 22 | ODR_10_HZ = const(1) 23 | ODR_50_HZ = const(2) 24 | ODR_100_HZ = const(3) 25 | ODR_200_HZ = const(4) 26 | ODR_400_HZ = const(5) 27 | ODR_800_HZ = const(6) 28 | 29 | ACC_G_DIV = 1000 * 65536 30 | 31 | 32 | class LIS2HH12: 33 | 34 | ACC_I2CADDR = const(30) 35 | 36 | PRODUCTID_REG = const(0x0F) 37 | CTRL1_REG = const(0x20) 38 | CTRL2_REG = const(0x21) 39 | CTRL3_REG = const(0x22) 40 | CTRL4_REG = const(0x23) 41 | CTRL5_REG = const(0x24) 42 | ACC_X_L_REG = const(0x28) 43 | ACC_X_H_REG = const(0x29) 44 | ACC_Y_L_REG = const(0x2A) 45 | ACC_Y_H_REG = const(0x2B) 46 | ACC_Z_L_REG = const(0x2C) 47 | ACC_Z_H_REG = const(0x2D) 48 | ACT_THS = const(0x1E) 49 | ACT_DUR = const(0x1F) 50 | 51 | SCALES = {FULL_SCALE_2G: 4000, FULL_SCALE_4G: 8000, FULL_SCALE_8G: 16000} 52 | ODRS = [0, 10, 50, 100, 200, 400, 800] 53 | 54 | def __init__(self, pysense = None, sda = 'P22', scl = 'P21'): 55 | if pysense is not None: 56 | self.i2c = pysense.i2c 57 | else: 58 | from machine import I2C 59 | self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) 60 | 61 | self.odr = 0 62 | self.full_scale = 0 63 | self.x = 0 64 | self.y = 0 65 | self.z = 0 66 | self.int_pin = None 67 | self.act_dur = 0 68 | self.debounced = False 69 | 70 | whoami = self.i2c.readfrom_mem(ACC_I2CADDR , PRODUCTID_REG, 1) 71 | if (whoami[0] != 0x41): 72 | raise ValueError("LIS2HH12 not found") 73 | 74 | # enable acceleration readings at 50Hz 75 | self.set_odr(ODR_50_HZ) 76 | 77 | # change the full-scale to 4g 78 | self.set_full_scale(FULL_SCALE_4G) 79 | 80 | # set the interrupt pin as active low and open drain 81 | self.set_register(CTRL5_REG, 3, 0, 3) 82 | 83 | # make a first read 84 | self.acceleration() 85 | 86 | def acceleration(self): 87 | x = self.i2c.readfrom_mem(ACC_I2CADDR , ACC_X_L_REG, 2) 88 | self.x = struct.unpack(' self.SCALES[self.full_scale]: 128 | error = "threshold %d exceeds full scale %d" % (threshold, self.SCALES[self.full_scale]) 129 | print(error) 130 | raise ValueError(error) 131 | 132 | if threshold < self.SCALES[self.full_scale] / 128: 133 | error = "threshold %d below resolution %d" % (threshold, self.SCALES[self.full_scale]/128) 134 | print(error) 135 | raise ValueError(error) 136 | 137 | if duration > 255 * 1000 * 8 / self.ODRS[self.odr]: 138 | error = "duration %d exceeds max possible value %d" % (duration, 255 * 1000 * 8 / self.ODRS[self.odr]) 139 | print(error) 140 | raise ValueError(error) 141 | 142 | if duration < 1000 * 8 / self.ODRS[self.odr]: 143 | error = "duration %d below resolution %d" % (duration, 1000 * 8 / self.ODRS[self.odr]) 144 | print(error) 145 | raise ValueError(error) 146 | 147 | _ths = int(127 * threshold / self.SCALES[self.full_scale]) & 0x7F 148 | _dur = int((duration * self.ODRS[self.odr]) / 1000 / 8) 149 | 150 | self.i2c.writeto_mem(ACC_I2CADDR, ACT_THS, _ths) 151 | self.i2c.writeto_mem(ACC_I2CADDR, ACT_DUR, _dur) 152 | 153 | # enable the activity/inactivity interrupt 154 | self.set_register(CTRL3_REG, 1, 5, 1) 155 | 156 | self._user_handler = handler 157 | self.int_pin = Pin('P13', mode=Pin.IN) 158 | self.int_pin.callback(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=self._int_handler) 159 | 160 | # return actual used threshold and duration 161 | return (_ths * self.SCALES[self.full_scale] / 128, _dur * 8 * 1000 / self.ODRS[self.odr]) 162 | 163 | def activity(self): 164 | if not self.debounced: 165 | time.sleep_ms(self.act_dur) 166 | self.debounced = True 167 | if self.int_pin(): 168 | return True 169 | return False 170 | 171 | def _int_handler(self, pin_o): 172 | if self._user_handler is not None: 173 | self._user_handler(pin_o) 174 | else: 175 | if pin_o(): 176 | print('Activity interrupt') 177 | else: 178 | print('Inactivity interrupt') 179 | -------------------------------------------------------------------------------- /shields/lib/LTR329ALS01.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2020, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | import time 12 | from machine import I2C 13 | 14 | class LTR329ALS01: 15 | ALS_I2CADDR = const(0x29) # The device's I2C address 16 | 17 | ALS_CONTR_REG = const(0x80) 18 | ALS_MEAS_RATE_REG = const(0x85) 19 | 20 | ALS_DATA_CH1_LOW = const(0x88) 21 | ALS_DATA_CH1_HIGH = const(0x89) 22 | ALS_DATA_CH0_LOW = const(0x8A) 23 | ALS_DATA_CH0_HIGH = const(0x8B) 24 | 25 | ALS_GAIN_1X = const(0x00) 26 | ALS_GAIN_2X = const(0x01) 27 | ALS_GAIN_4X = const(0x02) 28 | ALS_GAIN_8X = const(0x03) 29 | ALS_GAIN_48X = const(0x06) 30 | ALS_GAIN_96X = const(0x07) 31 | ALS_GAIN_VALUES = { 32 | ALS_GAIN_1X: 1, 33 | ALS_GAIN_2X: 2, 34 | ALS_GAIN_4X: 4, 35 | ALS_GAIN_8X: 8, 36 | ALS_GAIN_48X: 48, 37 | ALS_GAIN_96X: 96 38 | } 39 | 40 | ALS_INT_50 = const(0x01) 41 | ALS_INT_100 = const(0x00) 42 | ALS_INT_150 = const(0x04) 43 | ALS_INT_200 = const(0x02) 44 | ALS_INT_250 = const(0x05) 45 | ALS_INT_300 = const(0x06) 46 | ALS_INT_350 = const(0x07) 47 | ALS_INT_400 = const(0x03) 48 | ALS_INT_VALUES = { 49 | ALS_INT_50: 0.5, 50 | ALS_INT_100: 1, 51 | ALS_INT_150: 1.5, 52 | ALS_INT_200: 2, 53 | ALS_INT_250: 2.5, 54 | ALS_INT_300: 3, 55 | ALS_INT_350: 3.5, 56 | ALS_INT_400: 4 57 | } 58 | 59 | ALS_RATE_50 = const(0x00) 60 | ALS_RATE_100 = const(0x01) 61 | ALS_RATE_200 = const(0x02) 62 | ALS_RATE_500 = const(0x03) 63 | ALS_RATE_1000 = const(0x04) 64 | ALS_RATE_2000 = const(0x05) 65 | 66 | def __init__(self, pysense = None, sda = 'P22', scl = 'P21', gain = ALS_GAIN_1X, integration = ALS_INT_100, rate = ALS_RATE_500): 67 | if pysense is not None: 68 | self.i2c = pysense.i2c 69 | else: 70 | self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) 71 | 72 | self.gain = gain 73 | self.integration = integration 74 | 75 | contr = self._getContr(gain) 76 | self.i2c.writeto_mem(ALS_I2CADDR, ALS_CONTR_REG, bytearray([contr])) 77 | 78 | measrate = self._getMeasRate(integration, rate) 79 | self.i2c.writeto_mem(ALS_I2CADDR, ALS_MEAS_RATE_REG, bytearray([measrate])) 80 | 81 | time.sleep(0.01) 82 | 83 | def _getContr(self, gain): 84 | return ((gain & 0x07) << 2) + 0x01 85 | 86 | def _getMeasRate(self, integration, rate): 87 | return ((integration & 0x07) << 3) + (rate & 0x07) 88 | 89 | def _getWord(self, high, low): 90 | return ((high & 0xFF) << 8) + (low & 0xFF) 91 | 92 | def light(self): 93 | ch1low = self.i2c.readfrom_mem(ALS_I2CADDR , ALS_DATA_CH1_LOW, 1) 94 | ch1high = self.i2c.readfrom_mem(ALS_I2CADDR , ALS_DATA_CH1_HIGH, 1) 95 | data1 = int(self._getWord(ch1high[0], ch1low[0])) 96 | 97 | ch0low = self.i2c.readfrom_mem(ALS_I2CADDR , ALS_DATA_CH0_LOW, 1) 98 | ch0high = self.i2c.readfrom_mem(ALS_I2CADDR , ALS_DATA_CH0_HIGH, 1) 99 | data0 = int(self._getWord(ch0high[0], ch0low[0])) 100 | 101 | return (data0, data1) 102 | 103 | def lux(self): 104 | # Calculate Lux value from formular in Appendix A of the datasheet 105 | light_level = self.light() 106 | if light_level[0]+light_level[1] > 0: 107 | ratio = light_level[1]/(light_level[0]+light_level[1]) 108 | if ratio < 0.45: 109 | return (1.7743 * light_level[0] + 1.1059 * light_level[1]) / self.ALS_GAIN_VALUES[self.gain] / self.ALS_INT_VALUES[self.integration] 110 | elif ratio < 0.64 and ratio >= 0.45: 111 | return (4.2785 * light_level[0] - 1.9548 * light_level[1]) / self.ALS_GAIN_VALUES[self.gain] / self.ALS_INT_VALUES[self.integration] 112 | elif ratio < 0.85 and ratio >= 0.64: 113 | return (0.5926 * light_level[0] + 0.1185 * light_level[1]) / self.ALS_GAIN_VALUES[self.gain] / self.ALS_INT_VALUES[self.integration] 114 | else: 115 | return 0 116 | else: 117 | return 0 118 | -------------------------------------------------------------------------------- /shields/lib/MFRC630.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pycom/pycom-libraries/75d0e67cb421e0576a3a9677bb0d9d81f27ebdb7/shields/lib/MFRC630.mpy -------------------------------------------------------------------------------- /shields/lib/MFRC630.mpy-1.18: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pycom/pycom-libraries/75d0e67cb421e0576a3a9677bb0d9d81f27ebdb7/shields/lib/MFRC630.mpy-1.18 -------------------------------------------------------------------------------- /shields/lib/MPL3115A2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2020, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | import time 12 | from machine import I2C 13 | 14 | ALTITUDE = const(0) 15 | PRESSURE = const(1) 16 | 17 | class MPL3115A2exception(Exception): 18 | pass 19 | 20 | class MPL3115A2: 21 | MPL3115_I2CADDR = const(0x60) 22 | MPL3115_STATUS = const(0x00) 23 | MPL3115_PRESSURE_DATA_MSB = const(0x01) 24 | MPL3115_PRESSURE_DATA_CSB = const(0x02) 25 | MPL3115_PRESSURE_DATA_LSB = const(0x03) 26 | MPL3115_TEMP_DATA_MSB = const(0x04) 27 | MPL3115_TEMP_DATA_LSB = const(0x05) 28 | MPL3115_DR_STATUS = const(0x06) 29 | MPL3115_DELTA_DATA = const(0x07) 30 | MPL3115_WHO_AM_I = const(0x0c) 31 | MPL3115_FIFO_STATUS = const(0x0d) 32 | MPL3115_FIFO_DATA = const(0x0e) 33 | MPL3115_FIFO_SETUP = const(0x0e) 34 | MPL3115_TIME_DELAY = const(0x10) 35 | MPL3115_SYS_MODE = const(0x11) 36 | MPL3115_INT_SORCE = const(0x12) 37 | MPL3115_PT_DATA_CFG = const(0x13) 38 | MPL3115_BAR_IN_MSB = const(0x14) 39 | MPL3115_P_ARLARM_MSB = const(0x16) 40 | MPL3115_T_ARLARM = const(0x18) 41 | MPL3115_P_ARLARM_WND_MSB = const(0x19) 42 | MPL3115_T_ARLARM_WND = const(0x1b) 43 | MPL3115_P_MIN_DATA = const(0x1c) 44 | MPL3115_T_MIN_DATA = const(0x1f) 45 | MPL3115_P_MAX_DATA = const(0x21) 46 | MPL3115_T_MAX_DATA = const(0x24) 47 | MPL3115_CTRL_REG1 = const(0x26) 48 | MPL3115_CTRL_REG2 = const(0x27) 49 | MPL3115_CTRL_REG3 = const(0x28) 50 | MPL3115_CTRL_REG4 = const(0x29) 51 | MPL3115_CTRL_REG5 = const(0x2a) 52 | MPL3115_OFFSET_P = const(0x2b) 53 | MPL3115_OFFSET_T = const(0x2c) 54 | MPL3115_OFFSET_H = const(0x2d) 55 | 56 | def __init__(self, pysense = None, sda = 'P22', scl = 'P21', mode = PRESSURE): 57 | if pysense is not None: 58 | self.i2c = pysense.i2c 59 | else: 60 | self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) 61 | 62 | self.STA_reg = bytearray(1) 63 | self.mode = mode 64 | 65 | if self.mode is PRESSURE: 66 | self.i2c.writeto_mem(MPL3115_I2CADDR, MPL3115_CTRL_REG1, bytes([0x38])) # barometer mode, not raw, oversampling 128, minimum time 512 ms 67 | self.i2c.writeto_mem(MPL3115_I2CADDR, MPL3115_PT_DATA_CFG, bytes([0x07])) # no events detected 68 | self.i2c.writeto_mem(MPL3115_I2CADDR, MPL3115_CTRL_REG1, bytes([0x39])) # active 69 | elif self.mode is ALTITUDE: 70 | self.i2c.writeto_mem(MPL3115_I2CADDR, MPL3115_CTRL_REG1, bytes([0xB8])) # altitude mode, not raw, oversampling 128, minimum time 512 ms 71 | self.i2c.writeto_mem(MPL3115_I2CADDR, MPL3115_PT_DATA_CFG, bytes([0x07])) # no events detected 72 | self.i2c.writeto_mem(MPL3115_I2CADDR, MPL3115_CTRL_REG1, bytes([0xB9])) # active 73 | else: 74 | raise MPL3115A2exception("Invalid Mode MPL3115A2") 75 | 76 | if self._read_status(): 77 | pass 78 | else: 79 | raise MPL3115A2exception("Error with MPL3115A2") 80 | 81 | def _read_status(self): 82 | read_attempts = 0 83 | while read_attempts < 500: 84 | self.i2c.readfrom_mem_into(MPL3115_I2CADDR, MPL3115_STATUS, self.STA_reg) 85 | read_attempts += 1 86 | 87 | if(self.STA_reg[0] == 0): 88 | time.sleep(0.01) 89 | pass 90 | elif(self.STA_reg[0] & 0x04) == 4: 91 | return True 92 | else: 93 | return False 94 | 95 | # If we get here the sensor isn't responding. Reset it so next time in it should work 96 | self.i2c.writeto_mem(MPL3115_I2CADDR, MPL3115_CTRL_REG1, bytes([0x00])) # put into standby 97 | self.i2c.writeto_mem(MPL3115_I2CADDR, MPL3115_CTRL_REG1, bytes([0x04])) # reset 98 | return False 99 | 100 | def pressure(self): 101 | if self.mode == ALTITUDE: 102 | raise MPL3115A2exception("Incorrect Measurement Mode MPL3115A2") 103 | 104 | OUT_P_MSB = self.i2c.readfrom_mem(MPL3115_I2CADDR, MPL3115_PRESSURE_DATA_MSB,1) 105 | OUT_P_CSB = self.i2c.readfrom_mem(MPL3115_I2CADDR, MPL3115_PRESSURE_DATA_CSB,1) 106 | OUT_P_LSB = self.i2c.readfrom_mem(MPL3115_I2CADDR, MPL3115_PRESSURE_DATA_LSB,1) 107 | 108 | return float((OUT_P_MSB[0] << 10) + (OUT_P_CSB[0] << 2) + ((OUT_P_LSB[0] >> 6) & 0x03) + ((OUT_P_LSB[0] >> 4) & 0x03) / 4.0) 109 | 110 | def altitude(self): 111 | if self.mode == PRESSURE: 112 | raise MPL3115A2exception("Incorrect Measurement Mode MPL3115A2") 113 | 114 | OUT_P_MSB = self.i2c.readfrom_mem(MPL3115_I2CADDR, MPL3115_PRESSURE_DATA_MSB,1) 115 | OUT_P_CSB = self.i2c.readfrom_mem(MPL3115_I2CADDR, MPL3115_PRESSURE_DATA_CSB,1) 116 | OUT_P_LSB = self.i2c.readfrom_mem(MPL3115_I2CADDR, MPL3115_PRESSURE_DATA_LSB,1) 117 | 118 | alt_int = (OUT_P_MSB[0] << 8) + (OUT_P_CSB[0]) 119 | alt_frac = ((OUT_P_LSB[0] >> 4) & 0x0F) 120 | 121 | if alt_int > 32767: 122 | alt_int -= 65536 123 | 124 | return float(alt_int + alt_frac / 16.0) 125 | 126 | def temperature(self): 127 | OUT_T_MSB = self.i2c.readfrom_mem(MPL3115_I2CADDR, MPL3115_TEMP_DATA_MSB,1) 128 | OUT_T_LSB = self.i2c.readfrom_mem(MPL3115_I2CADDR, MPL3115_TEMP_DATA_LSB,1) 129 | 130 | temp_int = OUT_T_MSB[0] 131 | temp_frac = OUT_T_LSB[0] 132 | 133 | if temp_int > 127: 134 | temp_int -= 256 135 | 136 | return float(temp_int + temp_frac / 256.0) 137 | -------------------------------------------------------------------------------- /shields/lib/SI7006A20.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2019, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | import time 12 | from machine import I2C 13 | import math 14 | 15 | __version__ = '0.0.2' 16 | 17 | class SI7006A20: 18 | """ class for handling the temperature sensor SI7006-A20 19 | +/- 1 deg C error for temperature 20 | +/- 5% error for relative humidity 21 | datasheet available at https://www.silabs.com/documents/public/data-sheets/Si7006-A20.pdf """ 22 | 23 | SI7006A20_I2C_ADDR = const(0x40) 24 | 25 | # I2C commands 26 | TEMP_NOHOLDMASTER = const(0xF3) 27 | HUMD_NOHOLDMASTER = const(0xF5) 28 | WRITE_USER_REG1 = const(0xE6) 29 | READ_USER_REG1 = const(0xE7) 30 | WRITE_HEATER_CTRL_REG = const(0x51) 31 | READ_HEATER_CTRL_REG = const(0x11) 32 | 33 | # Register masks and offsets 34 | USER_REG1_HTR_ENABLE_MASK = const(0b00000100) 35 | USER_REG1_HTR_ENABLE_OFFSET = const(0x02) 36 | HTR_CTRL_REG_MASK = const(0b00001111) 37 | 38 | def __init__(self, pysense = None, sda = 'P22', scl = 'P21'): 39 | if pysense is not None: 40 | self.i2c = pysense.i2c 41 | else: 42 | self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) 43 | 44 | def _getWord(self, high, low): 45 | return ((high & 0xFF) << 8) + (low & 0xFF) 46 | 47 | def temperature(self): 48 | """ obtaining the temperature(degrees Celsius) measured by sensor """ 49 | self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([0xF3])) 50 | time.sleep(0.5) 51 | data = self.i2c.readfrom(SI7006A20_I2C_ADDR, 3) 52 | #print("CRC Raw temp data: " + hex(data[0]*65536 + data[1]*256 + data[2])) 53 | data = self._getWord(data[0], data[1]) 54 | temp = ((175.72 * data) / 65536.0) - 46.85 55 | return temp 56 | 57 | def humidity(self): 58 | """ obtaining the relative humidity(%) measured by sensor """ 59 | self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([0xF5])) 60 | time.sleep(0.5) 61 | data = self.i2c.readfrom(SI7006A20_I2C_ADDR, 2) 62 | data = self._getWord(data[0], data[1]) 63 | humidity = ((125.0 * data) / 65536.0) - 6.0 64 | return humidity 65 | 66 | def read_user_reg(self): 67 | """ reading the user configuration register """ 68 | self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([READ_USER_REG1])) 69 | time.sleep(0.5) 70 | data = self.i2c.readfrom(SI7006A20_I2C_ADDR, 1) 71 | return data[0] 72 | 73 | def read_heater_reg(self): 74 | """ reading the heater configuration register """ 75 | self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([READ_HEATER_CTRL_REG])) 76 | time.sleep(0.5) 77 | data = self.i2c.readfrom(SI7006A20_I2C_ADDR, 1) 78 | return data[0] 79 | 80 | def write_heater_reg(self, heater_value): 81 | """ writing the heater configuration register """ 82 | # We should only set the bottom four bits of this register 83 | heater_setting = heater_value & HTR_CTRL_REG_MASK 84 | self.write_reg(WRITE_HEATER_CTRL_REG, heater_setting) 85 | 86 | def heater_control(self, on_off): 87 | """ turn the heater on or off """ 88 | # Get current settings for everything else 89 | user_reg = self.read_user_reg() 90 | # Set the heater bit 91 | user_reg = (user_reg & ~USER_REG1_HTR_ENABLE_MASK) | (on_off << USER_REG1_HTR_ENABLE_OFFSET) 92 | self.write_reg(WRITE_USER_REG1, user_reg) 93 | 94 | def read_electronic_id(self): 95 | """ reading electronic identifier """ 96 | self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([0xFA]) + bytearray([0x0F])) 97 | time.sleep(0.5) 98 | sna = self.i2c.readfrom(SI7006A20_I2C_ADDR, 4) 99 | time.sleep(0.1) 100 | self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([0xFC]) + bytearray([0xC9])) 101 | time.sleep(0.5) 102 | snb = self.i2c.readfrom(SI7006A20_I2C_ADDR, 4) 103 | return [sna[0], sna[1], sna[2], sna[3], snb[0], snb[1], snb[2], snb[3]] 104 | 105 | def read_firmware(self): 106 | """ reading firmware version """ 107 | self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([0x84])+ bytearray([0xB8])) 108 | time.sleep(0.5) 109 | fw = self.i2c.readfrom(SI7006A20_I2C_ADDR, 1) 110 | return fw[0] 111 | 112 | def read_reg(self, reg_addr): 113 | """ reading a register """ 114 | self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([reg_addr])) 115 | time.sleep(0.5) 116 | data = self.i2c.readfrom(SI7006A20_I2C_ADDR, 1) 117 | return data[0] 118 | 119 | def write_reg(self, reg_addr, value): 120 | """ writing a register """ 121 | self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([reg_addr])+bytearray([value])) 122 | time.sleep(0.1) 123 | 124 | def dew_point(self): 125 | """ computing the dew pointe temperature (deg C) for the current Temperature and Humidity measured pair 126 | at dew-point temperature the relative humidity is 100% """ 127 | temp = self.temperature() 128 | humid = self.humidity() 129 | h = (math.log(humid, 10) - 2) / 0.4343 + (17.62 * temp) / (243.12 + temp) 130 | dew_p = 243.12 * h / (17.62 - h) 131 | return dew_p 132 | 133 | def humid_ambient(self, t_ambient, dew_p = None): 134 | """ returns the relative humidity compensated for the current Ambient temperature 135 | for ex: T-Ambient is 24.4 degC, but sensor indicates Temperature = 31.65 degC and Humidity = 47.3% 136 | -> then the actual Relative Humidity is 72.2% 137 | this is computed because the dew-point should be the same """ 138 | if dew_p is None: 139 | dew_p = self.dew_point() 140 | h = 17.62 * dew_p / (243.12 + dew_p) 141 | h_ambient = math.pow(10, (h - (17.62 * t_ambient) / (243.12 + t_ambient)) * 0.4343 + 2) 142 | return h_ambient 143 | -------------------------------------------------------------------------------- /shields/pyscan_1.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Simple Pyscan NFC / MiFare Classic Example 3 | Copyright (c) 2019, Pycom Limited. 4 | 5 | This example continuously sends a REQA for ISO14443A card type 6 | If a card is discovered, it will read the UID 7 | If DECODE_CARD = True, will attempt to authenticate with CARDkey 8 | If authentication succeeds will attempt to read sectors from the card 9 | ''' 10 | 11 | from pycoproc_1 import Pycoproc 12 | from MFRC630 import MFRC630 13 | from LIS2HH12 import LIS2HH12 14 | from LTR329ALS01 import LTR329ALS01 15 | import time 16 | import pycom 17 | 18 | #add your card UID here 19 | VALID_CARDS = [[0x43, 0x95, 0xDD, 0xF8], 20 | [0x43, 0x95, 0xDD, 0xF9], 21 | [0x46, 0x5A, 0xEB, 0x7D, 0x8A, 0x08, 0x04]] 22 | 23 | 24 | # This is the default key for an unencrypted MiFare card 25 | CARDkey = [ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ] 26 | DECODE_CARD = False 27 | 28 | py = Pycoproc(Pycoproc.PYSCAN) 29 | nfc = MFRC630(py) 30 | lt = LTR329ALS01(py) 31 | li = LIS2HH12(py) 32 | 33 | pybytes_enabled = False 34 | if 'pybytes' in globals(): 35 | if(pybytes.isconnected()): 36 | print('Pybytes is connected, sending signals to Pybytes') 37 | pybytes_enabled = True 38 | 39 | RGB_BRIGHTNESS = 0x8 40 | 41 | RGB_RED = (RGB_BRIGHTNESS << 16) 42 | RGB_GREEN = (RGB_BRIGHTNESS << 8) 43 | RGB_BLUE = (RGB_BRIGHTNESS) 44 | 45 | counter = 0 46 | 47 | def check_uid(uid, len): 48 | return VALID_CARDS.count(uid[:len]) 49 | 50 | def send_sensor_data(name, timeout): 51 | if(pybytes_enabled): 52 | while(True): 53 | pybytes.send_signal(2, lt.light()) 54 | pybytes.send_signal(3, li.acceleration()) 55 | time.sleep(timeout) 56 | 57 | # Make sure heartbeat is disabled before setting RGB LED 58 | pycom.heartbeat(False) 59 | 60 | # Initialise the MFRC630 with some settings 61 | nfc.mfrc630_cmd_init() 62 | 63 | print('Scanning for cards') 64 | while True: 65 | # Send REQA for ISO14443A card type 66 | atqa = nfc.mfrc630_iso14443a_WUPA_REQA(nfc.MFRC630_ISO14443_CMD_REQA) 67 | if (atqa != 0): 68 | # A card has been detected, read UID 69 | print('A card has been detected, reading its UID ...') 70 | uid = bytearray(10) 71 | uid_len = nfc.mfrc630_iso14443a_select(uid) 72 | print('UID has length {}'.format(uid_len)) 73 | if (uid_len > 0): 74 | # A valid UID has been detected, print details 75 | counter += 1 76 | print("%d\tUID [%d]: %s" % (counter, uid_len, nfc.format_block(uid, uid_len))) 77 | if DECODE_CARD: 78 | # Try to authenticate with CARD key 79 | nfc.mfrc630_cmd_load_key(CARDkey) 80 | for sector in range(0, 16): 81 | if (nfc.mfrc630_MF_auth(uid, nfc.MFRC630_MF_AUTH_KEY_A, sector * 4)): 82 | pycom.rgbled(RGB_GREEN) 83 | # Authentication was sucessful, read card data 84 | readbuf = bytearray(16) 85 | for b in range(0, 4): 86 | f_sect = sector * 4 + b 87 | len = nfc.mfrc630_MF_read_block(f_sect, readbuf) 88 | print("\t\tSector %s: Block: %s: %s" % (nfc.format_block([sector], 1), nfc.format_block([b], 1), nfc.format_block(readbuf, len))) 89 | else: 90 | print("Authentication denied for sector %s!" % nfc.format_block([sector], 1)) 91 | pycom.rgbled(RGB_RED) 92 | # It is necessary to call mfrc630_MF_deauth after authentication 93 | # Although this is also handled by the reset / init cycle 94 | nfc.mfrc630_MF_deauth() 95 | else: 96 | #check if card uid is listed in VALID_CARDS 97 | if (check_uid(list(uid), uid_len)) > 0: 98 | print('Card is listed, turn LED green') 99 | pycom.rgbled(RGB_GREEN) 100 | if(pybytes_enabled): 101 | pybytes.send_signal(1, ('Card is listed', uid)) 102 | else: 103 | print('Card is not listed, turn LED red') 104 | pycom.rgbled(RGB_RED) 105 | if(pybytes_enabled): 106 | pybytes.send_signal(1, ('Unauthorized card detected', uid)) 107 | 108 | else: 109 | pycom.rgbled(RGB_BLUE) 110 | # We could go into power saving mode here... to be investigated 111 | nfc.mfrc630_cmd_reset() 112 | time.sleep(.5) 113 | # Re-Initialise the MFRC630 with settings as these got wiped during reset 114 | nfc.mfrc630_cmd_init() 115 | -------------------------------------------------------------------------------- /shields/pysense_1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2020, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | # See https://docs.pycom.io for more information regarding library specifics 12 | 13 | import time 14 | import pycom 15 | from pycoproc_1 import Pycoproc 16 | import machine 17 | 18 | from LIS2HH12 import LIS2HH12 19 | from SI7006A20 import SI7006A20 20 | from LTR329ALS01 import LTR329ALS01 21 | from MPL3115A2 import MPL3115A2,ALTITUDE,PRESSURE 22 | 23 | pycom.heartbeat(False) 24 | pycom.rgbled(0x0A0A08) # white 25 | 26 | py = Pycoproc(Pycoproc.PYSENSE) 27 | 28 | pybytes_enabled = False 29 | if 'pybytes' in globals(): 30 | if(pybytes.isconnected()): 31 | print('Pybytes is connected, sending signals to Pybytes') 32 | pybytes_enabled = True 33 | 34 | mp = MPL3115A2(py,mode=ALTITUDE) # Returns height in meters. Mode may also be set to PRESSURE, returning a value in Pascals 35 | print("MPL3115A2 temperature: " + str(mp.temperature())) 36 | print("Altitude: " + str(mp.altitude())) 37 | mpp = MPL3115A2(py,mode=PRESSURE) # Returns pressure in Pa. Mode may also be set to ALTITUDE, returning a value in meters 38 | print("Pressure: " + str(mpp.pressure())) 39 | 40 | si = SI7006A20(py) 41 | print("Temperature: " + str(si.temperature())+ " deg C and Relative Humidity: " + str(si.humidity()) + " %RH") 42 | print("Dew point: "+ str(si.dew_point()) + " deg C") 43 | t_ambient = 24.4 44 | print("Humidity Ambient for " + str(t_ambient) + " deg C is " + str(si.humid_ambient(t_ambient)) + "%RH") 45 | 46 | 47 | lt = LTR329ALS01(py) 48 | print("Light (channel Blue lux, channel Red lux): " + str(lt.light())) 49 | 50 | li = LIS2HH12(py) 51 | print("Acceleration: " + str(li.acceleration())) 52 | print("Roll: " + str(li.roll())) 53 | print("Pitch: " + str(li.pitch())) 54 | 55 | # set your battery voltage limits here 56 | vmax = 4.2 57 | vmin = 3.3 58 | battery_voltage = py.read_battery_voltage() 59 | battery_percentage = (battery_voltage - vmin / (vmax - vmin))*100 60 | print("Battery voltage: " + str(py.read_battery_voltage()), " percentage: ", battery_percentage) 61 | if(pybytes_enabled): 62 | pybytes.send_signal(1, mpp.pressure()) 63 | pybytes.send_signal(2, si.temperature()) 64 | pybytes.send_signal(3, lt.light()) 65 | pybytes.send_signal(4, li.acceleration()) 66 | pybytes.send_battery_level(int(battery_percentage)) 67 | print("Sent data to pybytes") 68 | 69 | time.sleep(5) 70 | py.setup_sleep(10) 71 | py.go_to_sleep() 72 | -------------------------------------------------------------------------------- /shields/pysense_2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2020, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | # See https://docs.pycom.io for more information regarding library specifics 12 | 13 | import time 14 | import pycom 15 | from pycoproc_2 import Pycoproc 16 | import machine 17 | 18 | from LIS2HH12 import LIS2HH12 19 | from SI7006A20 import SI7006A20 20 | from LTR329ALS01 import LTR329ALS01 21 | from MPL3115A2 import MPL3115A2,ALTITUDE,PRESSURE 22 | 23 | pycom.heartbeat(False) 24 | pycom.rgbled(0x0A0A08) # white 25 | 26 | py = Pycoproc() 27 | if py.read_product_id() != Pycoproc.USB_PID_PYSENSE: 28 | raise Exception('Not a Pysense') 29 | 30 | pybytes_enabled = False 31 | if 'pybytes' in globals(): 32 | if(pybytes.isconnected()): 33 | print('Pybytes is connected, sending signals to Pybytes') 34 | pybytes_enabled = True 35 | 36 | mp = MPL3115A2(py,mode=ALTITUDE) # Returns height in meters. Mode may also be set to PRESSURE, returning a value in Pascals 37 | print("MPL3115A2 temperature: " + str(mp.temperature())) 38 | print("Altitude: " + str(mp.altitude())) 39 | mpp = MPL3115A2(py,mode=PRESSURE) # Returns pressure in Pa. Mode may also be set to ALTITUDE, returning a value in meters 40 | print("Pressure: " + str(mpp.pressure())) 41 | 42 | 43 | si = SI7006A20(py) 44 | print("Temperature: " + str(si.temperature())+ " deg C and Relative Humidity: " + str(si.humidity()) + " %RH") 45 | print("Dew point: "+ str(si.dew_point()) + " deg C") 46 | t_ambient = 24.4 47 | print("Humidity Ambient for " + str(t_ambient) + " deg C is " + str(si.humid_ambient(t_ambient)) + "%RH") 48 | 49 | 50 | lt = LTR329ALS01(py) 51 | print("Light (channel Blue, channel Red): " + str(lt.light())," Lux: ", str(lt.lux()), "lx") 52 | 53 | li = LIS2HH12(py) 54 | print("Acceleration: " + str(li.acceleration())) 55 | print("Roll: " + str(li.roll())) 56 | print("Pitch: " + str(li.pitch())) 57 | 58 | print("Battery voltage: " + str(py.read_battery_voltage())) 59 | 60 | # set your battery voltage limits here 61 | vmax = 4.2 62 | vmin = 3.3 63 | battery_voltage = py.read_battery_voltage() 64 | battery_percentage = (battery_voltage - vmin / (vmax - vmin))*100 65 | print("Battery voltage: " + str(py.read_battery_voltage()), " percentage: ", battery_percentage) 66 | if(pybytes_enabled): 67 | pybytes.send_signal(1, mpp.pressure()) 68 | pybytes.send_signal(2, si.temperature()) 69 | pybytes.send_signal(3, lt.light()) 70 | pybytes.send_signal(4, li.acceleration()) 71 | pybytes.send_battery_level(int(battery_percentage)) 72 | print("Sent data to pybytes") 73 | -------------------------------------------------------------------------------- /shields/pytrack_1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2020, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | import machine 12 | import math 13 | import network 14 | import os 15 | import time 16 | import utime 17 | import gc 18 | from machine import RTC 19 | from machine import SD 20 | from L76GNSS import L76GNSS 21 | from pycoproc_1 import Pycoproc 22 | 23 | time.sleep(2) 24 | gc.enable() 25 | 26 | # setup rtc 27 | rtc = machine.RTC() 28 | rtc.ntp_sync("pool.ntp.org") 29 | utime.sleep_ms(750) 30 | print('\nRTC Set from NTP to UTC:', rtc.now()) 31 | utime.timezone(7200) 32 | print('Adjusted from UTC to EST timezone', utime.localtime(), '\n') 33 | 34 | py = Pycoproc(Pycoproc.PYTRACK) 35 | l76 = L76GNSS(py, timeout=30) 36 | 37 | pybytes_enabled = False 38 | if 'pybytes' in globals(): 39 | if(pybytes.isconnected()): 40 | print('Pybytes is connected, sending signals to Pybytes') 41 | pybytes_enabled = True 42 | 43 | # sd = SD() 44 | # os.mount(sd, '/sd') 45 | # f = open('/sd/gps-record.txt', 'w') 46 | 47 | while (True): 48 | coord = l76.coordinates() 49 | #f.write("{} - {}\n".format(coord, rtc.now())) 50 | print("{} - {} - {}".format(coord, rtc.now(), gc.mem_free())) 51 | if(pybytes_enabled): 52 | pybytes.send_signal(1, coord) 53 | time.sleep(10) 54 | 55 | """ 56 | # sleep procedure 57 | time.sleep(3) 58 | py.setup_sleep(10) 59 | py.go_to_sleep() 60 | """ 61 | -------------------------------------------------------------------------------- /shields/pytrack_2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2020, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | import machine 12 | import math 13 | import network 14 | import os 15 | import time 16 | import utime 17 | import gc 18 | import pycom 19 | from machine import RTC 20 | from machine import SD 21 | from L76GNSS import L76GNSS 22 | from pycoproc_2 import Pycoproc 23 | 24 | pycom.heartbeat(False) 25 | pycom.rgbled(0x0A0A08) # white 26 | 27 | time.sleep(2) 28 | gc.enable() 29 | 30 | # setup rtc 31 | rtc = machine.RTC() 32 | rtc.ntp_sync("pool.ntp.org") 33 | utime.sleep_ms(750) 34 | print('\nRTC Set from NTP to UTC:', rtc.now()) 35 | utime.timezone(7200) 36 | print('Adjusted from UTC to EST timezone', utime.localtime(), '\n') 37 | 38 | py = Pycoproc() 39 | if py.read_product_id() != Pycoproc.USB_PID_PYTRACK: 40 | raise Exception('Not a Pytrack') 41 | 42 | time.sleep(1) 43 | l76 = L76GNSS(py, timeout=30, buffer=512) 44 | 45 | pybytes_enabled = False 46 | if 'pybytes' in globals(): 47 | if(pybytes.isconnected()): 48 | print('Pybytes is connected, sending signals to Pybytes') 49 | pybytes_enabled = True 50 | 51 | # sd = SD() 52 | # os.mount(sd, '/sd') 53 | # f = open('/sd/gps-record.txt', 'w') 54 | 55 | while (True): 56 | coord = l76.coordinates() 57 | #f.write("{} - {}\n".format(coord, rtc.now())) 58 | print("{} - {} - {}".format(coord, rtc.now(), gc.mem_free())) 59 | if(pybytes_enabled): 60 | pybytes.send_signal(1, coord) 61 | time.sleep(10) 62 | 63 | """ 64 | # sleep procedure 65 | time.sleep(3) 66 | py.setup_sleep(10) 67 | py.go_to_sleep() 68 | """ 69 | -------------------------------------------------------------------------------- /shields/shield_2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2020, Pycom Limited. 4 | # 5 | # This software is licensed under the GNU GPL version 3 or any 6 | # later version, with permitted additional terms. For more information 7 | # see the Pycom Licence v1.0 document supplied with this file, or 8 | # available at https://www.pycom.io/opensource/licensing 9 | # 10 | 11 | # This script support Pysense 2 and Pytrack 2 12 | # It demonstrates two examples: 13 | # * go to ultra low power mode (~10uA @3.75V) with all sensors, incl accelerometer and also pycom module (Fipy, Gpy, etc) off - tap the MCLR button for this 14 | # * go to low power mode (~165uA @3.75V) with accelerometer on, pycom module in deepsleep and wake from accelerometer interrupt - hold the MCLR button down for this 15 | 16 | # See https://docs.pycom.io for more information regarding library specifics 17 | 18 | import time 19 | import pycom 20 | import struct 21 | from machine import Pin 22 | from pycoproc_2 import Pycoproc 23 | import machine 24 | from LIS2HH12 import LIS2HH12 25 | 26 | def accelerometer(): 27 | print("ACCELEROMETER:", "accel:", accelerometer_sensor.acceleration(), "roll:", accelerometer_sensor.roll(), "pitch:", accelerometer_sensor.pitch(), "x/y/z:", accelerometer_sensor.x, accelerometer_sensor.y, accelerometer_sensor.z ) 28 | 29 | def activity_int_handler(pin_o): 30 | if pin_o(): 31 | print('[Activity]') 32 | pycom.rgbled(0x00000A) # blue 33 | else: 34 | print('[Inactivity]') 35 | pycom.rgbled(0x0A0A00) # yellow 36 | 37 | def activity_int_handler_none(pin_o): 38 | pass 39 | 40 | def blink(color=0x0a0a0a, ct=5, on_ms=100, off_ms=100 ): 41 | while ct >= 0 : 42 | ct -= 1 43 | pycom.rgbled(color) 44 | time.sleep_ms(on_ms) 45 | pycom.rgbled(0x000000) 46 | time.sleep_ms(off_ms) 47 | 48 | def wait(color=0x0a0a0a, hold_timeout_ms=3000): 49 | print(" - tap MCLR button to go to ultra low power mode (everything off)") 50 | print(" - hold MCLR button down for", round(hold_timeout_ms/1000,1), "sec to go to low power mode and wake from accelerometer") 51 | print("wait for button ...") 52 | ct = 0 53 | pressed_time_ms = 0 54 | dot = '.' 55 | while True: 56 | if pycoproc.button_pressed(): 57 | if pressed_time_ms == 0: 58 | # the button just started to be pressed 59 | pressed_time_ms = time.ticks_ms() 60 | print("button pressed") 61 | pycom.rgbled(color) 62 | dot = '*' 63 | else: 64 | # the button is still being held down 65 | if time.ticks_ms() - pressed_time_ms > hold_timeout_ms: 66 | pycom.rgbled(0) 67 | dot = '_' 68 | else: 69 | if pressed_time_ms != 0: 70 | # the button was released 71 | print("button released") 72 | if time.ticks_ms() - pressed_time_ms > hold_timeout_ms: 73 | return True 74 | else: 75 | return False 76 | time.sleep(0.1) 77 | ct += 1 78 | if ct % 10 == 0: 79 | print(dot, end='') 80 | 81 | def pretty_reset_cause(): 82 | mrc = machine.reset_cause() 83 | print('reset_cause', mrc, end=' ') 84 | if mrc == machine.PWRON_RESET: 85 | print("PWRON_RESET") 86 | # plug in 87 | # press reset button on module 88 | # reset button on JTAG board 89 | # core dump 90 | elif mrc == machine.HARD_RESET: 91 | print("HARD_RESET") 92 | elif mrc == machine.WDT_RESET: 93 | print("WDT_RESET") 94 | # machine.reset() 95 | elif mrc == machine.DEEPSLEEP_RESET: 96 | print("DEEPSLEEP_RESET") 97 | # machine.deepsleep() 98 | elif mrc == machine.SOFT_RESET: 99 | print("SOFT_RESET") 100 | # Ctrl-D 101 | elif mrc == machine.BROWN_OUT_RESET: 102 | print("BROWN_OUT_RESET") 103 | 104 | def pretty_wake_reason(): 105 | mwr = machine.wake_reason() 106 | print("wake_reason", mwr, end=' ') 107 | if mwr[0] == machine.PWRON_WAKE: 108 | print("PWRON_WAKE") 109 | # reset button 110 | elif mwr[0] == machine.PIN_WAKE: 111 | print("PIN_WAKE") 112 | elif mwr[0] == machine.RTC_WAKE: 113 | print("RTC_WAKE") 114 | # from deepsleep 115 | elif mwr[0] == machine.ULP_WAKE: 116 | print("ULP_WAKE") 117 | 118 | 119 | ############################################################### 120 | sleep_time_s = 300 # 5 min 121 | pycom.heartbeat(False) 122 | pycom.rgbled(0x0a0a0a) # white 123 | import binascii 124 | import machine 125 | print(os.uname().sysname.lower() + '-' + binascii.hexlify(machine.unique_id()).decode("utf-8")[-4:], "pysense2") 126 | 127 | pretty_wake_reason() 128 | pretty_reset_cause() 129 | print("pycoproc init") 130 | pycoproc = Pycoproc() 131 | print("battery {:.2f} V".format(pycoproc.read_battery_voltage())) 132 | 133 | # init accelerometer 134 | accelerometer_sensor = LIS2HH12() 135 | # read accelerometer sensor values 136 | accelerometer() 137 | print("enable accelerometer interrupt") 138 | 139 | # enable_activity_interrupt( [mG], [ms], callback) 140 | # accelerometer_sensor.enable_activity_interrupt(8000, 200, activity_int_handler) # low sensitivty 141 | # accelerometer_sensor.enable_activity_interrupt(2000, 200, activity_int_handler) # medium sensitivity 142 | accelerometer_sensor.enable_activity_interrupt( 100, 200, activity_int_handler) # high sensitivity 143 | # accelerometer_sensor.enable_activity_interrupt(63, 160, activity_int_handler) # ultra sensitivty 144 | 145 | if wait(0x0A000A): # purple 146 | print("button was held") 147 | blink(0x000a00) # green 148 | print("enable pycom module to wake up from accelerometer interrupt") 149 | wake_pins = [Pin('P13', mode=Pin.IN, pull=Pin.PULL_DOWN)] 150 | machine.pin_sleep_wakeup(wake_pins, machine.WAKEUP_ANY_HIGH, True) 151 | 152 | print("put pycoproc to sleep and pycom module to deepsleep for", round(sleep_time_s/60,1), "minutes") 153 | pycoproc.setup_sleep(sleep_time_s) 154 | pycoproc.go_to_sleep(pycom_module_off=False, accelerometer_off=False, wake_interrupt=True) 155 | machine.deepsleep(sleep_time_s * 1000) 156 | else: 157 | print("button was tapped") 158 | blink(0x100600) # orange 159 | print("put pycoproc to sleep and turn pycom module off for", round(sleep_time_s/60,1), "minutes") 160 | pycoproc.setup_sleep(sleep_time_s) 161 | pycoproc.go_to_sleep() 162 | 163 | print("we never reach here!") 164 | --------------------------------------------------------------------------------