diff --git a/.gitignore b/.gitignore index beced4e..e943765 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,15 @@ *.conf .DS_Store +pyscan/* +pysense/* +pysense2/* +pytrack/* +pytrack2/* + +pyscan.zip +pysense.zip +pysense2.zip +pytrack.zip +pytrack2.zip + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..17034ef --- /dev/null +++ b/Makefile @@ -0,0 +1,102 @@ +release: pyscan pysense pysense2 pytrack pytrack2 + + +pyscan: + rm -rf pyscan + rm -f pyscan.zip + @echo "Making Pyscan" + mkdir pyscan + mkdir pyscan/lib + #sensors + cp shields/lib/LIS2HH12.py pyscan/lib/ + cp shields/lib/MFRC630.py pyscan/lib/ + cp shields/lib/SI7006A20.py pyscan/lib/ + cp shields/lib/LTR329ALS01.py pyscan/lib/ + #pycoproc + cp shields/lib/pycoproc_1.py pyscan/lib/ + #example + cp shields/pyscan_1.py pyscan/main.py + + zip -r pyscan.zip pyscan + +pysense: + rm -rf pysense + rm -f pysense.zip + @echo "Making Pysense" + mkdir pysense + mkdir pysense/lib + # sensors + cp shields/lib/LIS2HH12.py pysense/lib/ + cp shields/lib/LTR329ALS01.py pysense/lib/ + cp shields/lib/MPL3115A2.py pysense/lib/ + cp shields/lib/SI7006A20.py pysense/lib/ + # pycoproc + cp shields/lib/pycoproc_1.py pysense/lib/ + # example + cp shields/pysense_1.py pysense/main.py + + zip -r pysense.zip pysense + +pysense2: + rm -rf pysense2 + rm -f pysense2.zip + @echo "Making Pysense 2" + mkdir pysense2 + mkdir pysense2/lib + # sensors + cp shields/lib/LIS2HH12.py pysense2/lib/ + cp shields/lib/LTR329ALS01.py pysense2/lib/ + cp shields/lib/MPL3115A2.py pysense2/lib/ + cp shields/lib/SI7006A20.py pysense2/lib/ + # pycoproc + cp shields/lib/pycoproc_2.py pysense2/lib/ + # example + cp shields/pysense_2.py pysense2/main.py + + zip -r pysense2.zip pysense2 + +pytrack: + rm -rf pytrack + rm -f pytrack.zip + @echo "Making Pytrack" + mkdir pytrack + mkdir pytrack/lib + #sensors + cp shields/lib/L76GNSS.py pytrack/lib/ + cp shields/lib/LIS2HH12.py pytrack/lib/ + #pycoproc + cp shields/lib/pycoproc_1.py pytrack/lib/ + #example + cp shields/pytrack_1.py pytrack/main.py + + zip -r pytrack.zip pytrack + +pytrack2: + rm -rf pytrack2 + rm -f pytrack2.zip + @echo "Making Pytrack2" + mkdir pytrack2 + mkdir pytrack2/lib + #sensors + cp shields/lib/L76GNSS.py pytrack2/lib/ + cp shields/lib/LIS2HH12.py pytrack2/lib/ + #pycoproc + cp shields/lib/pycoproc_2.py pytrack2/lib/ + #example + cp shields/pytrack_2.py pytrack2/main.py + + zip -r pytrack2.zip pytrack2 + +clean: + @echo "Cleaning up files" + rm -rf pyscan + rm -rf pysense + rm -rf pysense2 + rm -rf pytrack + rm -rf pytrack2 + + rm -f pyscan.zip + rm -f pysense.zip + rm -f pysense2.zip + rm -f pytrack.zip + rm -f pytrack2.zip \ No newline at end of file diff --git a/README.md b/README.md index 178f449..ce4e46c 100644 --- a/README.md +++ b/README.md @@ -3,19 +3,12 @@ # Pycom Libraries and Examples ## Introduction -This repository contains out of the box examples for Pycom devices, including Pysense, Pytrack, and Pyscan. +This repository contains libraries and out of the box examples for Pycom devices, including the Shields: Pysense, Pytrack, and Pyscan. ## Table of Contents * [Examples](/examples) * [Libraries](/lib) -* [Pysense](/pysense) -* [Pytrack](/pytrack) -* [Pyscan](/pyscan) -* [Pybytes](/pybytes) - -## Pysense, Pytrack, Pyscan - -To install the required libraries for Pysense, Pytrack, and Pyscan, please download this repo as a ZIP file. You will then be able to extract and upload the library files to your Pycom device (via FTP or Pymakr Plugin). Please see [docs.pycom.io](https://docs.pycom.io) for more information. +* [Shields](/shields) ## Links * [Pycom](https://pycom.io) diff --git a/examples/OTA-lorawan/LoraServer.py b/examples/OTA-lorawan/LoraServer.py index 263947d..8382ee1 100644 --- a/examples/OTA-lorawan/LoraServer.py +++ b/examples/OTA-lorawan/LoraServer.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright (c) 2019, Pycom Limited. +# Copyright (c) 2020, Pycom Limited. # # This software is licensed under the GNU GPL version 3 or any # later version, with permitted additional terms. For more information @@ -17,9 +17,9 @@ login_payload = { "password": "string", - "username": "string" + "email": "string" } - + mcGroup_payload = { "multicastGroup": { "dr": 0, @@ -35,7 +35,7 @@ "serviceProfileID": "string" } } - + mcQueue_payload = { "multicastQueueItem": { "data": "string", @@ -44,43 +44,43 @@ "multicastGroupID": "string" } } - + mcAddDevice_payload = { "devEUI": "string", "multicastGroupID": "string" } - + class LoraServerClient: - + def __init__(self): self.server = config.LORASERVER_URL self.port = config.LORASERVER_API_PORT - self.username = config.LORASERVER_USER + self.email = config.LORASERVER_EMAIL self.passwd = config.LORASERVER_PASS - + def login(self): url = self.server + ':' + str(self.port) + '/api/internal/login' - + login_payload["password"] = self.passwd - login_payload["username"] = self.username - + login_payload["email"] = self.email + payload = bytes(json.dumps(login_payload),'utf-8') - + try: r = urllib.request.Request(url, data= payload, method= 'POST') r.add_header("Content-Type", "application/json") r.add_header("Accept", "application/json") - + with urllib.request.urlopen(r) as f: return json.loads(f.read().decode('utf-8'))['jwt'] - + except Exception as ex: print("Error getting the jwt: {}".format(ex)) - + return None - + def parse_service_profile_list(self, response, profile_name): - + try: json_obj = json.loads(response) for sp_obj in json_obj["result"]: @@ -88,52 +88,52 @@ def parse_service_profile_list(self, response, profile_name): return sp_obj["id"] except Exception as ex: print("Error parsing service profile list: {}".format(ex)) - + return None - + def request_service_profile_id(self, profile_name, jwt): url = self.server + ':' + str(self.port) + '/api/service-profiles?limit=100' - + try: r = urllib.request.Request(url, method= 'GET') r.add_header("Accept", "application/json") r.add_header("Grpc-Metadata-Authorization", "Bearer " + jwt) - + with urllib.request.urlopen(r) as f: return self.parse_service_profile_list(f.read().decode('utf-8'), profile_name) - + except Exception as ex: print("Error getting service profile id: {}".format(ex)) - + return None - + def create_multicast_group(self, dr, freq, group_name, serviceProfileID, jwt): - + group_id = self.generate_random_id() mcAddr = self.generate_randon_addr() mcAppSKey = self.generate_random_key() mcNwkSKey = self.generate_random_key() - + url = self.server + ':' + str(self.port) + '/api/multicast-groups' - + mcGroup_payload["multicastGroup"]["dr"] = dr mcGroup_payload["multicastGroup"]["frequency"] = freq mcGroup_payload["multicastGroup"]["id"] = group_id.decode("utf-8") - mcGroup_payload["multicastGroup"]["mcAddr"] = mcAddr.decode("utf-8") - mcGroup_payload["multicastGroup"]["mcAppSKey"] = mcAppSKey.decode("utf-8") - mcGroup_payload["multicastGroup"]["mcNwkSKey"] = mcNwkSKey.decode("utf-8") + mcGroup_payload["multicastGroup"]["mcAddr"] = mcAddr.decode("utf-8") + mcGroup_payload["multicastGroup"]["mcAppSKey"] = mcAppSKey.decode("utf-8") + mcGroup_payload["multicastGroup"]["mcNwkSKey"] = mcNwkSKey.decode("utf-8") mcGroup_payload["multicastGroup"]["name"] = group_name mcGroup_payload["multicastGroup"]["serviceProfileID"] = serviceProfileID - + payload = bytes(json.dumps(mcGroup_payload),'utf-8') - - + + try: r = urllib.request.Request(url, data= payload, method= 'POST') r.add_header("Content-Type", "application/json") r.add_header("Accept", "application/json") r.add_header("Grpc-Metadata-Authorization", "Bearer " + jwt) - + with urllib.request.urlopen(r) as f: resp = f.read().decode('utf-8') if '"id":' in resp: @@ -143,56 +143,56 @@ def create_multicast_group(self, dr, freq, group_name, serviceProfileID, jwt): return None except Exception as ex: print("Error creating multicast data: {}".format(ex)) - + return None - + def delete_multicast_group(self, group_id): - + url = self.server + ':' + str(self.port) + '/api/multicast-groups/' + group_id - + try: r = urllib.request.Request(url, method = 'DELETE') - + with urllib.request.urlopen(r) as f: return f.getcode() == 200 - + except Exception as ex: print("Error deleting multicast group: {}".format(ex)) - + return False - + def add_device_multicast_group(self, devEUI, group_id, jwt): - + url = self.server + ':' + str(self.port) + '/api/multicast-groups/' + group_id + '/devices' - + mcAddDevice_payload["devEUI"] = devEUI mcAddDevice_payload["multicastGroupID"] = group_id - + payload = json.dumps(mcAddDevice_payload).encode('utf-8') - + try: r = urllib.request.Request(url, data= payload, method= 'POST') r.add_header("Content-Type", "application/json") r.add_header("Accept", "application/json") r.add_header("Grpc-Metadata-Authorization", "Bearer " + jwt) - + with urllib.request.urlopen(r) as f: return f.getcode() == 200 - + except Exception as ex: print("Error adding device to multicast group: {}".format(ex)) - + return False - + def request_multicast_keys(self, group_id, jwt): - + url = self.server + ':' + str(self.port) + '/api/multicast-groups/' + group_id - + try: r = urllib.request.Request(url, method= 'GET') r.add_header("Accept", "application/json") r.add_header("Grpc-Metadata-Authorization", "Bearer " + jwt) - + with urllib.request.urlopen(r) as f: resp = f.read().decode('utf-8') if "mcNwkSKey" in resp: @@ -202,27 +202,27 @@ def request_multicast_keys(self, group_id, jwt): return None except Exception as ex: print("Error getting multicast keys: {}".format(ex)) - + return None - + def generate_randon_addr(self): return binascii.hexlify(os.urandom(4)) - + def generate_random_key(self): return binascii.hexlify(os.urandom(16)) - + def generate_random_id(self): return binascii.hexlify(os.urandom(4)) + b'-' + binascii.hexlify(os.urandom(2)) + b'-' + binascii.hexlify(os.urandom(2)) \ + b'-' + binascii.hexlify(os.urandom(2)) + b'-' + binascii.hexlify(os.urandom(6)) - + def multicast_queue_length(self, jwt, multicast_group): url = self.server + ':' + str(self.port) + '/api/multicast-groups/' + multicast_group + '/queue' - + try: r = urllib.request.Request(url, method= 'GET') r.add_header("Content-Type", "application/json") r.add_header("Grpc-Metadata-Authorization", "Bearer " + jwt) - + with urllib.request.urlopen(r) as f: resp = f.read().decode('utf-8') if "multicastQueueItems" in resp: @@ -232,35 +232,31 @@ def multicast_queue_length(self, jwt, multicast_group): return len(json_resp) else: return -1 - + except Exception as ex: print("Error getting multicast queue length: {}".format(ex)) - + return -1 - - + + def send(self, jwt, multicast_group, data): url = self.server + ':' + str(self.port) + '/api/multicast-groups/' + multicast_group + '/queue' - + mcQueue_payload["multicastQueueItem"]["data"] = base64.b64encode(data).decode("utf-8") mcQueue_payload["multicastQueueItem"]["multicastGroupID"] = multicast_group - + payload = bytes(json.dumps(mcQueue_payload),'utf-8') - + try: r = urllib.request.Request(url, data= payload, method= 'POST') r.add_header("Content-Type", "application/json") r.add_header("Accept", "application/json") r.add_header("Grpc-Metadata-Authorization", "Bearer " + jwt) - + with urllib.request.urlopen(r) as f: return f.getcode() == 200 - + except Exception as ex: print("Error sending multicast data: {}".format(ex)) - - return False - - - + return False diff --git a/examples/OTA-lorawan/config.py b/examples/OTA-lorawan/config.py index 31ac593..b4e1239 100644 --- a/examples/OTA-lorawan/config.py +++ b/examples/OTA-lorawan/config.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright (c) 2019, Pycom Limited. +# Copyright (c) 2020, Pycom Limited. # # This software is licensed under the GNU GPL version 3 or any # later version, with permitted additional terms. For more information @@ -13,7 +13,7 @@ LORASERVER_URL = 'http://localhost/' LORASERVER_MQTT_PORT = 1883 LORASERVER_API_PORT = 8080 -LORASERVER_USER = 'admin' +LORASERVER_EMAIL = 'admin' LORASERVER_PASS = 'admin' LORASERVER_SERVICE_PROFILE = 'ota_sp' @@ -23,4 +23,3 @@ #update configuration UPDATE_DELAY = 300 - diff --git a/examples/OTA-lorawan/diff_match_patch.py b/examples/OTA-lorawan/diff_match_patch.py index 0d2f225..7404534 100644 --- a/examples/OTA-lorawan/diff_match_patch.py +++ b/examples/OTA-lorawan/diff_match_patch.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright (c) 2019, Pycom Limited. +# Copyright (c) 2020, Pycom Limited. # # This software is licensed under the GNU GPL version 3 or any # later version, with permitted additional terms. For more information diff --git a/examples/OTA-lorawan/groupUpdater.py b/examples/OTA-lorawan/groupUpdater.py index 2de44df..98bca0a 100644 --- a/examples/OTA-lorawan/groupUpdater.py +++ b/examples/OTA-lorawan/groupUpdater.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright (c) 2019, Pycom Limited. +# Copyright (c) 2020, Pycom Limited. # # This software is licensed under the GNU GPL version 3 or any # later version, with permitted additional terms. For more information diff --git a/examples/OTA-lorawan/ota.py b/examples/OTA-lorawan/ota.py index f6b2642..f9482ad 100644 --- a/examples/OTA-lorawan/ota.py +++ b/examples/OTA-lorawan/ota.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright (c) 2019, Pycom Limited. +# Copyright (c) 2020, Pycom Limited. # # This software is licensed under the GNU GPL version 3 or any # later version, with permitted additional terms. For more information @@ -24,7 +24,7 @@ class OTAHandler: MSG_HEADER = b'$OTA' MSG_TAIL = b'*' MSG_END = b'' - + FULL_UPDATE = b'F' DIFF_UPDATE = b'D' NO_UPDATE = b'N' @@ -51,41 +51,41 @@ def __init__(self): self._latest_version = '0.0.0' self._v_lock = threading.Lock() self.firmware_dir = './firmware' - + self._next_update = -1 self._update_timer = None self._update_delay = config.UPDATE_DELAY self._device_dict = dict() self._keys_dict = dict() - + self._clientApp = LoraServerClient() self._loraserver_jwt = None - + self._service_profile = config.LORASERVER_SERVICE_PROFILE self._downlink_datarate = config.LORASERVER_DOWNLINK_DR self._downlink_freq = config.LORASERVER_DOWNLINK_FREQ - + self._m_th = threading.Thread(target=self._firmware_monitor) self._m_th.start() - + self.multicast_updaters = [] self._updater_lock = threading.Lock() - + def stop(self): self._exit = True def set_mqtt_client(self, client): self.p_client = client - + def _firmware_monitor(self): self._loraserver_jwt = self._clientApp.login() while not self._exit: with self._v_lock: self._latest_version = self._check_version() - + time.sleep(5) - + def process_rx_msg(self, payload): dev_eui = self.get_device_eui(payload) @@ -98,36 +98,36 @@ def process_rx_msg(self, payload): self._send_multicast_keys(dev_eui) elif msg_type == self.LISTENING_MSG: self._send_listening_reply(dev_eui) - + def _send_listening_reply(self, dev_eui): - + msg = bytearray() msg.extend(self.MSG_HEADER) msg.extend(b',' + str(self.LISTENING_REPLY).encode()) msg.extend(b',' + self.MSG_TAIL) - + self.send_payload(dev_eui, msg) - + def _send_multicast_keys(self, dev_eui): - + msg = bytearray() msg.extend(self.MSG_HEADER) msg.extend(b',' + str(self.MULTICAST_KEY_REPLY).encode()) - + if dev_eui in self._device_dict: multicast_param_key = self._device_dict[dev_eui] multicast_param = self._keys_dict[multicast_param_key] - + msg.extend(b',' + multicast_param[1]) msg.extend(b',' + multicast_param[2]) msg.extend(b',' + multicast_param[3]) else: msg.extend(b',,,') - + msg.extend(b',' + self.MSG_TAIL) - + self.send_payload(dev_eui, msg) - + def get_device_eui(self, payload): dev_eui = None try: @@ -136,7 +136,7 @@ def get_device_eui(self, payload): print("Exception extracting device eui") return dev_eui - + def get_msg_type(self, msg): msg_type = -1 @@ -146,7 +146,7 @@ def get_msg_type(self, msg): print("Exception getting message type") return msg_type - + def decode_device_msg(self, payload): dev_msg = None try: @@ -155,28 +155,28 @@ def decode_device_msg(self, payload): except Exception as ex: print("Exception decoding device message") return dev_msg - + def _create_multicast_group(self, update_info): service_id = self._clientApp.request_service_profile_id(self._service_profile, self._loraserver_jwt) - + group_name = update_info.replace(',','-') multicast_param = self._clientApp.create_multicast_group(self._downlink_datarate, self._downlink_freq, group_name, service_id, self._loraserver_jwt) - + return multicast_param - + def _init_update_params(self, dev_eui, dev_version, latest_version): if self._next_update <= 0: self._next_update = int(time.time()) + self._update_delay self._update_timer = threading.Timer(self._update_delay, self.update_proc) self._update_timer.start() - + update_info = dev_version.strip() + ',' + latest_version.strip() self._device_dict[dev_eui] = update_info if update_info not in self._keys_dict: multicast_param = self._create_multicast_group(update_info) self._keys_dict[update_info] = multicast_param self._clientApp.add_device_multicast_group(dev_eui, multicast_param[0], self._loraserver_jwt) - + def _send_update_info(self, dev_eui, msg): print(msg) dev_version = self.get_device_version(msg) @@ -188,7 +188,7 @@ def _send_update_info(self, dev_eui, msg): self._init_update_params(dev_eui, dev_version, version) msg = self._create_update_info_msg(version, dev_version) self.send_payload(dev_eui, msg) - + def get_device_version(self, msg): dev_version = None try: @@ -197,7 +197,7 @@ def get_device_version(self, msg): print("Exception extracting device version") return dev_version - + def _check_version(self): latest = '0.0.0' for d in os.listdir(self.firmware_dir): @@ -205,47 +205,47 @@ def _check_version(self): continue if latest is None or LooseVersion(latest) < LooseVersion(d): latest = d - + return latest - + def get_latest_version(self): with self._v_lock: return self._latest_version - + def is_empty_multicast_queue(self, jwt, multicast_group_id): queue_length =self._clientApp.multicast_queue_length(jwt, multicast_group_id) if queue_length > 0: return False else: return True - + def clear_multicast_group(self, dict_key): with self._updater_lock: if dict_key in self._keys_dict: group_id = self._keys_dict[dict_key][0] self._clientApp.delete_multicast_group(group_id) del self._keys_dict[dict_key] - + self._device_dict = {key:val for key, val in self._device_dict.items() if val != dict_key} - + for updater in self.multicast_updaters: if updater.tag == dict_key: self.multicast_updaters.remove(updater) - + if len(self.multicast_updaters) == 0: self._next_update = -1 self._update_timer = None - + def update_proc(self): - + for dict_key in self._keys_dict: dev_version = dict_key.split(',')[0] latest_version = dict_key.split(',')[1] multicast_group_id = self._keys_dict[dict_key][0] upater = updateHandler(dev_version, latest_version, self._clientApp, self._loraserver_jwt, multicast_group_id, self) - + self.multicast_updaters.append(upater) - + def _get_update_type(self, need_updating, device_version): update_type = b',' + self.NO_UPDATE print(os.path.isdir(self.firmware_dir + '/' + device_version)) @@ -254,9 +254,9 @@ def _get_update_type(self, need_updating, device_version): return b',' + self.DIFF_UPDATE else: return b',' + self.FULL_UPDATE - + return update_type - + def _create_update_info_msg(self, version, device_version): msg = bytearray() msg.extend(self.MSG_HEADER) @@ -272,8 +272,10 @@ def _create_update_info_msg(self, version, device_version): msg.extend(b',' + str(int(time.time())).encode()) msg.extend(b',' + self.MSG_TAIL) return msg - + def send_payload(self, dev_eui, data): b64Data = base64.b64encode(data) payload = '{"reference": "abcd1234" ,"fPort":1,"data": "' + b64Data.decode() + '"}' - self.p_client.publish(topic="application/" + str(config.LORASERVER_APP_ID) + "/device/" + dev_eui + "/tx",payload=payload) + topic = "application/" + str(config.LORASERVER_APP_ID) + "/device/" + dev_eui + "/command/down" + print('Publish to {} : {}'.format(topic, payload)) + self.p_client.publish(topic=topic,payload=payload) diff --git a/examples/OTA-lorawan/requirements.txt b/examples/OTA-lorawan/requirements.txt new file mode 100644 index 0000000..620ac93 --- /dev/null +++ b/examples/OTA-lorawan/requirements.txt @@ -0,0 +1 @@ +paho_mqtt==1.5.1 diff --git a/examples/OTA-lorawan/updaterService.py b/examples/OTA-lorawan/updaterService.py index 54aa8cb..9da347a 100644 --- a/examples/OTA-lorawan/updaterService.py +++ b/examples/OTA-lorawan/updaterService.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright (c) 2019, Pycom Limited. +# Copyright (c) 2020, Pycom Limited. # # This software is licensed under the GNU GPL version 3 or any # later version, with permitted additional terms. For more information @@ -22,7 +22,7 @@ def sigint_handler(signum, frame): global exit exit = True print("Terminating Lora OTA updater") - + def on_message(mosq, ota, msg): print("{} {} {}".format(msg.topic, msg.qos, msg.payload)) ota.process_rx_msg(msg.payload.decode()) @@ -32,19 +32,19 @@ def on_publish(mosq, obj, mid): if __name__ == '__main__': signal.signal(signal.SIGINT, sigint_handler) - + ota = OTAHandler() client = paho.Client(userdata=ota) client.connect(config.LORASERVER_IP, config.LORASERVER_MQTT_PORT, 60) - + client.on_message = on_message client.on_publish = on_publish - - client.subscribe("application/+/device/+/rx", 0) + + client.subscribe("application/+/device/+/event/up", 0) ota.set_mqtt_client(client) - + while client.loop() == 0 and not exit: pass diff --git a/lib/sqnsupgrade/README.md b/lib/sqnsupgrade/README.md index 7646d50..e6ec433 100644 --- a/lib/sqnsupgrade/README.md +++ b/lib/sqnsupgrade/README.md @@ -12,7 +12,7 @@ Please start with the following steps: 1. Upgrade the Pycom Firmware Updater tool to latest version 2. Select Firmware Type `stable` in the communication window to upgrade to latest stable version -You can find the different versions of firmwares available here: http://software.pycom.io/downloads/sequans2.html​ +You can find the different versions of firmwares available here: http://software.pycom.io/downloads/sequans2.html These files are password protected, to download them you should be a forum.pycom.io member and access to: Announcements & News --> Announcements only for members --> Firmware Files for Sequans LTE modem now are secured, or clicking Here diff --git a/lib/sqnsupgrade/sqnsupgrade.py b/lib/sqnsupgrade/sqnsupgrade.py index b488787..7c8db01 100644 --- a/lib/sqnsupgrade/sqnsupgrade.py +++ b/lib/sqnsupgrade/sqnsupgrade.py @@ -1,7 +1,7 @@ #!/usr/bin/env python -VERSION = "1.2.5" +VERSION = "1.2.7" -# Copyright (c) 2019, Pycom Limited. +# Copyright (c) 2021, Pycom Limited. # # This software is licensed under the GNU GPL version 3 or any # later version, with permitted additional terms. For more information @@ -129,6 +129,7 @@ def return_code(self, rsp, debug=False): def wait_for_modem(self, send=True, expected=b'OK', echo_char=None): + self.__serial.read() rsp = b'' start = time.time() while True: @@ -339,16 +340,17 @@ def __get_wait_msg(self, load_fff=True): - def __run(self, file_path=None, baudrate=921600, port=None, resume=False, load_ffh=False, mirror=False, switch_ffh=False, bootrom=False, rgbled=0x050505, debug=False, pkgdebug=False, atneg=True, max_try=10, direct=True, atneg_only=False, info_only=False, expected_smod=None, verbose=False, load_fff=False, mtools=False): + def __run(self, file_path=None, baudrate=921600, port=None, resume=False, load_ffh=False, mirror=False, switch_ffh=False, bootrom=False, rgbled=0x050505, debug=False, pkgdebug=False, atneg=True, max_try=10, direct=True, atneg_only=False, info_only=False, expected_smod=None, verbose=False, load_fff=False, mtools=False, fc=False, force_fff=False): self.__wait_msg = False mirror = True if atneg_only else mirror recover = True if atneg_only else load_ffh resume = True if mirror or recover or atneg_only or info_only else resume verbose = True if debug else verbose load_fff = False if bootrom and switch_ffh else load_fff + load_fff = True if force_fff else load_fff target_baudrate = baudrate baudrate = self.__modem_speed if self.__speed_detected else baudrate - if debug: print('mirror? {} recover? {} resume? {} direct? {} atneg_only? {} bootrom? {} load_fff? {}'.format(mirror, recover, resume, direct, atneg_only, bootrom, load_fff)) + if debug: print('file_path? {} mirror? {} recover? {} resume? {} direct? {} atneg_only? {} bootrom? {} load_fff? {}'.format(file_path, mirror, recover, resume, direct, atneg_only, bootrom, load_fff)) if debug: print('baudrate: {} target_baudrate: {}'.format(baudrate, target_baudrate)) abort = True external = False @@ -365,7 +367,7 @@ def __run(self, file_path=None, baudrate=921600, port=None, resume=False, load_f external = True br = 115200 if recover and not direct else baudrate if debug: print('Setting baudrate to {}'.format(br)) - self.__serial = serial.Serial(port, br, bytesize=serial.EIGHTBITS, timeout=1 if info_only else 0.1) + self.__serial = serial.Serial(port, br, bytesize=serial.EIGHTBITS, timeout=1 if info_only else 0.1, rtscts=fc) self.__serial.reset_input_buffer() self.__serial.reset_output_buffer() @@ -399,7 +401,11 @@ def __run(self, file_path=None, baudrate=921600, port=None, resume=False, load_f from sqnsbrz import bootrom except: # fallback to uncompressed - from sqnsbr import bootrom + try: + from sqnsbr import bootrom + except: + print('This firmware does not contain a recovery bootrom.') + return False blob = bootrom() blobsize = blob.get_size() else: @@ -408,11 +414,11 @@ def __run(self, file_path=None, baudrate=921600, port=None, resume=False, load_f if blobsize < 128: print('Firmware file is too small!') reconnect_uart() - sys.exit(1) + return False if blobsize > 4194304: - if load_fff: + if load_fff and not force_fff: print("Firmware file is too big to load via FFF method. Using ON_THE_FLY") - load_fff = False + load_fff = False blob = open(file_path, "rb") if not load_ffh: @@ -445,12 +451,6 @@ def __run(self, file_path=None, baudrate=921600, port=None, resume=False, load_f response = self.return_pretty_response(self.read_rsp(size=7)) if debug: print("AT+SMOD? returned {}".format(response)) - if verbose: print('Sending AT+FSRDFILE="/fs/crashdump"') - self.__serial.write(b'AT+FSRDFILE="/fs/crashdump"\r\n') - response = self.read_rsp(size=100) - if verbose: print('AT+FSRDFILE="/fs/crashdump" returned {}'.format(response)) - self.__serial.read() - self.__serial.write(b"AT+SQNSUPGRADENTF=\"started\"\r\n") response = self.read_rsp(size=100) if verbose: print('AT+SQNSUPGRADENTF="started" returned {}'.format(response)) @@ -484,7 +484,7 @@ def __run(self, file_path=None, baudrate=921600, port=None, resume=False, load_f else: print('Received ERROR from AT+SMSWBOOT=3,1! Aborting!') reconnect_uart() - sys.exit(1) + return False time.sleep(3) resp = self.__serial.read() if debug: print("Response after reset: {}".format(resp)) @@ -501,12 +501,6 @@ def __run(self, file_path=None, baudrate=921600, port=None, resume=False, load_f response = self.read_rsp(size=100) if verbose: print("AT+SMLOG? returned {}".format(response)) - if verbose: print('Sending AT+FSRDFILE="/fs/crashdump"') - self.__serial.write(b'AT+FSRDFILE="/fs/crashdump"\r\n') - response = self.read_rsp(size=100) - if verbose: print('AT+FSRDFILE="/fs/crashdump" returned {}'.format(response)) - self.__serial.read() - else: self.__serial.read(100) @@ -525,10 +519,10 @@ def __run(self, file_path=None, baudrate=921600, port=None, resume=False, load_f time.sleep(.5) self.__serial.read(100) print('Going into MIRROR mode... please close this terminal to resume the upgrade via UART') - self.uart_mirror(rgbled) + return self.uart_mirror(rgbled) elif bootrom: - if verbose: print('Starting STP') + if verbose: print('Starting STP [BR]') else: if verbose: if load_fff: @@ -548,8 +542,11 @@ def __run(self, file_path=None, baudrate=921600, port=None, resume=False, load_f response = self.read_rsp(size=4) if response != b'OK\r\n' and response != b'\r\nOK' and response != b'\nOK': raise OSError("Invalid answer '%s' from the device" % response) - blob.close() - + try: + blob.close() + except Exception as ex: + if debug: print('Exception: {}'.format(ex)) + pass self.__serial.read() elif recover and (not direct): if atneg: @@ -564,31 +561,40 @@ def __run(self, file_path=None, baudrate=921600, port=None, resume=False, load_f time.sleep(.5) self.__serial.read(100) print('Going into MIRROR mode... please close this terminal to resume the upgrade via UART') - self.uart_mirror(rgbled) + return self.uart_mirror(rgbled) else: self.__serial.write(b"AT+STP\n") - response = self.read_rsp(size=6) + response = self.read_rsp(size=2) if not b'OK' in response: print('Failed to start STP mode!') reconnect_uart() - sys.exit(1) + return False else: print('AT auto-negotiation failed! Exiting.') return False else: if debug: print('Starting STP mode...') self.__serial.write(b"AT+STP\n") - response = self.read_rsp(size=6) + response = self.read_rsp(size=4) if not b'OK' in response: print('Failed to start STP mode!') reconnect_uart() - sys.exit(1) + return False try: if debug: - if verbose: print('Starting STP code upload') - if stp.start(blob, blobsize, self.__serial, baudrate, AT=False, debug=debug, pkgdebug=pkgdebug): - blob.close() + if verbose: print('Starting STP code upload with pkgdebug={}'.format(pkgdebug)) + time.sleep(.1) + self.__serial.read() + time.sleep(.1) + start = stp.start(blob, blobsize, self.__serial, baudrate, AT=False, debug=debug, pkgdebug=pkgdebug) + if debug: print('start returned {} type {}'.format(start, type(start))) + if start == True: + try: + blob.close() + except Exception as ex: + if debug: print('Exception: {}'.format(ex)) + pass self.__serial.read() if switch_ffh: if verbose: print('Bootrom updated successfully, switching to recovery mode') @@ -602,12 +608,22 @@ def __run(self, file_path=None, baudrate=921600, port=None, resume=False, load_f if verbose: print('Code download done, returning to user mode') abort = recover else: - blob.close() - print('Code download failed, aborting!') + try: + blob.close() + except Exception as ex: + if debug: print('Exception: {}'.format(ex)) + pass + print('Code download failed[1], aborting!') return False - except: - blob.close() - print('Code download failed, aborting!') + except Exception as ex: + try: + blob.close() + except Exception as ex: + if debug: print('Exception: {}'.format(ex)) + pass + + print('Exception: {}'.format(ex)) + print('Code download failed [2], aborting!') abort = True time.sleep(1.5) @@ -638,7 +654,7 @@ def __run(self, file_path=None, baudrate=921600, port=None, resume=False, load_f if not self.wakeup_modem(baudrate, port, 100, 1, debug, self.__get_wait_msg(load_fff=load_fff)): print("Timeout while waiting for modem to finish updating!") reconnect_uart() - sys.exit(1) + return False start = time.time() while True: @@ -708,7 +724,7 @@ def __run(self, file_path=None, baudrate=921600, port=None, resume=False, load_f else: print("Invalid response after upgrade... aborting.") reconnect_uart() - sys.exit(1) + return False self.__serial.write(b"AT\r\n") self.__serial.write(b"AT\r\n") @@ -720,11 +736,6 @@ def __run(self, file_path=None, baudrate=921600, port=None, resume=False, load_f resonse = self.read_rsp(100) if verbose: print('AT+SQNSUPGRADENTF="success" returned {}'.format(response)) time.sleep(.25) - if verbose: print('Sending AT+FSRDFILE="/fs/crashdump"') - self.__serial.write(b'AT+FSRDFILE="/fs/crashdump"\r\n') - resonse = self.read_rsp(100) - if verbose: print('AT+FSRDFILE="/fs/crashdump" returned {}'.format(response)) - self.__serial.read() return True elif sqnup_result is None: print('Modem upgrade was unsucessfull. Please check your firmware file(s)') @@ -819,7 +830,7 @@ def at_negotiation(self, baudrate, port, max_try, mirror, atneg_only, debug, tar self.__serial = UART(1, baudrate=target_baudrate, pins=self.__pins, timeout_chars=100) else: self.__serial = None - self.__serial = serial.Serial(port, target_baudrate, bytesize=serial.EIGHTBITS, timeout=0.1) + self.__serial = serial.Serial(port, target_baudrate, bytesize=serial.EIGHTBITS, timeout=0.1, rtscts=fc) self.__serial.reset_input_buffer() self.__serial.reset_output_buffer() self.__serial.flush() @@ -849,13 +860,14 @@ def uart_mirror(self, color): time.sleep(.5) pycom.rgbled(color) LTE.modem_upgrade_mode() + return True def success_message(self, port=None, verbose=False, debug=False): print("Your modem has been successfully updated.") print("Here is the current firmware version:\n") self.show_info(port=port, verbose=verbose, debug=debug) - def upgrade(self, ffile, mfile=None, baudrate=921600, retry=False, resume=False, debug=False, pkgdebug=False, verbose=False, load_fff=True, load_only=False, mtools=False): + def upgrade(self, ffile, mfile=None, baudrate=921600, retry=False, resume=False, debug=False, pkgdebug=False, verbose=False, load_fff=True, load_only=False, mtools=False, force_fff=False): success = True if not retry and mfile is not None: if resume or self.__check_br(br_only=True, verbose=verbose, debug=debug): @@ -880,12 +892,13 @@ def upgrade(self, ffile, mfile=None, baudrate=921600, retry=False, resume=False, print('Unable to upgrade bootrom.') if debug: print('Success2? {}'.format(success)) if success: - if self.__run(file_path=ffile, resume=True if mfile is not None else resume, baudrate=baudrate, direct=False, debug=debug, pkgdebug=pkgdebug, verbose=verbose, load_fff=False if mfile else load_fff, mtools=mtools): + if self.__run(file_path=ffile, resume=True if mfile is not None else resume, baudrate=baudrate, direct=False, debug=debug, pkgdebug=pkgdebug, verbose=verbose, load_fff=False if mfile else load_fff, mtools=mtools, force_fff=force_fff): if self.__check_br(verbose=verbose, debug=debug): - self.__run(bootrom=True, debug=debug, direct=False, pkgdebug=pkgdebug, verbose=verbose, load_fff=True) + success = self.__run(bootrom=True, debug=debug, direct=False, pkgdebug=pkgdebug, verbose=verbose, load_fff=True) self.success_message(verbose=verbose, debug=debug) else: print('Unable to load updater from {}'.format(mfile)) + return success def upgrade_uart(self, ffh_mode=False, mfile=None, retry=False, resume=False, color=0x050505, debug=False, pkgdebug=False, verbose=False, load_fff=True): success = False @@ -896,11 +909,11 @@ def upgrade_uart(self, ffh_mode=False, mfile=None, retry=False, resume=False, co if not success: print('Firmware does not support LTE.modem_upgrade_mode()!') reconnect_uart() - sys.exit(1) + return False print('Preparing modem for upgrade...') if not retry and ffh_mode: success = False - if self.__check_br(verbose=verbose, debug=debug): + if self.__check_br(br_only=True, verbose=verbose, debug=debug): success = self.__run(bootrom=True, resume=resume, switch_ffh=True, direct=False, debug=debug, pkgdebug=pkgdebug, verbose=verbose) time.sleep(1) else: @@ -923,16 +936,16 @@ def upgrade_uart(self, ffh_mode=False, mfile=None, retry=False, resume=False, co else: print('Unable to upgrade bootrom.') - def show_info(self, port=None, debug=False, verbose=False): - self.__run(port=port, debug=debug, info_only=True, verbose=verbose) + def show_info(self, port=None, debug=False, verbose=False, fc=False): + self.__run(port=port, debug=debug, info_only=True, verbose=verbose, fc=fc) - def upgrade_ext(self, port, ffile, mfile, resume=False, debug=False, pkgdebug=False, verbose=False, load_fff=True): + def upgrade_ext(self, port, ffile, mfile, resume=False, debug=False, pkgdebug=False, verbose=False, load_fff=True, fc=False, force_fff=False): success = True if mfile is not None: success = False - success = self.__run(file_path=mfile, load_ffh=True, port=port, debug=debug, pkgdebug=pkgdebug, verbose=verbose) + success = self.__run(file_path=mfile, load_ffh=True, port=port, debug=debug, pkgdebug=pkgdebug, verbose=verbose, fc=fc) if success: - if self.__run(file_path=ffile, resume=True if mfile is not None else resume, direct=False, port=port, debug=debug, pkgdebug=pkgdebug, verbose=verbose, load_fff=load_fff): + if self.__run(file_path=ffile, resume=True if mfile is not None else resume, direct=False, port=port, debug=debug, pkgdebug=pkgdebug, verbose=verbose, load_fff=load_fff, fc=fc, force_fff=force_fff): self.success_message(port=port, verbose=verbose, debug=debug) else: print('Unable to load updater from {}'.format(mfile)) @@ -941,7 +954,7 @@ def detect_error(): print('Could not detect your modem!') print('Please try to power off your device and restart in safeboot mode.') reconnect_uart() - sys.exit(1) + return False def print_welcome(): print('<<< Welcome to the SQN3330 firmware updater [{}] >>>'.format(VERSION)) @@ -968,11 +981,12 @@ def load(mfile, baudrate=921600, verbose=False, debug=False, hangup=False): print('Modem must be in recovery mode!') reconnect_uart() - def run(ffile, mfile=None, baudrate=921600, verbose=False, debug=False, load_fff=True, hangup=True): + def run(ffile, mfile=None, baudrate=921600, verbose=False, debug=False, load_fff=True, hangup=True, force_fff=False): print_welcome() retry = False resume = False mtools = False + success = False sqnup = sqnsupgrade() if sqnup.check_files(ffile, mfile, debug): state = sqnup.detect_modem_state(debug=debug, hangup=hangup) @@ -984,15 +998,16 @@ def run(ffile, mfile=None, baudrate=921600, verbose=False, debug=False, load_fff if mfile is None: print('Your modem is in recovery mode. Please specify updater.elf file') reconnect_uart() - sys.exit(1) + return False elif state == 4: resume = True elif state == 1: mtools = True elif state == -1: detect_error() - sqnup.upgrade(ffile=ffile, mfile=mfile, baudrate=baudrate, retry=retry, resume=resume, debug=debug, pkgdebug=False, verbose=verbose, load_fff=load_fff, mtools=mtools) + success = sqnup.upgrade(ffile=ffile, mfile=mfile, baudrate=baudrate, retry=retry, resume=resume, debug=debug, pkgdebug=False, verbose=verbose, load_fff=load_fff, mtools=mtools, force_fff=force_fff) reconnect_uart() + return success def uart(ffh_mode=False, mfile=None, color=0x050505, verbose=False, debug=False, hangup=True): print_welcome() @@ -1054,12 +1069,12 @@ def state(verbose=False, debug=False, retry=5, hangup=False): return sqnup.detect_modem_state(debug=debug, hangup=hangup, retry=retry) else: - def run(port, ffile, mfile=None, resume=False, debug=False, verbose=False, load_fff=True): + def run(port, ffile, mfile=None, resume=False, debug=False, verbose=False, load_fff=True, fc=False, force_fff=False): print_welcome() sqnup = sqnsupgrade() if sqnup.check_files(ffile, mfile, debug): - sqnup.upgrade_ext(port=port, ffile=ffile, mfile=mfile, resume=resume, debug=debug, pkgdebug=False, verbose=verbose, load_fff=load_fff) + sqnup.upgrade_ext(port=port, ffile=ffile, mfile=mfile, resume=resume, debug=debug, pkgdebug=False, verbose=verbose, load_fff=load_fff, force_fff=force_fff) - def version(port, verbose=False, debug=False): + def version(port, verbose=False, debug=False, fc=False): sqnup = sqnsupgrade() sqnup.show_info(port=port, debug=debug, verbose=verbose) diff --git a/pybytes/README.md b/pybytes/README.md deleted file mode 100644 index 406b228..0000000 --- a/pybytes/README.md +++ /dev/null @@ -1,23 +0,0 @@ -

- -# Pybytes Examples - -Please note that these examples only work when using the `pybytes` firmware. - -# Pytrack, Pysense, Pyscan -- To run pytrack on individuals module you will need to create one pybytes_config.json file on root which usually contains data related to device e.g Network preferance, device id, server, username, password etc. - -- You will needs to upload all the files to module in order to get data on pybytes dashboard. - -Note: Note: For using Pyscan, you need to upload either MFRC630.mpy or MFRC630.py. -It is always recommended to use MFRC630.mpy as it takes less space on your module. - -- Pytrack: -GPS coordinate and Acceleration would give result in tuples. - -- Pysense: -Humidity and temperature output would be in float32. -Light sensor output would be in tuples. - -- Pyscan: -Light sensor and Acceleration would give output in tuples. diff --git a/pybytes/pyscan/lib/LIS2HH12.py b/pybytes/pyscan/lib/LIS2HH12.py deleted file mode 100644 index 49a6b5a..0000000 --- a/pybytes/pyscan/lib/LIS2HH12.py +++ /dev/null @@ -1,178 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2019, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -import math -import time -import struct -from machine import Pin - - -FULL_SCALE_2G = const(0) -FULL_SCALE_4G = const(2) -FULL_SCALE_8G = const(3) - -ODR_POWER_DOWN = const(0) -ODR_10_HZ = const(1) -ODR_50_HZ = const(2) -ODR_100_HZ = const(3) -ODR_200_HZ = const(4) -ODR_400_HZ = const(5) -ODR_800_HZ = const(6) - -ACC_G_DIV = 1000 * 65536 - - -class LIS2HH12: - - ACC_I2CADDR = const(30) - - PRODUCTID_REG = const(0x0F) - CTRL1_REG = const(0x20) - CTRL2_REG = const(0x21) - CTRL3_REG = const(0x22) - CTRL4_REG = const(0x23) - CTRL5_REG = const(0x24) - ACC_X_L_REG = const(0x28) - ACC_X_H_REG = const(0x29) - ACC_Y_L_REG = const(0x2A) - ACC_Y_H_REG = const(0x2B) - ACC_Z_L_REG = const(0x2C) - ACC_Z_H_REG = const(0x2D) - ACT_THS = const(0x1E) - ACT_DUR = const(0x1F) - - SCALES = {FULL_SCALE_2G: 4000, FULL_SCALE_4G: 8000, FULL_SCALE_8G: 16000} - ODRS = [0, 10, 50, 100, 200, 400, 800] - - def __init__(self, pysense = None, sda = 'P22', scl = 'P21'): - if pysense is not None: - self.i2c = pysense.i2c - else: - from machine import I2C - self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) - - self.odr = 0 - self.full_scale = 0 - self.x = 0 - self.y = 0 - self.z = 0 - self.int_pin = None - self.act_dur = 0 - self.debounced = False - - whoami = self.i2c.readfrom_mem(ACC_I2CADDR , PRODUCTID_REG, 1) - if (whoami[0] != 0x41): - raise ValueError("LIS2HH12 not found") - - # enable acceleration readings at 50Hz - self.set_odr(ODR_50_HZ) - - # change the full-scale to 4g - self.set_full_scale(FULL_SCALE_4G) - - # set the interrupt pin as active low and open drain - self.set_register(CTRL5_REG, 3, 0, 3) - - # make a first read - self.acceleration() - - def acceleration(self): - x = self.i2c.readfrom_mem(ACC_I2CADDR , ACC_X_L_REG, 2) - self.x = struct.unpack(' self.SCALES[self.full_scale]: - error = "threshold %d exceeds full scale %d" % (thresold, self.SCALES[self.full_scale]) - print(error) - raise ValueError(error) - - if threshold < self.SCALES[self.full_scale] / 128: - error = "threshold %d below resolution %d" % (thresold, self.SCALES[self.full_scale]/128) - print(error) - raise ValueError(error) - - if duration > 255 * 1000 * 8 / self.ODRS[self.odr]: - error = "duration %d exceeds max possible value %d" % (duration, 255 * 1000 * 8 / self.ODRS[self.odr]) - print(error) - raise ValueError(error) - - if duration < 1000 * 8 / self.ODRS[self.odr]: - error = "duration %d below resolution %d" % (duration, 1000 * 8 / self.ODRS[self.odr]) - print(error) - raise ValueError(error) - - _ths = int(127 * threshold / self.SCALES[self.full_scale]) & 0x7F - _dur = int((duration * self.ODRS[self.odr]) / 1000 / 8) - - self.i2c.writeto_mem(ACC_I2CADDR, ACT_THS, _ths) - self.i2c.writeto_mem(ACC_I2CADDR, ACT_DUR, _dur) - - # enable the activity/inactivity interrupt - self.set_register(CTRL3_REG, 1, 5, 1) - - self._user_handler = handler - self.int_pin = Pin('P13', mode=Pin.IN) - self.int_pin.callback(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=self._int_handler) - - # return actual used thresold and duration - return (_ths * self.SCALES[self.full_scale] / 128, _dur * 8 * 1000 / self.ODRS[self.odr]) - - def activity(self): - if not self.debounced: - time.sleep_ms(self.act_dur) - self.debounced = True - if self.int_pin(): - return True - return False - - def _int_handler(self, pin_o): - if self._user_handler is not None: - self._user_handler(pin_o) - else: - if pin_o(): - print('Activity interrupt') - else: - print('Inactivity interrupt') diff --git a/pybytes/pyscan/lib/LTR329ALS01.py b/pybytes/pyscan/lib/LTR329ALS01.py deleted file mode 100644 index 49e3439..0000000 --- a/pybytes/pyscan/lib/LTR329ALS01.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2019, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -import time -from machine import I2C - -class LTR329ALS01: - ALS_I2CADDR = const(0x29) # The device's I2C address - - ALS_CONTR_REG = const(0x80) - ALS_MEAS_RATE_REG = const(0x85) - - ALS_DATA_CH1_LOW = const(0x88) - ALS_DATA_CH1_HIGH = const(0x89) - ALS_DATA_CH0_LOW = const(0x8A) - ALS_DATA_CH0_HIGH = const(0x8B) - - ALS_GAIN_1X = const(0x00) - ALS_GAIN_2X = const(0x01) - ALS_GAIN_4X = const(0x02) - ALS_GAIN_8X = const(0x03) - ALS_GAIN_48X = const(0x06) - ALS_GAIN_96X = const(0x07) - - ALS_INT_50 = const(0x01) - ALS_INT_100 = const(0x00) - ALS_INT_150 = const(0x04) - ALS_INT_200 = const(0x02) - ALS_INT_250 = const(0x05) - ALS_INT_300 = const(0x06) - ALS_INT_350 = const(0x07) - ALS_INT_400 = const(0x03) - - ALS_RATE_50 = const(0x00) - ALS_RATE_100 = const(0x01) - ALS_RATE_200 = const(0x02) - ALS_RATE_500 = const(0x03) - ALS_RATE_1000 = const(0x04) - ALS_RATE_2000 = const(0x05) - - def __init__(self, pysense = None, sda = 'P22', scl = 'P21', gain = ALS_GAIN_1X, integration = ALS_INT_100, rate = ALS_RATE_500): - if pysense is not None: - self.i2c = pysense.i2c - else: - self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) - - contr = self._getContr(gain) - self.i2c.writeto_mem(ALS_I2CADDR, ALS_CONTR_REG, bytearray([contr])) - - measrate = self._getMeasRate(integration, rate) - self.i2c.writeto_mem(ALS_I2CADDR, ALS_MEAS_RATE_REG, bytearray([measrate])) - - time.sleep(0.01) - - def _getContr(self, gain): - return ((gain & 0x07) << 2) + 0x01 - - def _getMeasRate(self, integration, rate): - return ((integration & 0x07) << 3) + (rate & 0x07) - - def _getWord(self, high, low): - return ((high & 0xFF) << 8) + (low & 0xFF) - - def light(self): - ch1low = self.i2c.readfrom_mem(ALS_I2CADDR , ALS_DATA_CH1_LOW, 1) - ch1high = self.i2c.readfrom_mem(ALS_I2CADDR , ALS_DATA_CH1_HIGH, 1) - data1 = int(self._getWord(ch1high[0], ch1low[0])) - - ch0low = self.i2c.readfrom_mem(ALS_I2CADDR , ALS_DATA_CH0_LOW, 1) - ch0high = self.i2c.readfrom_mem(ALS_I2CADDR , ALS_DATA_CH0_HIGH, 1) - data0 = int(self._getWord(ch0high[0], ch0low[0])) - - return (data0, data1) diff --git a/pybytes/pyscan/lib/MFRC630.mpy b/pybytes/pyscan/lib/MFRC630.mpy deleted file mode 100644 index 81ee92f..0000000 Binary files a/pybytes/pyscan/lib/MFRC630.mpy and /dev/null differ diff --git a/pybytes/pyscan/lib/MFRC630.py b/pybytes/pyscan/lib/MFRC630.py deleted file mode 100644 index 20ad25d..0000000 --- a/pybytes/pyscan/lib/MFRC630.py +++ /dev/null @@ -1,756 +0,0 @@ -''' -Pyscan NFC library -Copyright (c) 2019, Pycom Limited. -Based on a library for NXP's MFRC630 NFC IC https://github.com/iwanders/MFRC630 -The MIT License (MIT) -Copyright (c) 2016 Ivor Wanders -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -''' - -import time, binascii - -class MFRC630: - - NFC_I2CADDR = const(0x28) - # commands - MFRC630_CMD_IDLE = const(0x00) # (no arguments) ; no action, cancels current command execution. */ - MFRC630_CMD_LPCD = const(0x01) # (no arguments) ; low-power card detection. */ - MFRC630_CMD_LOADKEY = const(0x02) # (keybyte1), (keybyte2), (keybyte3), (keybyte4), (keybyte5), - MFRC630_CMD_MFAUTHENT = const(0x03) # 60h or 61h, (block address), (card serial number byte0), (card - MFRC630_CMD_RECEIVE = const(0x05) # (no arguments) ; activates the receive circuit. */ - MFRC630_CMD_TRANSMIT = const(0x06) # bytes to send: byte1, byte2, ...; transmits data from the FIFO - MFRC630_CMD_TRANSCEIVE = const(0x07) # bytes to send: byte1, byte2, ....; transmits data from the FIFO - MFRC630_CMD_WRITEE2 = const(0x08) # addressH, addressL, data; gets one byte from FIFO buffer and - MFRC630_CMD_WRITEE2PAGE = const(0x09) # (page Address), data0, [data1..data63]; gets up to 64 bytes (one - MFRC630_CMD_READE2 = const(0x0A) # addressH, address L, length; reads data from the EEPROM and copies - MFRC630_CMD_LOADREG = const(0x0C) # (EEPROM addressH), (EEPROM addressL), RegAdr, (number of Register - MFRC630_CMD_LOADPROTOCOL = const(0x0D) # (Protocol number RX), (Protocol number TX) reads data from the - MFRC630_CMD_LOADKEYE2 = const(0x0E) # KeyNr; copies a key from the EEPROM into the key buffer. */ - MFRC630_CMD_STOREKEYE2 = const(0x0F) # KeyNr, byte1, byte2, byte3, byte4, byte5, byte6; stores a MIFARE - MFRC630_CMD_READRNR = const(0x1C) # (no arguments) ; Copies bytes from the Random Number generator - MFRC630_CMD_SOFTRESET = const(0x1F) # (no arguments) ; resets the MFRC630. */ - - MFRC630_STATUS_STATE_IDLE = const(0b000) # Status register; Idle - MFRC630_STATUS_STATE_TXWAIT = const(0b001) # Status register; Tx wait - MFRC630_STATUS_STATE_TRANSMITTING = const(0b011) # Status register; Transmitting. - MFRC630_STATUS_STATE_RXWAIT = const(0b101) # Status register; Rx wait. - MFRC630_STATUS_STATE_WAIT_FOR_DATA = const(0b110) # Status register; Waiting for data. - MFRC630_STATUS_STATE_RECEIVING = const(0b111) # Status register; Receiving data. - MFRC630_STATUS_STATE_NOT_USED = const(0b100) # Status register; Not used. - MFRC630_STATUS_CRYPTO1_ON = const(1 << 5) # Status register; Crypto1 (MIFARE authentication) is on. - - MFRC630_PROTO_ISO14443A_106_MILLER_MANCHESTER = const(0) - MFRC630_PROTO_ISO14443A_212_MILLER_BPSK = const(1) - MFRC630_PROTO_ISO14443A_424_MILLER_BPSK = const(2) - MFRC630_PROTO_ISO14443A_848_MILLER_BPSK = const(3) - MFRC630_PROTO_ISO14443B_106_NRZ_BPSK = const(4) - MFRC630_PROTO_ISO14443B_212_NRZ_BPSK = const(5) - MFRC630_PROTO_ISO14443B_424_NRZ_BPSK = const(6) - MFRC630_PROTO_ISO14443B_848_NRZ_BPSK = const(7) - MFRC630_PROTO_FELICA_212_MANCHESTER_MANCHESTER = const(8) - MFRC630_PROTO_FELICA_424_MANCHESTER_MANCHESTER = const(9) - MFRC630_PROTO_ISO15693_1_OF_4_SSC = const(10) - MFRC630_PROTO_ISO15693_1_OF_4_DSC = const(11) - MFRC630_PROTO_ISO15693_1_OF_256_SSC = const(12) - MFRC630_PROTO_EPC_UID_UNITRAY_SSC = const(13) - MFRC630_PROTO_ISO18000_MODE_3 = const(14) - MFRC630_RECOM_14443A_ID1_106 = [ 0x8A, 0x08, 0x21, 0x1A, 0x18, 0x18, 0x0F, 0x27, 0x00, 0xC0, 0x12, 0xCF, 0x00, 0x04, 0x90, 0x32, 0x12, 0x0A ] - MFRC630_RECOM_14443A_ID1_212 = [ 0x8E, 0x12, 0x11, 0x06, 0x18, 0x18, 0x0F, 0x10, 0x00, 0xC0, 0x12, 0xCF, 0x00, 0x05, 0x90, 0x3F, 0x12, 0x02 ] - MFRC630_RECOM_14443A_ID1_424 = [ 0x8E, 0x12, 0x11, 0x06, 0x18, 0x18, 0x0F, 0x08, 0x00, 0xC0, 0x12, 0xCF, 0x00, 0x06, 0x90, 0x3F, 0x12, 0x0A ] - MFRC630_RECOM_14443A_ID1_848 = [ 0x8F, 0xDB, 0x11, 0x06, 0x18, 0x18, 0x0F, 0x02, 0x00, 0xC0, 0x12, 0xCF, 0x00, 0x07, 0x90, 0x3F, 0x12, 0x02 ] - MFRC630_ISO14443_CMD_REQA = const(0x26) # request (idle -> ready) - MFRC630_ISO14443_CMD_WUPA = const(0x52) # wake up type a (idle / halt -> ready) - MFRC630_ISO14443_CAS_LEVEL_1 = const(0x93) # Cascade level 1 for select. - MFRC630_ISO14443_CAS_LEVEL_2 = const(0x95) # Cascade level 2 for select. - MFRC630_ISO14443_CAS_LEVEL_3 = const(0x97) # Cascade level 3 for select. - MFRC630_MF_AUTH_KEY_A = const(0x60) # A key_type for mifare auth. - MFRC630_MF_AUTH_KEY_B = const(0x61) # A key_type for mifare auth. - MFRC630_MF_CMD_READ = const(0x30) # To read a block from mifare card. - MFRC630_MF_CMD_WRITE = const(0xA0) # To write a block to a mifare card. - MFRC630_MF_ACK = const(0x0A) # Sent by cards to acknowledge an operation. - - # registers - MFRC630_REG_COMMAND = const(0x00) # Starts and stops command execution - MFRC630_REG_HOSTCTRL = const(0x01) # Host control register - MFRC630_REG_FIFOCONTROL = const(0x02) # Control register of the FIFO - MFRC630_REG_WATERLEVEL = const(0x03) # Level of the FIFO underflow and overflow warning - MFRC630_REG_FIFOLENGTH = const(0x04) # Length of the FIFO - MFRC630_REG_FIFODATA = const(0x05) # Data In/Out exchange register of FIFO buffer - MFRC630_REG_IRQ0 = const(0x06) # Interrupt register 0 - MFRC630_REG_IRQ1 = const(0x07) # Interrupt register 1 - MFRC630_REG_IRQ0EN = const(0x08) # Interrupt enable register 0 - MFRC630_REG_IRQ1EN = const(0x09) # Interrupt enable register 1 - MFRC630_REG_ERROR = const(0x0A) # Error bits showing the error status of the last command execution - MFRC630_REG_STATUS = const(0x0B) # Contains status of the communication - MFRC630_REG_RXBITCTRL = const(0x0C) # Control for anticoll. adjustments for bit oriented protocols - MFRC630_REG_RXCOLL = const(0x0D) # Collision position register - MFRC630_REG_TCONTROL = const(0x0E) # Control of Timer 0..3 - MFRC630_REG_T0CONTROL = const(0x0F) # Control of Timer0 - MFRC630_REG_T0RELOADHI = const(0x10) # High register of the reload value of Timer0 - MFRC630_REG_T0RELOADLO = const(0x11) # Low register of the reload value of Timer0 - MFRC630_REG_T0COUNTERVALHI = const(0x12) # Counter value high register of Timer0 - MFRC630_REG_T0COUNTERVALLO = const(0x13) # Counter value low register of Timer0 - MFRC630_REG_T1CONTROL = const(0x14) # Control of Timer1 - MFRC630_REG_T1RELOADHI = const(0x15) # High register of the reload value of Timer1 - MFRC630_REG_T1COUNTERVALHI = const(0x17) # Counter value high register of Timer1 - MFRC630_REG_T1COUNTERVALLO = const(0x18) # Counter value low register of Timer1 - MFRC630_REG_T2CONTROL = const(0x19) # Control of Timer2 - MFRC630_REG_T2RELOADHI = const(0x1A) # High byte of the reload value of Timer2 - MFRC630_REG_T2RELOADLO = const(0x1B) # Low byte of the reload value of Timer2 - MFRC630_REG_T2COUNTERVALHI = const(0x1C) # Counter value high byte of Timer2 - MFRC630_REG_T2COUNTERVALLO = const(0x1D) # Counter value low byte of Timer2 - MFRC630_REG_T3CONTROL = const(0x1E) # Control of Timer3 - MFRC630_REG_T3RELOADHI = const(0x1F) # High byte of the reload value of Timer3 - MFRC630_REG_T3RELOADLO = const(0x20) # Low byte of the reload value of Timer3 - MFRC630_REG_T3COUNTERVALHI = const(0x21) # Counter value high byte of Timer3 - MFRC630_REG_T3COUNTERVALLO = const(0x22) # Counter value low byte of Timer3 - MFRC630_REG_T4CONTROL = const(0x23) # Control of Timer4 - MFRC630_REG_T4RELOADHI = const(0x24) # High byte of the reload value of Timer4 - MFRC630_REG_T4RELOADLO = const(0x25) # Low byte of the reload value of Timer4 - MFRC630_REG_T4COUNTERVALHI = const(0x26) # Counter value high byte of Timer4 - MFRC630_REG_T4COUNTERVALLO = const(0x27) # Counter value low byte of Timer4 - MFRC630_REG_DRVMOD = const(0x28) # Driver mode register - MFRC630_REG_TXAMP = const(0x29) # Transmitter amplifier register - MFRC630_REG_DRVCON = const(0x2A) # Driver configuration register - MFRC630_REG_TXL = const(0x2B) # Transmitter register - MFRC630_REG_TXCRCPRESET = const(0x2C) # Transmitter CRC control register, preset value - MFRC630_REG_RXCRCCON = const(0x2D) # Receiver CRC control register, preset value - MFRC630_REG_TXDATANUM = const(0x2E) # Transmitter data number register - MFRC630_REG_TXMODWIDTH = const(0x2F) # Transmitter modulation width register - MFRC630_REG_TXSYM10BURSTLEN = const(0x30) # Transmitter symbol 1 + symbol 0 burst length register - MFRC630_REG_TXWAITCTRL = const(0x31) # Transmitter wait control - MFRC630_REG_TXWAITLO = const(0x32) # Transmitter wait low - MFRC630_REG_FRAMECON = const(0x33) # Transmitter frame control - MFRC630_REG_RXSOFD = const(0x34) # Receiver start of frame detection - MFRC630_REG_RXCTRL = const(0x35) # Receiver control register - MFRC630_REG_RXWAIT = const(0x36) # Receiver wait register - MFRC630_REG_RXTHRESHOLD = const(0x37) # Receiver threshold register - MFRC630_REG_RCV = const(0x38) # Receiver register - MFRC630_REG_RXANA = const(0x39) # Receiver analog register - MFRC630_REG_RFU = const(0x3A) # (Reserved for future use) - MFRC630_REG_SERIALSPEED = const(0x3B) # Serial speed register - MFRC630_REG_LFO_TRIMM = const(0x3C) # Low-power oscillator trimming register - MFRC630_REG_PLL_CTRL = const(0x3D) # IntegerN PLL control register, for mcu clock output adjustment - MFRC630_REG_PLL_DIVOUT = const(0x3E) # IntegerN PLL control register, for mcu clock output adjustment - MFRC630_REG_LPCD_QMIN = const(0x3F) # Low-power card detection Q channel minimum threshold - MFRC630_REG_LPCD_QMAX = const(0x40) # Low-power card detection Q channel maximum threshold - MFRC630_REG_LPCD_IMIN = const(0x41) # Low-power card detection I channel minimum threshold - MFRC630_REG_LPCD_I_RESULT = const(0x42) # Low-power card detection I channel result register - MFRC630_REG_LPCD_Q_RESULT = const(0x43) # Low-power card detection Q channel result register - MFRC630_REG_PADEN = const(0x44) # PIN enable register - MFRC630_REG_PADOUT = const(0x45) # PIN out register - MFRC630_REG_PADIN = const(0x46) # PIN in register - MFRC630_REG_SIGOUT = const(0x47) # Enables and controls the SIGOUT Pin - MFRC630_REG_VERSION = const(0x7F) # Version and subversion register - - MFRC630_TXDATANUM_DATAEN = const(1 << 3) - MFRC630_RECOM_14443A_CRC = const(0x18) - - MFRC630_ERROR_EE_ERR = const(1 << 7) - MFRC630_ERROR_FIFOWRERR = const(1 << 6) - MFRC630_ERROR_FIFOOVL = const(1 << 5) - MFRC630_ERROR_MINFRAMEERR = const(1 << 4) - MFRC630_ERROR_NODATAERR = const(1 << 3) - MFRC630_ERROR_COLLDET = const(1 << 2) - MFRC630_ERROR_PROTERR = const(1 << 1) - MFRC630_ERROR_INTEGERR = const(1 << 0) - - MFRC630_CRC_ON = const(1) - MFRC630_CRC_OFF = const(0) - - MFRC630_IRQ0EN_IRQ_INV = const(1 << 7) - MFRC630_IRQ0EN_HIALERT_IRQEN = const(1 << 6) - MFRC630_IRQ0EN_LOALERT_IRQEN = const(1 << 5) - MFRC630_IRQ0EN_IDLE_IRQEN = const(1 << 4) - MFRC630_IRQ0EN_TX_IRQEN = const(1 << 3) - MFRC630_IRQ0EN_RX_IRQEN = const(1 << 2) - MFRC630_IRQ0EN_ERR_IRQEN = const(1 << 1) - MFRC630_IRQ0EN_RXSOF_IRQEN = const(1 << 0) - - MFRC630_IRQ1EN_TIMER0_IRQEN = const(1 << 0) - - MFRC630_TCONTROL_CLK_211KHZ = const(0b01) - MFRC630_TCONTROL_START_TX_END = const(0b01 << 4) - - MFRC630_IRQ1_GLOBAL_IRQ = const(1 << 6) - - MFRC630_IRQ0_ERR_IRQ = const(1 << 1) - MFRC630_IRQ0_RX_IRQ = const(1 << 2) - - def __init__(self, pyscan=None, sda='P22', scl='P21', timeout=None, debug=False): - if pyscan is not None: - self.i2c = pyscan.i2c - else: - from machine import I2C - self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) - self._DEBUG = debug - self.mfrc630_cmd_reset() - - # ToDo: Timeout not yet implemented! - # self.chrono = Timer.Chrono() - # self.timeout = timeout - # self.timeout_status = True - - def print_debug(self, msg): - if self._DEBUG: - print(msg) - - def mfrc630_read_reg(self, reg): - return self.i2c.readfrom_mem(NFC_I2CADDR, reg, 1)[0] - - def mfrc630_write_reg(self, reg, data): - self.i2c.writeto_mem(NFC_I2CADDR, reg, bytes([data & 0xFF])) - - def mfrc630_write_regs(self, reg, data): - self.i2c.writeto_mem(NFC_I2CADDR, reg, bytes(data)) - - def mfrc630_read_fifo(self, len): - if len > 0: - return self.i2c.readfrom_mem(NFC_I2CADDR, MFRC630_REG_FIFODATA, len) - else: - return None - - def mfrc630_cmd_idle(self): - self.mfrc630_write_reg(MFRC630_REG_COMMAND, MFRC630_CMD_IDLE) - - def mfrc630_flush_fifo(self): - self.mfrc630_write_reg(MFRC630_REG_FIFOCONTROL, 1 << 4) - - def mfrc630_setup_fifo(self): - self.mfrc630_write_reg(MFRC630_REG_FIFOCONTROL, 0x90) - self.mfrc630_write_reg(MFRC630_REG_WATERLEVEL, 0xFE) - - def mfrc630_write_fifo(self, data): - self.mfrc630_write_regs(MFRC630_REG_FIFODATA, data) - - def mfrc630_cmd_load_protocol(self, rx, tx): - self.mfrc630_flush_fifo() - self.mfrc630_write_fifo([rx, tx]) - self.mfrc630_write_reg(MFRC630_REG_COMMAND, MFRC630_CMD_LOADPROTOCOL) - - def mfrc630_cmd_transceive(self, data): - self.mfrc630_cmd_idle() - self.mfrc630_flush_fifo() - self.mfrc630_setup_fifo() - self.mfrc630_write_fifo(data) - self.mfrc630_write_reg(MFRC630_REG_COMMAND, MFRC630_CMD_TRANSCEIVE) - - def mfrc630_cmd_init(self): - self.mfrc630_write_regs(MFRC630_REG_DRVMOD, self.MFRC630_RECOM_14443A_ID1_106) - self.mfrc630_write_reg(0x28, 0x8E) - self.mfrc630_write_reg(0x29, 0x15) - self.mfrc630_write_reg(0x2A, 0x11) - self.mfrc630_write_reg(0x2B, 0x06) - - def mfrc630_cmd_reset(self): - self.mfrc630_cmd_idle() - self.mfrc630_write_reg(MFRC630_REG_COMMAND, MFRC630_CMD_SOFTRESET) - - def mfrc630_clear_irq0(self): - self.mfrc630_write_reg(MFRC630_REG_IRQ0, ~(1 << 7)) - - def mfrc630_clear_irq1(self): - self.mfrc630_write_reg(MFRC630_REG_IRQ1, ~(1 << 7)) - - def mfrc630_irq0(self): - return self.mfrc630_read_reg(MFRC630_REG_IRQ0) - - def mfrc630_irq1(self): - return self.mfrc630_read_reg(MFRC630_REG_IRQ1) - - def mfrc630_timer_set_control(self, timer, value): - self.mfrc630_write_reg(MFRC630_REG_T0CONTROL + (5 * timer), value) - - def mfrc630_timer_set_reload(self, timer, value): - self.mfrc630_write_reg(MFRC630_REG_T0RELOADHI + (5 * timer), value >> 8) - self.mfrc630_write_reg(MFRC630_REG_T0RELOADLO + (5 * timer), 0xFF) - - def mfrc630_timer_set_value(self, timer, value): - self.mfrc630_write_reg(MFRC630_REG_T0COUNTERVALHI + (5 * timer), value >> 8) - self.mfrc630_write_reg(MFRC630_REG_T0COUNTERVALLO + (5 * timer), 0xFF) - - def mfrc630_fifo_length(self): - # should do 512 byte fifo handling here - return self.mfrc630_read_reg(MFRC630_REG_FIFOLENGTH) - - def mfrc630_status(self): - return self.mfrc630_read_reg(MFRC630_REG_STATUS) - - def mfrc630_error(self): - return self.mfrc630_read_reg(MFRC630_REG_ERROR) - - def mfrc630_cmd_load_key(self, key): - self.mfrc630_cmd_idle() - self.mfrc630_flush_fifo() - self.mfrc630_write_fifo(key) - self.mfrc630_write_reg(MFRC630_REG_COMMAND, MFRC630_CMD_LOADKEY) - - def mfrc630_cmd_auth(self, key_type, block_address, card_uid): - self.mfrc630_cmd_idle() - parameters = [ key_type, block_address, card_uid[0], card_uid[1], card_uid[2], card_uid[3] ] - self.mfrc630_flush_fifo() - self.mfrc630_write_fifo(parameters) - self.mfrc630_write_reg(MFRC630_REG_COMMAND, MFRC630_CMD_MFAUTHENT) - - def mfrc630_MF_read_block(self, block_address, dest): - self.mfrc630_flush_fifo() - - self.mfrc630_write_reg(MFRC630_REG_TXCRCPRESET, MFRC630_RECOM_14443A_CRC | MFRC630_CRC_ON) - self.mfrc630_write_reg(MFRC630_REG_RXCRCCON, MFRC630_RECOM_14443A_CRC | MFRC630_CRC_ON) - - send_req = [ MFRC630_MF_CMD_READ, block_address ] - - # configure a timeout timer. - timer_for_timeout = 0 # should match the enabled interupt. - - # enable the global IRQ for idle, errors and timer. - self.mfrc630_write_reg(MFRC630_REG_IRQ0EN, MFRC630_IRQ0EN_IDLE_IRQEN | MFRC630_IRQ0EN_ERR_IRQEN) - self.mfrc630_write_reg(MFRC630_REG_IRQ1EN, MFRC630_IRQ1EN_TIMER0_IRQEN) - - - # Set timer to 221 kHz clock, start at the end of Tx. - self.mfrc630_timer_set_control(timer_for_timeout, MFRC630_TCONTROL_CLK_211KHZ | MFRC630_TCONTROL_START_TX_END) - # Frame waiting time: FWT = (256 x 16/fc) x 2 FWI - # FWI defaults to four... so that would mean wait for a maximum of ~ 5ms - self.mfrc630_timer_set_reload(timer_for_timeout, 2000) # 2000 ticks of 5 usec is 10 ms. - self.mfrc630_timer_set_value(timer_for_timeout, 2000) - - irq1_value = 0 - irq0_value = 0 - - self.mfrc630_clear_irq0() # clear irq0 - self.mfrc630_clear_irq1() # clear irq1 - - # Go into send, then straight after in receive. - self.mfrc630_cmd_transceive(send_req) - - # block until we are done - while not (irq1_value & (1 << timer_for_timeout)): - irq1_value = self.mfrc630_irq1() - if (irq1_value & MFRC630_IRQ1_GLOBAL_IRQ): - self.print_debug("irq1: %x" % irq1_value) - break # stop polling irq1 and quit the timeout loop. - - self.mfrc630_cmd_idle() - - if irq1_value & (1 << timer_for_timeout): - self.print_debug("this indicates a timeout") - # this indicates a timeout - return 0 - - irq0_value = self.mfrc630_irq0() - if (irq0_value & MFRC630_IRQ0_ERR_IRQ): - self.print_debug("some error") - # some error - return 0 - - self.print_debug("all seems to be well...") - # all seems to be well... - buffer_length = self.mfrc630_fifo_length() - rx_len = buffer_length if (buffer_length <= 16) else 16 - dest = self.mfrc630_read_fifo(rx_len) - return rx_len - - - def mfrc630_iso14443a_WUPA_REQA(self, instruction): - self.mfrc630_cmd_idle() - - self.mfrc630_flush_fifo() - - #Set register such that we sent 7 bits, set DataEn such that we can send data - self.mfrc630_write_reg(MFRC630_REG_TXDATANUM, 7 | MFRC630_TXDATANUM_DATAEN) - - # disable the CRC registers - self.mfrc630_write_reg(MFRC630_REG_TXCRCPRESET, MFRC630_RECOM_14443A_CRC | MFRC630_CRC_OFF) - self.mfrc630_write_reg(MFRC630_REG_RXCRCCON, MFRC630_RECOM_14443A_CRC | MFRC630_CRC_OFF) - self.mfrc630_write_reg(MFRC630_REG_RXBITCTRL, 0) - - # clear interrupts - self.mfrc630_clear_irq0() - self.mfrc630_clear_irq1() - - # enable the global IRQ for Rx done and Errors. - self.mfrc630_write_reg(MFRC630_REG_IRQ0EN, MFRC630_IRQ0EN_RX_IRQEN | MFRC630_IRQ0EN_ERR_IRQEN) - self.mfrc630_write_reg(MFRC630_REG_IRQ1EN, MFRC630_IRQ1EN_TIMER0_IRQEN) - - # configure timer - timer_for_timeout = 0 - # Set timer to 221 kHz clock, start at the end of Tx. - self.mfrc630_timer_set_control(timer_for_timeout, MFRC630_TCONTROL_CLK_211KHZ | MFRC630_TCONTROL_START_TX_END) - - # Frame waiting time: FWT = (256 x 16/fc) x 2 FWI - # FWI defaults to four... so that would mean wait for a maximum of ~ 5ms - self.mfrc630_timer_set_reload(timer_for_timeout, 1000) # 1000 ticks of 5 usec is 5 ms. - self.mfrc630_timer_set_value(timer_for_timeout, 1000) - - # Go into send, then straight after in receive. - self.mfrc630_cmd_transceive([instruction]) - self.print_debug('Sending REQA') - - # block until we are done - irq1_value = 0 - while not (irq1_value & (1 << timer_for_timeout)): - irq1_value = self.mfrc630_irq1() - if irq1_value & MFRC630_IRQ1_GLOBAL_IRQ: # either ERR_IRQ or RX_IRQ - break # stop polling irq1 and quit the timeout loop - - self.print_debug('After waiting for answer') - self.mfrc630_cmd_idle() - - # if no Rx IRQ, or if there's an error somehow, return 0 - irq0 = self.mfrc630_irq0() - if (not (irq0 & MFRC630_IRQ0_RX_IRQ)) or (irq0 & MFRC630_IRQ0_ERR_IRQ): - self.print_debug('No RX, irq1: %x irq0: %x' % (irq1_value, irq0)) - return 0 - - return self.mfrc630_fifo_length() - self.print_debug("rx_len:", rx_len) - if rx_len == 2: # ATQA should answer with 2 bytes - res = self.mfrc630_read_fifo(rx_len) - self.print_debug('ATQA answer:', res) - return res - return 0 - - def mfrc630_print_block(self, data, len): - if self._DEBUG: - print(self.mfrc630_format_block(data, len)) - - def mfrc630_format_block(self, data, len): - if type(data) == bytearray: - len_i = 0 - try: - len_i = int(len) - except: - pass - if (len_i > 0): - return ' '.join('{:02x}'.format(x) for x in data[:len_i]).upper() - else: - return ' '.join('{:02x}'.format(x) for x in data).upper() - else: - self.print_debug("DATA has type: " + str(type(data))) - try: - return "Length: %d Data: %s" % (len,binascii.hexlify(data,' ')) - except: - return "Data: %s with Length: %s" % (str(data), len) - - - def mfrc630_iso14443a_select(self, uid): - - self.print_debug("Starting select") - - self.mfrc630_cmd_idle() - self.mfrc630_flush_fifo() - - # enable the global IRQ for Rx done and Errors. - self.mfrc630_write_reg(MFRC630_REG_IRQ0EN, MFRC630_IRQ0EN_RX_IRQEN | MFRC630_IRQ0EN_ERR_IRQEN) - self.mfrc630_write_reg(MFRC630_REG_IRQ1EN, MFRC630_IRQ1EN_TIMER0_IRQEN) # only trigger on timer for irq1 - - # configure a timeout timer, use timer 0. - timer_for_timeout = 0 - - # Set timer to 221 kHz clock, start at the end of Tx. - self.mfrc630_timer_set_control(timer_for_timeout, MFRC630_TCONTROL_CLK_211KHZ | MFRC630_TCONTROL_START_TX_END) - # Frame waiting time: FWT = (256 x 16/fc) x 2 FWI - # FWI defaults to four... so that would mean wait for a maximum of ~ 5ms - - self.mfrc630_timer_set_reload(timer_for_timeout, 1000) # 1000 ticks of 5 usec is 5 ms. - self.mfrc630_timer_set_value(timer_for_timeout, 1000) - - for cascade_level in range(1, 4): - self.print_debug("Starting cascade level: %d" % cascade_level) - cmd = 0 - known_bits = 0 # known bits of the UID at this level so far. - send_req = bytearray(7) # used as Tx buffer. - uid_this_level = send_req[2:] - message_length = 0 - if cascade_level == 1: - cmd = MFRC630_ISO14443_CAS_LEVEL_1; - elif cascade_level == 2: - cmd = MFRC630_ISO14443_CAS_LEVEL_2; - elif cascade_level == 3: - cmd = MFRC630_ISO14443_CAS_LEVEL_3; - - # disable CRC in anticipation of the anti collision protocol - self.mfrc630_write_reg(MFRC630_REG_TXCRCPRESET, MFRC630_RECOM_14443A_CRC | MFRC630_CRC_OFF) - self.mfrc630_write_reg(MFRC630_REG_RXCRCCON, MFRC630_RECOM_14443A_CRC | MFRC630_CRC_OFF) - - # max 32 loops of the collision loop. - for collision_n in range(0, 33): - self.print_debug("CL: %d, coll loop: %d, kb %d long" % (cascade_level, collision_n, known_bits)) - self.mfrc630_print_block(uid_this_level, (known_bits + 8 - 1) / 8) - # clear interrupts - self.mfrc630_clear_irq0() - self.mfrc630_clear_irq1() - - send_req[0] = cmd; - send_req[1] = 0x20 + known_bits - send_req[2:5] = uid_this_level[0:3] - - # Only transmit the last 'x' bits of the current byte we are discovering - # First limit the txdatanum, such that it limits the correct number of bits. - self.mfrc630_write_reg(MFRC630_REG_TXDATANUM, (known_bits % 8) | MFRC630_TXDATANUM_DATAEN) - - # ValuesAfterColl: If cleared, every received bit after a collision is - # replaced by a zero. This function is needed for ISO/IEC14443 anticollision (0<<7). - # We want to shift the bits with RxAlign - rxalign = known_bits % 8; - self.print_debug("Setting rx align to: %d" % rxalign) - self.mfrc630_write_reg(MFRC630_REG_RXBITCTRL, (0 << 7) | (rxalign << 4)) - - # then sent the send_req to the hardware, - # (known_bits / 8) + 1): The ceiled number of bytes by known bits. - # +2 for cmd and NVB. - if ((known_bits % 8) == 0): - message_length = ((known_bits / 8)) + 2; - else: - message_length = ((known_bits / 8) + 1) + 2; - - # Send message - self.mfrc630_cmd_transceive(send_req[:int(message_length)]) - - # block until we are done - irq1_value = 0 - while not (irq1_value & (1 << timer_for_timeout)): - irq1_value = self.mfrc630_irq1() - # either ERR_IRQ or RX_IRQ or Timer - if (irq1_value & MFRC630_IRQ1_GLOBAL_IRQ): - break # stop polling irq1 and quit the timeout loop. - - self.mfrc630_cmd_idle() - - # next up, we have to check what happened. - irq0 = self.mfrc630_irq0() - error = self.mfrc630_read_reg(MFRC630_REG_ERROR) - coll = self.mfrc630_read_reg(MFRC630_REG_RXCOLL) - self.print_debug("irq0: %x coll: %x error: %x " % (irq0, coll, error)) - collision_pos = 0 - if irq0 and MFRC630_IRQ0_ERR_IRQ: # some error occured. - self.print_debug("some error occured.") - # Check what kind of error. - if (error & MFRC630_ERROR_COLLDET): - # A collision was detected... - if (coll & (1 << 7)): - collision_pos = coll & (~(1 << 7)) - self.print_debug("Collision at %x", collision_pos) - # This be a true collision... we have to select either the address - # with 1 at this position or with zero - # ISO spec says typically a 1 is added, that would mean: - # uint8_t selection = 1; - - # However, it makes sense to allow some kind of user input for this, so we use the - # current value of uid at this position, first index right byte, then shift such - # that it is in the rightmost position, ten select the last bit only. - # We cannot compensate for the addition of the cascade tag, so this really - # only works for the first cascade level, since we only know whether we had - # a cascade level at the end when the SAK was received. - choice_pos = known_bits + collision_pos - selection = (uid[((choice_pos + (cascade_level - 1) * 3) / 8)] >> ((choice_pos) % 8)) & 1 - - # We just OR this into the UID at the right position, later we - # OR the UID up to this point into uid_this_level. - uid_this_level[((choice_pos) / 8)] |= selection << ((choice_pos) % 8) - known_bits = known_bits + 1 # add the bit we just decided. - self.print_debug("Known Bits: %d" % known_bits) - - self.print_debug("uid_this_level now kb %d long: " % known_bits) - self.mfrc630_print_block(uid_this_level, 10) - else: - # Datasheet of mfrc630: - # bit 7 (CollPosValid) not set: - # Otherwise no collision is detected or - # the position of the collision is out of the range of bits CollPos. - self.print_debug("Collision but no valid collpos.") - collision_pos = 0x20 - known_bits - else: - # we got data despite an error, and no collisions, that means we can still continue. - collision_pos = 0x20 - known_bits - self.print_debug("Got data despite error: %x, setting collision_pos to: %x" % (error, collision_pos)) - elif (irq0 & MFRC630_IRQ0_RX_IRQ): - # we got data, and no collisions, that means all is well. - self.print_debug("we got data, and no collisions, that means all is well.") - collision_pos = 0x20 - known_bits - self.print_debug("Got data, no collision, setting to: %x" % collision_pos) - else: - # We have no error, nor received an RX. No response, no card? - self.print_debug("We have no error, nor received an RX. No response, no card?") - return 0 - - self.print_debug("collision_pos: %x" % collision_pos) - # read the UID Cln so far from the buffer. - rx_len = self.mfrc630_fifo_length() - buf = self.mfrc630_read_fifo(rx_len if rx_len < 5 else 5) - - self.print_debug("Fifo %d long" % rx_len) - self.mfrc630_print_block(buf, rx_len) - - self.print_debug("uid_this_level kb %d long: " % known_bits) - self.mfrc630_print_block(uid_this_level, (known_bits + 8 - 1) / 8) - - # move the buffer into the uid at this level, but OR the result such that - # we do not lose the bit we just set if we have a collision. - for rbx in range(0, rx_len): - uid_this_level[int(known_bits / 8) + rbx] = uid_this_level[int(known_bits / 8) + rbx] | buf[rbx] - self.print_debug("uid_this_level after reading buffer (known_bits=%d):" % known_bits) - self.mfrc630_print_block(uid_this_level, 0) - self.print_debug("known_bits: %x + collision_pos: %x = %x" % (known_bits, collision_pos, known_bits + collision_pos)) - known_bits = known_bits + collision_pos - self.print_debug("known_bits: %x" % known_bits) - - if known_bits >= 32: - self.print_debug("exit collision loop: uid_this_level kb %d long: " % known_bits); - self.mfrc630_print_block(uid_this_level, 10) - break; # done with collision loop - # end collission loop - - # check if the BCC matches - bcc_val = uid_this_level[4] # always at position 4, either with CT UID[0-2] or UID[0-3] in front. - bcc_calc = uid_this_level[0] ^ uid_this_level[1] ^ uid_this_level[2] ^ uid_this_level[3] - self.print_debug("BCC calc: %x" % bcc_calc) - if (bcc_val != bcc_calc): - self.print_debug("Something went wrong, BCC does not match.") - return 0 - - # clear interrupts - self.mfrc630_clear_irq0() - self.mfrc630_clear_irq1() - - send_req[0] = cmd - send_req[1] = 0x70 - send_req[2] = uid_this_level[0] - send_req[3] = uid_this_level[1] - send_req[4] = uid_this_level[2] - send_req[5] = uid_this_level[3] - send_req[6] = bcc_calc - message_length = 7 - - # Ok, almost done now, we re-enable the CRC's - self.mfrc630_write_reg(MFRC630_REG_TXCRCPRESET, MFRC630_RECOM_14443A_CRC | MFRC630_CRC_ON) - self.mfrc630_write_reg(MFRC630_REG_RXCRCCON, MFRC630_RECOM_14443A_CRC | MFRC630_CRC_ON) - - # reset the Tx and Rx registers (disable alignment, transmit full bytes) - self.mfrc630_write_reg(MFRC630_REG_TXDATANUM, (known_bits % 8) | MFRC630_TXDATANUM_DATAEN) - rxalign = 0 - self.mfrc630_write_reg(MFRC630_REG_RXBITCTRL, (0 << 7) | (rxalign << 4)) - - # actually send it! - self.mfrc630_cmd_transceive(send_req) - self.print_debug("send_req %d long: " % message_length) - self.mfrc630_print_block(send_req, message_length) - - # Block until we are done... - irq1_value = 0 - while not (irq1_value & (1 << timer_for_timeout)): - irq1_value = self.mfrc630_irq1() - if (irq1_value & MFRC630_IRQ1_GLOBAL_IRQ): # either ERR_IRQ or RX_IRQ - break # stop polling irq1 and quit the timeout loop. - self.mfrc630_cmd_idle() - - # Check the source of exiting the loop. - irq0_value = self.mfrc630_irq0() - self.print_debug("irq0: %x" % irq0_value) - if irq0_value & MFRC630_IRQ0_ERR_IRQ: - # Check what kind of error. - error = self.mfrc630_read_reg(MFRC630_REG_ERROR) - self.print_debug("error: %x" % error) - if error & MFRC630_ERROR_COLLDET: - # a collision was detected with NVB=0x70, should never happen. - self.print_debug("a collision was detected with NVB=0x70, should never happen.") - return 0 - # Read the sak answer from the fifo. - sak_len = self.mfrc630_fifo_length() - self.print_debug("sak_len: %x" % sak_len) - if sak_len != 1: - return 0 - - sak_value = self.mfrc630_read_fifo(sak_len) - - self.print_debug("SAK answer: ") - self.mfrc630_print_block(sak_value, 1) - - if (sak_value[0] & (1 << 2)): - # UID not yet complete, continue with next cascade. - # This also means the 0'th byte of the UID in this level was CT, so we - # have to shift all bytes when moving to uid from uid_this_level. - for UIDn in range(0, 3): - # uid_this_level[UIDn] = uid_this_level[UIDn + 1]; - uid[(cascade_level - 1) * 3 + UIDn] = uid_this_level[UIDn + 1] - else: - # Done according so SAK! - # Add the bytes at this level to the UID. - for UIDn in range(0, 4): - uid[(cascade_level - 1) * 3 + UIDn] = uid_this_level[UIDn]; - - # Finally, return the length of the UID that's now at the uid "pointer". - return cascade_level * 3 + 1; - - self.print_debug("Exit cascade loop nr. %d: " % cascade_level) - self.mfrc630_print_block(uid, 10) - - return 0 # getting a UID failed. - - def mfrc630_MF_auth(self, uid, key_type, block): - # Enable the right interrupts. - - # configure a timeout timer. - timer_for_timeout = 0 # should match the enabled interrupt. - - # According to datasheet Interrupt on idle and timer with MFAUTHENT, but lets - # include ERROR as well. - self.mfrc630_write_reg(MFRC630_REG_IRQ0EN, MFRC630_IRQ0EN_IDLE_IRQEN | MFRC630_IRQ0EN_ERR_IRQEN) - self.mfrc630_write_reg(MFRC630_REG_IRQ1EN, MFRC630_IRQ1EN_TIMER0_IRQEN) # only trigger on timer for irq1 - - # Set timer to 221 kHz clock, start at the end of Tx. - self.mfrc630_timer_set_control(timer_for_timeout, MFRC630_TCONTROL_CLK_211KHZ | MFRC630_TCONTROL_START_TX_END) - # Frame waiting time: FWT = (256 x 16/fc) x 2 FWI - # FWI defaults to four... so that would mean wait for a maximum of ~ 5ms - - self.mfrc630_timer_set_reload(timer_for_timeout, 2000) # 2000 ticks of 5 usec is 10 ms. - self.mfrc630_timer_set_value(timer_for_timeout, 2000) - - irq1_value = 0 - - self.mfrc630_clear_irq0() # clear irq0 - self.mfrc630_clear_irq1() # clear irq1 - - # start the authentication procedure. - self.mfrc630_cmd_auth(key_type, block, uid) - - # block until we are done - while not (irq1_value & (1 << timer_for_timeout)): - irq1_value = self.mfrc630_irq1() - if (irq1_value & MFRC630_IRQ1_GLOBAL_IRQ): - break # stop polling irq1 and quit the timeout loop. - - if (irq1_value & (1 << timer_for_timeout)): - # this indicates a timeout - return 0 # we have no authentication - - # status is always valid, it is set to 0 in case of authentication failure. - status = self.mfrc630_read_reg(MFRC630_REG_STATUS) - return (status & MFRC630_STATUS_CRYPTO1_ON) - - def mfrc630_MF_deauth(self): - self.mfrc630_write_reg(MFRC630_REG_STATUS, 0) - - def format_block(self, block, length): - ret_val = "" - for i in range(0, length): - if (block[i] < 16): - ret_val += ("0%x " % block[i]) - else: - ret_val += ("%x " % block[i]) - return ret_val.upper() diff --git a/pybytes/pyscan/lib/pycoproc.py b/pybytes/pyscan/lib/pycoproc.py deleted file mode 100644 index 0b7772b..0000000 --- a/pybytes/pyscan/lib/pycoproc.py +++ /dev/null @@ -1,294 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2019, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -from machine import Pin -from machine import I2C -import time -import pycom - -__version__ = '0.0.2' - -""" PIC MCU wakeup reason types """ -WAKE_REASON_ACCELEROMETER = 1 -WAKE_REASON_PUSH_BUTTON = 2 -WAKE_REASON_TIMER = 4 -WAKE_REASON_INT_PIN = 8 - -class Pycoproc: - """ class for handling the interaction with PIC MCU """ - - I2C_SLAVE_ADDR = const(8) - - CMD_PEEK = const(0x0) - CMD_POKE = const(0x01) - CMD_MAGIC = const(0x02) - CMD_HW_VER = const(0x10) - CMD_FW_VER = const(0x11) - CMD_PROD_ID = const(0x12) - CMD_SETUP_SLEEP = const(0x20) - CMD_GO_SLEEP = const(0x21) - CMD_CALIBRATE = const(0x22) - CMD_BAUD_CHANGE = const(0x30) - CMD_DFU = const(0x31) - - REG_CMD = const(0) - REG_ADDRL = const(1) - REG_ADDRH = const(2) - REG_AND = const(3) - REG_OR = const(4) - REG_XOR = const(5) - - ANSELA_ADDR = const(0x18C) - ANSELB_ADDR = const(0x18D) - ANSELC_ADDR = const(0x18E) - - ADCON0_ADDR = const(0x9D) - ADCON1_ADDR = const(0x9E) - - IOCAP_ADDR = const(0x391) - IOCAN_ADDR = const(0x392) - - INTCON_ADDR = const(0x0B) - OPTION_REG_ADDR = const(0x95) - - _ADCON0_CHS_POSN = const(0x02) - _ADCON0_ADON_MASK = const(0x01) - _ADCON1_ADCS_POSN = const(0x04) - _ADCON0_GO_nDONE_MASK = const(0x02) - - ADRESL_ADDR = const(0x09B) - ADRESH_ADDR = const(0x09C) - - TRISC_ADDR = const(0x08E) - - PORTA_ADDR = const(0x00C) - PORTC_ADDR = const(0x00E) - - WPUA_ADDR = const(0x20C) - - WAKE_REASON_ADDR = const(0x064C) - MEMORY_BANK_ADDR = const(0x0620) - - PCON_ADDR = const(0x096) - STATUS_ADDR = const(0x083) - - EXP_RTC_PERIOD = const(7000) - - def __init__(self, i2c=None, sda='P22', scl='P21'): - if i2c is not None: - self.i2c = i2c - else: - self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) - - self.sda = sda - self.scl = scl - self.clk_cal_factor = 1 - self.reg = bytearray(6) - self.wake_int = False - self.wake_int_pin = False - self.wake_int_pin_rising_edge = True - - # Make sure we are inserted into the - # correct board and can talk to the PIC - try: - self.read_fw_version() - except Exception as e: - raise Exception('Board not detected: {}'.format(e)) - - # init the ADC for the battery measurements - self.poke_memory(ANSELC_ADDR, 1 << 2) - self.poke_memory(ADCON0_ADDR, (0x06 << _ADCON0_CHS_POSN) | _ADCON0_ADON_MASK) - self.poke_memory(ADCON1_ADDR, (0x06 << _ADCON1_ADCS_POSN)) - # enable the pull-up on RA3 - self.poke_memory(WPUA_ADDR, (1 << 3)) - # make RC5 an input - self.set_bits_in_memory(TRISC_ADDR, 1 << 5) - # set RC6 and RC7 as outputs and enable power to the sensors and the GPS - self.mask_bits_in_memory(TRISC_ADDR, ~(1 << 6)) - self.mask_bits_in_memory(TRISC_ADDR, ~(1 << 7)) - - if self.read_fw_version() < 6: - raise ValueError('Firmware out of date') - - - def _write(self, data, wait=True): - self.i2c.writeto(I2C_SLAVE_ADDR, data) - if wait: - self._wait() - - def _read(self, size): - return self.i2c.readfrom(I2C_SLAVE_ADDR, size + 1)[1:(size + 1)] - - def _wait(self): - count = 0 - time.sleep_us(10) - while self.i2c.readfrom(I2C_SLAVE_ADDR, 1)[0] != 0xFF: - time.sleep_us(100) - count += 1 - if (count > 500): # timeout after 50ms - raise Exception('Board timeout') - - def _send_cmd(self, cmd): - self._write(bytes([cmd])) - - def read_hw_version(self): - self._send_cmd(CMD_HW_VER) - d = self._read(2) - return (d[1] << 8) + d[0] - - def read_fw_version(self): - self._send_cmd(CMD_FW_VER) - d = self._read(2) - return (d[1] << 8) + d[0] - - def read_product_id(self): - self._send_cmd(CMD_PROD_ID) - d = self._read(2) - return (d[1] << 8) + d[0] - - def peek_memory(self, addr): - self._write(bytes([CMD_PEEK, addr & 0xFF, (addr >> 8) & 0xFF])) - return self._read(1)[0] - - def poke_memory(self, addr, value): - self._write(bytes([CMD_POKE, addr & 0xFF, (addr >> 8) & 0xFF, value & 0xFF])) - - def magic_write_read(self, addr, _and=0xFF, _or=0, _xor=0): - self._write(bytes([CMD_MAGIC, addr & 0xFF, (addr >> 8) & 0xFF, _and & 0xFF, _or & 0xFF, _xor & 0xFF])) - return self._read(1)[0] - - def toggle_bits_in_memory(self, addr, bits): - self.magic_write_read(addr, _xor=bits) - - def mask_bits_in_memory(self, addr, mask): - self.magic_write_read(addr, _and=mask) - - def set_bits_in_memory(self, addr, bits): - self.magic_write_read(addr, _or=bits) - - def get_wake_reason(self): - """ returns the wakeup reason, a value out of constants WAKE_REASON_* """ - return self.peek_memory(WAKE_REASON_ADDR) - - def get_sleep_remaining(self): - """ returns the remaining time from sleep, as an interrupt (wakeup source) might have triggered """ - c3 = self.peek_memory(WAKE_REASON_ADDR + 3) - c2 = self.peek_memory(WAKE_REASON_ADDR + 2) - c1 = self.peek_memory(WAKE_REASON_ADDR + 1) - time_device_s = (c3 << 16) + (c2 << 8) + c1 - # this time is from PIC internal oscilator, so it needs to be adjusted with the calibration value - try: - self.calibrate_rtc() - except Exception: - pass - time_s = int((time_device_s / self.clk_cal_factor) + 0.5) # 0.5 used for round - return time_s - - def setup_sleep(self, time_s): - try: - self.calibrate_rtc() - except Exception: - pass - time_s = int((time_s * self.clk_cal_factor) + 0.5) # round to the nearest integer - if time_s >= 2**(8*3): - time_s = 2**(8*3)-1 - self._write(bytes([CMD_SETUP_SLEEP, time_s & 0xFF, (time_s >> 8) & 0xFF, (time_s >> 16) & 0xFF])) - - def go_to_sleep(self, gps=True): - # enable or disable back-up power to the GPS receiver - if gps: - self.set_bits_in_memory(PORTC_ADDR, 1 << 7) - else: - self.mask_bits_in_memory(PORTC_ADDR, ~(1 << 7)) - # disable the ADC - self.poke_memory(ADCON0_ADDR, 0) - - if self.wake_int: - # Don't touch RA3, RA5 or RC1 so that interrupt wake-up works - self.poke_memory(ANSELA_ADDR, ~((1 << 3) | (1 << 5))) - self.poke_memory(ANSELC_ADDR, ~((1 << 6) | (1 << 7) | (1 << 1))) - else: - # disable power to the accelerometer, and don't touch RA3 so that button wake-up works - self.poke_memory(ANSELA_ADDR, ~(1 << 3)) - self.poke_memory(ANSELC_ADDR, ~(1 << 7)) - - self.poke_memory(ANSELB_ADDR, 0xFF) - - # check if INT pin (PIC RC1), should be used for wakeup - if self.wake_int_pin: - if self.wake_int_pin_rising_edge: - self.set_bits_in_memory(OPTION_REG_ADDR, 1 << 6) # rising edge of INT pin - else: - self.mask_bits_in_memory(OPTION_REG_ADDR, ~(1 << 6)) # falling edge of INT pin - self.mask_bits_in_memory(ANSELC_ADDR, ~(1 << 1)) # disable analog function for RC1 pin - self.set_bits_in_memory(TRISC_ADDR, 1 << 1) # make RC1 input pin - self.mask_bits_in_memory(INTCON_ADDR, ~(1 << 1)) # clear INTF - self.set_bits_in_memory(INTCON_ADDR, 1 << 4) # enable interrupt; set INTE) - - self._write(bytes([CMD_GO_SLEEP]), wait=False) - # kill the run pin - Pin('P3', mode=Pin.OUT, value=0) - - def calibrate_rtc(self): - # the 1.024 factor is because the PIC LF operates at 31 KHz - # WDT has a frequency divider to generate 1 ms - # and then there is a binary prescaler, e.g., 1, 2, 4 ... 512, 1024 ms - # hence the need for the constant - self._write(bytes([CMD_CALIBRATE]), wait=False) - self.i2c.deinit() - Pin('P21', mode=Pin.IN) - pulses = pycom.pulses_get('P21', 100) - self.i2c.init(mode=I2C.MASTER, pins=(self.sda, self.scl)) - idx = 0 - for i in range(len(pulses)): - if pulses[i][1] > EXP_RTC_PERIOD: - idx = i - break - try: - period = pulses[idx][1] - pulses[(idx - 1)][1] - except: - period = 0 - if period > 0: - self.clk_cal_factor = (EXP_RTC_PERIOD / period) * (1000 / 1024) - if self.clk_cal_factor > 1.25 or self.clk_cal_factor < 0.75: - self.clk_cal_factor = 1 - - def button_pressed(self): - button = self.peek_memory(PORTA_ADDR) & (1 << 3) - return not button - - def read_battery_voltage(self): - self.set_bits_in_memory(ADCON0_ADDR, _ADCON0_GO_nDONE_MASK) - time.sleep_us(50) - while self.peek_memory(ADCON0_ADDR) & _ADCON0_GO_nDONE_MASK: - time.sleep_us(100) - adc_val = (self.peek_memory(ADRESH_ADDR) << 2) + (self.peek_memory(ADRESL_ADDR) >> 6) - return (((adc_val * 3.3 * 280) / 1023) / 180) + 0.01 # add 10mV to compensate for the drop in the FET - - def setup_int_wake_up(self, rising, falling): - """ rising is for activity detection, falling for inactivity """ - wake_int = False - if rising: - self.set_bits_in_memory(IOCAP_ADDR, 1 << 5) - wake_int = True - else: - self.mask_bits_in_memory(IOCAP_ADDR, ~(1 << 5)) - - if falling: - self.set_bits_in_memory(IOCAN_ADDR, 1 << 5) - wake_int = True - else: - self.mask_bits_in_memory(IOCAN_ADDR, ~(1 << 5)) - self.wake_int = wake_int - - def setup_int_pin_wake_up(self, rising_edge = True): - """ allows wakeup to be made by the INT pin (PIC -RC1) """ - self.wake_int_pin = True - self.wake_int_pin_rising_edge = rising_edge diff --git a/pybytes/pyscan/lib/pyscan.py b/pybytes/pyscan/lib/pyscan.py deleted file mode 100644 index 40a1830..0000000 --- a/pybytes/pyscan/lib/pyscan.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2019, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -from pycoproc import Pycoproc - -__version__ = '1.0.0' - -class Pyscan(Pycoproc): - - def __init__(self, i2c=None, sda='P22', scl='P21'): - Pycoproc.__init__(self, i2c, sda, scl) \ No newline at end of file diff --git a/pybytes/pyscan/main.py b/pybytes/pyscan/main.py deleted file mode 100644 index 26a58e9..0000000 --- a/pybytes/pyscan/main.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2019, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -''' -Simple Pyscan NFC / MiFare Classic Example -Copyright (c) 2019, Pycom Limited. -This example runs the NFC discovery loop in a thread. -If a card is detected it will read the UID and compare it to VALID_CARDS -RGB LED is BLUE while waiting for card, -GREEN if card is valid, RED if card is invalid -''' - -DEBUG = False # change to True to see debug messages - -from pyscan import Pyscan -from MFRC630 import MFRC630 -from LIS2HH12 import LIS2HH12 -from LTR329ALS01 import LTR329ALS01 -import binascii -import time -import pycom -import _thread - -VALID_CARDS = [[0x43, 0x95, 0xDD, 0xF8], - [0x43, 0x95, 0xDD, 0xF9]] - -py = Pyscan() -nfc = MFRC630(py) -lt = LTR329ALS01(py) -li = LIS2HH12(py) - -RGB_BRIGHTNESS = 0x8 - -RGB_RED = (RGB_BRIGHTNESS << 16) -RGB_GREEN = (RGB_BRIGHTNESS << 8) -RGB_BLUE = (RGB_BRIGHTNESS) - -# Make sure heartbeat is disabled before setting RGB LED -pycom.heartbeat(False) - -# Initialise the MFRC630 with some settings -nfc.mfrc630_cmd_init() - -def check_uid(uid, len): - return VALID_CARDS.count(uid[:len]) - -def print_debug(msg): - if DEBUG: - print(msg) - -def send_sensor_data(name, timeout): - while(pybytes): - pybytes.send_virtual_pin_value(True, 2, lt.light()) - pybytes.send_virtual_pin_value(True, 3, li.acceleration()) - time.sleep(timeout) - -def discovery_loop(arg1, arg2): - while(pybytes): - # Send REQA for ISO14443A card type - print_debug('Sending REQA for ISO14443A card type...') - atqa = nfc.mfrc630_iso14443a_WUPA_REQA(nfc.MFRC630_ISO14443_CMD_REQA) - print_debug('Response: {}'.format(atqa)) - if (atqa != 0): - # A card has been detected, read UID - print_debug('A card has been detected, read UID...') - uid = bytearray(10) - uid_len = nfc.mfrc630_iso14443a_select(uid) - print_debug('UID has length: {}'.format(uid_len)) - if (uid_len > 0): - print_debug('Checking if card with UID: [{:s}] is listed in VALID_CARDS...'.format(binascii.hexlify(uid[:uid_len],' ').upper())) - if (check_uid(list(uid), uid_len)) > 0: - print_debug('Card is listed, turn LED green') - pycom.rgbled(RGB_GREEN) - pybytes.send_virtual_pin_value(True, 1, ('Card is listed, Access granted', uid)) - else: - print_debug('Card is not listed, turn LED red') - pycom.rgbled(RGB_RED) - pybytes.send_virtual_pin_value(True, 1, ('Card is not listed, Access denied', uid)) - else: - # No card detected - print_debug('Did not detect any card...') - pycom.rgbled(RGB_BLUE) - pybytes.send_virtual_pin_value(True, 1, ('Did not detect any card', 0)) - nfc.mfrc630_cmd_reset() - time.sleep(5) - nfc.mfrc630_cmd_init() - - -# This is the start of our main execution... start the thread -_thread.start_new_thread(discovery_loop, (nfc, 0)) -_thread.start_new_thread(send_sensor_data, ('Thread 2',10)) diff --git a/pybytes/pysense/lib/LIS2HH12.py b/pybytes/pysense/lib/LIS2HH12.py deleted file mode 100644 index 49a6b5a..0000000 --- a/pybytes/pysense/lib/LIS2HH12.py +++ /dev/null @@ -1,178 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2019, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -import math -import time -import struct -from machine import Pin - - -FULL_SCALE_2G = const(0) -FULL_SCALE_4G = const(2) -FULL_SCALE_8G = const(3) - -ODR_POWER_DOWN = const(0) -ODR_10_HZ = const(1) -ODR_50_HZ = const(2) -ODR_100_HZ = const(3) -ODR_200_HZ = const(4) -ODR_400_HZ = const(5) -ODR_800_HZ = const(6) - -ACC_G_DIV = 1000 * 65536 - - -class LIS2HH12: - - ACC_I2CADDR = const(30) - - PRODUCTID_REG = const(0x0F) - CTRL1_REG = const(0x20) - CTRL2_REG = const(0x21) - CTRL3_REG = const(0x22) - CTRL4_REG = const(0x23) - CTRL5_REG = const(0x24) - ACC_X_L_REG = const(0x28) - ACC_X_H_REG = const(0x29) - ACC_Y_L_REG = const(0x2A) - ACC_Y_H_REG = const(0x2B) - ACC_Z_L_REG = const(0x2C) - ACC_Z_H_REG = const(0x2D) - ACT_THS = const(0x1E) - ACT_DUR = const(0x1F) - - SCALES = {FULL_SCALE_2G: 4000, FULL_SCALE_4G: 8000, FULL_SCALE_8G: 16000} - ODRS = [0, 10, 50, 100, 200, 400, 800] - - def __init__(self, pysense = None, sda = 'P22', scl = 'P21'): - if pysense is not None: - self.i2c = pysense.i2c - else: - from machine import I2C - self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) - - self.odr = 0 - self.full_scale = 0 - self.x = 0 - self.y = 0 - self.z = 0 - self.int_pin = None - self.act_dur = 0 - self.debounced = False - - whoami = self.i2c.readfrom_mem(ACC_I2CADDR , PRODUCTID_REG, 1) - if (whoami[0] != 0x41): - raise ValueError("LIS2HH12 not found") - - # enable acceleration readings at 50Hz - self.set_odr(ODR_50_HZ) - - # change the full-scale to 4g - self.set_full_scale(FULL_SCALE_4G) - - # set the interrupt pin as active low and open drain - self.set_register(CTRL5_REG, 3, 0, 3) - - # make a first read - self.acceleration() - - def acceleration(self): - x = self.i2c.readfrom_mem(ACC_I2CADDR , ACC_X_L_REG, 2) - self.x = struct.unpack(' self.SCALES[self.full_scale]: - error = "threshold %d exceeds full scale %d" % (thresold, self.SCALES[self.full_scale]) - print(error) - raise ValueError(error) - - if threshold < self.SCALES[self.full_scale] / 128: - error = "threshold %d below resolution %d" % (thresold, self.SCALES[self.full_scale]/128) - print(error) - raise ValueError(error) - - if duration > 255 * 1000 * 8 / self.ODRS[self.odr]: - error = "duration %d exceeds max possible value %d" % (duration, 255 * 1000 * 8 / self.ODRS[self.odr]) - print(error) - raise ValueError(error) - - if duration < 1000 * 8 / self.ODRS[self.odr]: - error = "duration %d below resolution %d" % (duration, 1000 * 8 / self.ODRS[self.odr]) - print(error) - raise ValueError(error) - - _ths = int(127 * threshold / self.SCALES[self.full_scale]) & 0x7F - _dur = int((duration * self.ODRS[self.odr]) / 1000 / 8) - - self.i2c.writeto_mem(ACC_I2CADDR, ACT_THS, _ths) - self.i2c.writeto_mem(ACC_I2CADDR, ACT_DUR, _dur) - - # enable the activity/inactivity interrupt - self.set_register(CTRL3_REG, 1, 5, 1) - - self._user_handler = handler - self.int_pin = Pin('P13', mode=Pin.IN) - self.int_pin.callback(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=self._int_handler) - - # return actual used thresold and duration - return (_ths * self.SCALES[self.full_scale] / 128, _dur * 8 * 1000 / self.ODRS[self.odr]) - - def activity(self): - if not self.debounced: - time.sleep_ms(self.act_dur) - self.debounced = True - if self.int_pin(): - return True - return False - - def _int_handler(self, pin_o): - if self._user_handler is not None: - self._user_handler(pin_o) - else: - if pin_o(): - print('Activity interrupt') - else: - print('Inactivity interrupt') diff --git a/pybytes/pysense/lib/LTR329ALS01.py b/pybytes/pysense/lib/LTR329ALS01.py deleted file mode 100644 index 49e3439..0000000 --- a/pybytes/pysense/lib/LTR329ALS01.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2019, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -import time -from machine import I2C - -class LTR329ALS01: - ALS_I2CADDR = const(0x29) # The device's I2C address - - ALS_CONTR_REG = const(0x80) - ALS_MEAS_RATE_REG = const(0x85) - - ALS_DATA_CH1_LOW = const(0x88) - ALS_DATA_CH1_HIGH = const(0x89) - ALS_DATA_CH0_LOW = const(0x8A) - ALS_DATA_CH0_HIGH = const(0x8B) - - ALS_GAIN_1X = const(0x00) - ALS_GAIN_2X = const(0x01) - ALS_GAIN_4X = const(0x02) - ALS_GAIN_8X = const(0x03) - ALS_GAIN_48X = const(0x06) - ALS_GAIN_96X = const(0x07) - - ALS_INT_50 = const(0x01) - ALS_INT_100 = const(0x00) - ALS_INT_150 = const(0x04) - ALS_INT_200 = const(0x02) - ALS_INT_250 = const(0x05) - ALS_INT_300 = const(0x06) - ALS_INT_350 = const(0x07) - ALS_INT_400 = const(0x03) - - ALS_RATE_50 = const(0x00) - ALS_RATE_100 = const(0x01) - ALS_RATE_200 = const(0x02) - ALS_RATE_500 = const(0x03) - ALS_RATE_1000 = const(0x04) - ALS_RATE_2000 = const(0x05) - - def __init__(self, pysense = None, sda = 'P22', scl = 'P21', gain = ALS_GAIN_1X, integration = ALS_INT_100, rate = ALS_RATE_500): - if pysense is not None: - self.i2c = pysense.i2c - else: - self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) - - contr = self._getContr(gain) - self.i2c.writeto_mem(ALS_I2CADDR, ALS_CONTR_REG, bytearray([contr])) - - measrate = self._getMeasRate(integration, rate) - self.i2c.writeto_mem(ALS_I2CADDR, ALS_MEAS_RATE_REG, bytearray([measrate])) - - time.sleep(0.01) - - def _getContr(self, gain): - return ((gain & 0x07) << 2) + 0x01 - - def _getMeasRate(self, integration, rate): - return ((integration & 0x07) << 3) + (rate & 0x07) - - def _getWord(self, high, low): - return ((high & 0xFF) << 8) + (low & 0xFF) - - def light(self): - ch1low = self.i2c.readfrom_mem(ALS_I2CADDR , ALS_DATA_CH1_LOW, 1) - ch1high = self.i2c.readfrom_mem(ALS_I2CADDR , ALS_DATA_CH1_HIGH, 1) - data1 = int(self._getWord(ch1high[0], ch1low[0])) - - ch0low = self.i2c.readfrom_mem(ALS_I2CADDR , ALS_DATA_CH0_LOW, 1) - ch0high = self.i2c.readfrom_mem(ALS_I2CADDR , ALS_DATA_CH0_HIGH, 1) - data0 = int(self._getWord(ch0high[0], ch0low[0])) - - return (data0, data1) diff --git a/pybytes/pysense/lib/MPL3115A2.py b/pybytes/pysense/lib/MPL3115A2.py deleted file mode 100644 index 28a4297..0000000 --- a/pybytes/pysense/lib/MPL3115A2.py +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2019, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -import time -from machine import I2C - -ALTITUDE = const(0) -PRESSURE = const(1) - -class MPL3115A2exception(Exception): - pass - -class MPL3115A2: - MPL3115_I2CADDR = const(0x60) - MPL3115_STATUS = const(0x00) - MPL3115_PRESSURE_DATA_MSB = const(0x01) - MPL3115_PRESSURE_DATA_CSB = const(0x02) - MPL3115_PRESSURE_DATA_LSB = const(0x03) - MPL3115_TEMP_DATA_MSB = const(0x04) - MPL3115_TEMP_DATA_LSB = const(0x05) - MPL3115_DR_STATUS = const(0x06) - MPL3115_DELTA_DATA = const(0x07) - MPL3115_WHO_AM_I = const(0x0c) - MPL3115_FIFO_STATUS = const(0x0d) - MPL3115_FIFO_DATA = const(0x0e) - MPL3115_FIFO_SETUP = const(0x0e) - MPL3115_TIME_DELAY = const(0x10) - MPL3115_SYS_MODE = const(0x11) - MPL3115_INT_SORCE = const(0x12) - MPL3115_PT_DATA_CFG = const(0x13) - MPL3115_BAR_IN_MSB = const(0x14) - MPL3115_P_ARLARM_MSB = const(0x16) - MPL3115_T_ARLARM = const(0x18) - MPL3115_P_ARLARM_WND_MSB = const(0x19) - MPL3115_T_ARLARM_WND = const(0x1b) - MPL3115_P_MIN_DATA = const(0x1c) - MPL3115_T_MIN_DATA = const(0x1f) - MPL3115_P_MAX_DATA = const(0x21) - MPL3115_T_MAX_DATA = const(0x24) - MPL3115_CTRL_REG1 = const(0x26) - MPL3115_CTRL_REG2 = const(0x27) - MPL3115_CTRL_REG3 = const(0x28) - MPL3115_CTRL_REG4 = const(0x29) - MPL3115_CTRL_REG5 = const(0x2a) - MPL3115_OFFSET_P = const(0x2b) - MPL3115_OFFSET_T = const(0x2c) - MPL3115_OFFSET_H = const(0x2d) - - def __init__(self, pysense = None, sda = 'P22', scl = 'P21', mode = PRESSURE): - if pysense is not None: - self.i2c = pysense.i2c - else: - self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) - - self.STA_reg = bytearray(1) - self.mode = mode - - if self.mode is PRESSURE: - self.i2c.writeto_mem(MPL3115_I2CADDR, MPL3115_CTRL_REG1, bytes([0x38])) # barometer mode, not raw, oversampling 128, minimum time 512 ms - self.i2c.writeto_mem(MPL3115_I2CADDR, MPL3115_PT_DATA_CFG, bytes([0x07])) # no events detected - self.i2c.writeto_mem(MPL3115_I2CADDR, MPL3115_CTRL_REG1, bytes([0x39])) # active - elif self.mode is ALTITUDE: - self.i2c.writeto_mem(MPL3115_I2CADDR, MPL3115_CTRL_REG1, bytes([0xB8])) # altitude mode, not raw, oversampling 128, minimum time 512 ms - self.i2c.writeto_mem(MPL3115_I2CADDR, MPL3115_PT_DATA_CFG, bytes([0x07])) # no events detected - self.i2c.writeto_mem(MPL3115_I2CADDR, MPL3115_CTRL_REG1, bytes([0xB9])) # active - else: - raise MPL3115A2exception("Invalid Mode MPL3115A2") - - if self._read_status(): - pass - else: - raise MPL3115A2exception("Error with MPL3115A2") - - def _read_status(self): - while True: - self.i2c.readfrom_mem_into(MPL3115_I2CADDR, MPL3115_STATUS, self.STA_reg) - - if(self.STA_reg[0] == 0): - time.sleep(0.01) - pass - elif(self.STA_reg[0] & 0x04) == 4: - return True - else: - return False - - def pressure(self): - if self.mode == ALTITUDE: - raise MPL3115A2exception("Incorrect Measurement Mode MPL3115A2") - - OUT_P_MSB = self.i2c.readfrom_mem(MPL3115_I2CADDR, MPL3115_PRESSURE_DATA_MSB,1) - OUT_P_CSB = self.i2c.readfrom_mem(MPL3115_I2CADDR, MPL3115_PRESSURE_DATA_CSB,1) - OUT_P_LSB = self.i2c.readfrom_mem(MPL3115_I2CADDR, MPL3115_PRESSURE_DATA_LSB,1) - - 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) - - def altitude(self): - if self.mode == PRESSURE: - raise MPL3115A2exception("Incorrect Measurement Mode MPL3115A2") - - OUT_P_MSB = self.i2c.readfrom_mem(MPL3115_I2CADDR, MPL3115_PRESSURE_DATA_MSB,1) - OUT_P_CSB = self.i2c.readfrom_mem(MPL3115_I2CADDR, MPL3115_PRESSURE_DATA_CSB,1) - OUT_P_LSB = self.i2c.readfrom_mem(MPL3115_I2CADDR, MPL3115_PRESSURE_DATA_LSB,1) - - alt_int = (OUT_P_MSB[0] << 8) + (OUT_P_CSB[0]) - alt_frac = ((OUT_P_LSB[0] >> 4) & 0x0F) - - if alt_int > 32767: - alt_int -= 65536 - - return float(alt_int + alt_frac / 16.0) - - def temperature(self): - OUT_T_MSB = self.i2c.readfrom_mem(MPL3115_I2CADDR, MPL3115_TEMP_DATA_MSB,1) - OUT_T_LSB = self.i2c.readfrom_mem(MPL3115_I2CADDR, MPL3115_TEMP_DATA_LSB,1) - - temp_int = OUT_T_MSB[0] - temp_frac = OUT_T_LSB[0] - - if temp_int > 127: - temp_int -= 256 - - return float(temp_int + temp_frac / 256.0) diff --git a/pybytes/pysense/lib/SI7006A20.py b/pybytes/pysense/lib/SI7006A20.py deleted file mode 100644 index d9a4025..0000000 --- a/pybytes/pysense/lib/SI7006A20.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2019, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -import time -from machine import I2C -import math - -__version__ = '0.0.2' - -class SI7006A20: - """ class for handling the temperature sensor SI7006-A20 - +/- 1 deg C error for temperature - +/- 5% error for relative humidity - datasheet available at https://www.silabs.com/documents/public/data-sheets/Si7006-A20.pdf """ - - SI7006A20_I2C_ADDR = const(0x40) - - TEMP_NOHOLDMASTER = const(0xF3) - HUMD_NOHOLDMASTER = const(0xF5) - - def __init__(self, pysense = None, sda = 'P22', scl = 'P21'): - if pysense is not None: - self.i2c = pysense.i2c - else: - self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) - - def _getWord(self, high, low): - return ((high & 0xFF) << 8) + (low & 0xFF) - - def temperature(self): - """ obtaining the temperature(degrees Celsius) measured by sensor """ - self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([0xF3])) - time.sleep(0.5) - data = self.i2c.readfrom(SI7006A20_I2C_ADDR, 3) - #print("CRC Raw temp data: " + hex(data[0]*65536 + data[1]*256 + data[2])) - data = self._getWord(data[0], data[1]) - temp = ((175.72 * data) / 65536.0) - 46.85 - return temp - - def humidity(self): - """ obtaining the relative humidity(%) measured by sensor """ - self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([0xF5])) - time.sleep(0.5) - data = self.i2c.readfrom(SI7006A20_I2C_ADDR, 2) - data = self._getWord(data[0], data[1]) - humidity = ((125.0 * data) / 65536.0) - 6.0 - return humidity - - def read_user_reg(self): - """ reading the user configuration register """ - self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([0xE7])) - time.sleep(0.5) - data = self.i2c.readfrom(SI7006A20_I2C_ADDR, 1) - return data[0] - - def read_heater_reg(self): - """ reading the heater configuration register """ - self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([0x11])) - time.sleep(0.5) - data = self.i2c.readfrom(SI7006A20_I2C_ADDR, 1) - return data[0] - - def read_electronic_id(self): - """ reading electronic identifier """ - self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([0xFA]) + bytearray([0x0F])) - time.sleep(0.5) - sna = self.i2c.readfrom(SI7006A20_I2C_ADDR, 4) - time.sleep(0.1) - self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([0xFC]) + bytearray([0xC9])) - time.sleep(0.5) - snb = self.i2c.readfrom(SI7006A20_I2C_ADDR, 4) - return [sna[0], sna[1], sna[2], sna[3], snb[0], snb[1], snb[2], snb[3]] - - def read_firmware(self): - """ reading firmware version """ - self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([0x84])+ bytearray([0xB8])) - time.sleep(0.5) - fw = self.i2c.readfrom(SI7006A20_I2C_ADDR, 1) - return fw[0] - - def read_reg(self, reg_addr): - """ reading a register """ - self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([reg_addr])) - time.sleep(0.5) - data = self.i2c.readfrom(SI7006A20_I2C_ADDR, 1) - return data[0] - - def write_reg(self, reg_addr, value): - """ writing a register """ - self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([reg_addr])+bytearray([value])) - time.sleep(0.1) - - def dew_point(self): - """ computing the dew pointe temperature (deg C) for the current Temperature and Humidity measured pair - at dew-point temperature the relative humidity is 100% """ - temp = self.temperature() - humid = self.humidity() - h = (math.log(humid, 10) - 2) / 0.4343 + (17.62 * temp) / (243.12 + temp) - dew_p = 243.12 * h / (17.62 - h) - return dew_p - - def humid_ambient(self, t_ambient, dew_p = None): - """ returns the relative humidity compensated for the current Ambient temperature - for ex: T-Ambient is 24.4 degC, but sensor indicates Temperature = 31.65 degC and Humidity = 47.3% - -> then the actual Relative Humidity is 72.2% - this is computed because the dew-point should be the same """ - if dew_p is None: - dew_p = self.dew_point() - h = 17.62 * dew_p / (243.12 + dew_p) - h_ambient = math.pow(10, (h - (17.62 * t_ambient) / (243.12 + t_ambient)) * 0.4343 + 2) - return h_ambient diff --git a/pybytes/pysense/lib/pycoproc.py b/pybytes/pysense/lib/pycoproc.py deleted file mode 100644 index 0b7772b..0000000 --- a/pybytes/pysense/lib/pycoproc.py +++ /dev/null @@ -1,294 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2019, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -from machine import Pin -from machine import I2C -import time -import pycom - -__version__ = '0.0.2' - -""" PIC MCU wakeup reason types """ -WAKE_REASON_ACCELEROMETER = 1 -WAKE_REASON_PUSH_BUTTON = 2 -WAKE_REASON_TIMER = 4 -WAKE_REASON_INT_PIN = 8 - -class Pycoproc: - """ class for handling the interaction with PIC MCU """ - - I2C_SLAVE_ADDR = const(8) - - CMD_PEEK = const(0x0) - CMD_POKE = const(0x01) - CMD_MAGIC = const(0x02) - CMD_HW_VER = const(0x10) - CMD_FW_VER = const(0x11) - CMD_PROD_ID = const(0x12) - CMD_SETUP_SLEEP = const(0x20) - CMD_GO_SLEEP = const(0x21) - CMD_CALIBRATE = const(0x22) - CMD_BAUD_CHANGE = const(0x30) - CMD_DFU = const(0x31) - - REG_CMD = const(0) - REG_ADDRL = const(1) - REG_ADDRH = const(2) - REG_AND = const(3) - REG_OR = const(4) - REG_XOR = const(5) - - ANSELA_ADDR = const(0x18C) - ANSELB_ADDR = const(0x18D) - ANSELC_ADDR = const(0x18E) - - ADCON0_ADDR = const(0x9D) - ADCON1_ADDR = const(0x9E) - - IOCAP_ADDR = const(0x391) - IOCAN_ADDR = const(0x392) - - INTCON_ADDR = const(0x0B) - OPTION_REG_ADDR = const(0x95) - - _ADCON0_CHS_POSN = const(0x02) - _ADCON0_ADON_MASK = const(0x01) - _ADCON1_ADCS_POSN = const(0x04) - _ADCON0_GO_nDONE_MASK = const(0x02) - - ADRESL_ADDR = const(0x09B) - ADRESH_ADDR = const(0x09C) - - TRISC_ADDR = const(0x08E) - - PORTA_ADDR = const(0x00C) - PORTC_ADDR = const(0x00E) - - WPUA_ADDR = const(0x20C) - - WAKE_REASON_ADDR = const(0x064C) - MEMORY_BANK_ADDR = const(0x0620) - - PCON_ADDR = const(0x096) - STATUS_ADDR = const(0x083) - - EXP_RTC_PERIOD = const(7000) - - def __init__(self, i2c=None, sda='P22', scl='P21'): - if i2c is not None: - self.i2c = i2c - else: - self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) - - self.sda = sda - self.scl = scl - self.clk_cal_factor = 1 - self.reg = bytearray(6) - self.wake_int = False - self.wake_int_pin = False - self.wake_int_pin_rising_edge = True - - # Make sure we are inserted into the - # correct board and can talk to the PIC - try: - self.read_fw_version() - except Exception as e: - raise Exception('Board not detected: {}'.format(e)) - - # init the ADC for the battery measurements - self.poke_memory(ANSELC_ADDR, 1 << 2) - self.poke_memory(ADCON0_ADDR, (0x06 << _ADCON0_CHS_POSN) | _ADCON0_ADON_MASK) - self.poke_memory(ADCON1_ADDR, (0x06 << _ADCON1_ADCS_POSN)) - # enable the pull-up on RA3 - self.poke_memory(WPUA_ADDR, (1 << 3)) - # make RC5 an input - self.set_bits_in_memory(TRISC_ADDR, 1 << 5) - # set RC6 and RC7 as outputs and enable power to the sensors and the GPS - self.mask_bits_in_memory(TRISC_ADDR, ~(1 << 6)) - self.mask_bits_in_memory(TRISC_ADDR, ~(1 << 7)) - - if self.read_fw_version() < 6: - raise ValueError('Firmware out of date') - - - def _write(self, data, wait=True): - self.i2c.writeto(I2C_SLAVE_ADDR, data) - if wait: - self._wait() - - def _read(self, size): - return self.i2c.readfrom(I2C_SLAVE_ADDR, size + 1)[1:(size + 1)] - - def _wait(self): - count = 0 - time.sleep_us(10) - while self.i2c.readfrom(I2C_SLAVE_ADDR, 1)[0] != 0xFF: - time.sleep_us(100) - count += 1 - if (count > 500): # timeout after 50ms - raise Exception('Board timeout') - - def _send_cmd(self, cmd): - self._write(bytes([cmd])) - - def read_hw_version(self): - self._send_cmd(CMD_HW_VER) - d = self._read(2) - return (d[1] << 8) + d[0] - - def read_fw_version(self): - self._send_cmd(CMD_FW_VER) - d = self._read(2) - return (d[1] << 8) + d[0] - - def read_product_id(self): - self._send_cmd(CMD_PROD_ID) - d = self._read(2) - return (d[1] << 8) + d[0] - - def peek_memory(self, addr): - self._write(bytes([CMD_PEEK, addr & 0xFF, (addr >> 8) & 0xFF])) - return self._read(1)[0] - - def poke_memory(self, addr, value): - self._write(bytes([CMD_POKE, addr & 0xFF, (addr >> 8) & 0xFF, value & 0xFF])) - - def magic_write_read(self, addr, _and=0xFF, _or=0, _xor=0): - self._write(bytes([CMD_MAGIC, addr & 0xFF, (addr >> 8) & 0xFF, _and & 0xFF, _or & 0xFF, _xor & 0xFF])) - return self._read(1)[0] - - def toggle_bits_in_memory(self, addr, bits): - self.magic_write_read(addr, _xor=bits) - - def mask_bits_in_memory(self, addr, mask): - self.magic_write_read(addr, _and=mask) - - def set_bits_in_memory(self, addr, bits): - self.magic_write_read(addr, _or=bits) - - def get_wake_reason(self): - """ returns the wakeup reason, a value out of constants WAKE_REASON_* """ - return self.peek_memory(WAKE_REASON_ADDR) - - def get_sleep_remaining(self): - """ returns the remaining time from sleep, as an interrupt (wakeup source) might have triggered """ - c3 = self.peek_memory(WAKE_REASON_ADDR + 3) - c2 = self.peek_memory(WAKE_REASON_ADDR + 2) - c1 = self.peek_memory(WAKE_REASON_ADDR + 1) - time_device_s = (c3 << 16) + (c2 << 8) + c1 - # this time is from PIC internal oscilator, so it needs to be adjusted with the calibration value - try: - self.calibrate_rtc() - except Exception: - pass - time_s = int((time_device_s / self.clk_cal_factor) + 0.5) # 0.5 used for round - return time_s - - def setup_sleep(self, time_s): - try: - self.calibrate_rtc() - except Exception: - pass - time_s = int((time_s * self.clk_cal_factor) + 0.5) # round to the nearest integer - if time_s >= 2**(8*3): - time_s = 2**(8*3)-1 - self._write(bytes([CMD_SETUP_SLEEP, time_s & 0xFF, (time_s >> 8) & 0xFF, (time_s >> 16) & 0xFF])) - - def go_to_sleep(self, gps=True): - # enable or disable back-up power to the GPS receiver - if gps: - self.set_bits_in_memory(PORTC_ADDR, 1 << 7) - else: - self.mask_bits_in_memory(PORTC_ADDR, ~(1 << 7)) - # disable the ADC - self.poke_memory(ADCON0_ADDR, 0) - - if self.wake_int: - # Don't touch RA3, RA5 or RC1 so that interrupt wake-up works - self.poke_memory(ANSELA_ADDR, ~((1 << 3) | (1 << 5))) - self.poke_memory(ANSELC_ADDR, ~((1 << 6) | (1 << 7) | (1 << 1))) - else: - # disable power to the accelerometer, and don't touch RA3 so that button wake-up works - self.poke_memory(ANSELA_ADDR, ~(1 << 3)) - self.poke_memory(ANSELC_ADDR, ~(1 << 7)) - - self.poke_memory(ANSELB_ADDR, 0xFF) - - # check if INT pin (PIC RC1), should be used for wakeup - if self.wake_int_pin: - if self.wake_int_pin_rising_edge: - self.set_bits_in_memory(OPTION_REG_ADDR, 1 << 6) # rising edge of INT pin - else: - self.mask_bits_in_memory(OPTION_REG_ADDR, ~(1 << 6)) # falling edge of INT pin - self.mask_bits_in_memory(ANSELC_ADDR, ~(1 << 1)) # disable analog function for RC1 pin - self.set_bits_in_memory(TRISC_ADDR, 1 << 1) # make RC1 input pin - self.mask_bits_in_memory(INTCON_ADDR, ~(1 << 1)) # clear INTF - self.set_bits_in_memory(INTCON_ADDR, 1 << 4) # enable interrupt; set INTE) - - self._write(bytes([CMD_GO_SLEEP]), wait=False) - # kill the run pin - Pin('P3', mode=Pin.OUT, value=0) - - def calibrate_rtc(self): - # the 1.024 factor is because the PIC LF operates at 31 KHz - # WDT has a frequency divider to generate 1 ms - # and then there is a binary prescaler, e.g., 1, 2, 4 ... 512, 1024 ms - # hence the need for the constant - self._write(bytes([CMD_CALIBRATE]), wait=False) - self.i2c.deinit() - Pin('P21', mode=Pin.IN) - pulses = pycom.pulses_get('P21', 100) - self.i2c.init(mode=I2C.MASTER, pins=(self.sda, self.scl)) - idx = 0 - for i in range(len(pulses)): - if pulses[i][1] > EXP_RTC_PERIOD: - idx = i - break - try: - period = pulses[idx][1] - pulses[(idx - 1)][1] - except: - period = 0 - if period > 0: - self.clk_cal_factor = (EXP_RTC_PERIOD / period) * (1000 / 1024) - if self.clk_cal_factor > 1.25 or self.clk_cal_factor < 0.75: - self.clk_cal_factor = 1 - - def button_pressed(self): - button = self.peek_memory(PORTA_ADDR) & (1 << 3) - return not button - - def read_battery_voltage(self): - self.set_bits_in_memory(ADCON0_ADDR, _ADCON0_GO_nDONE_MASK) - time.sleep_us(50) - while self.peek_memory(ADCON0_ADDR) & _ADCON0_GO_nDONE_MASK: - time.sleep_us(100) - adc_val = (self.peek_memory(ADRESH_ADDR) << 2) + (self.peek_memory(ADRESL_ADDR) >> 6) - return (((adc_val * 3.3 * 280) / 1023) / 180) + 0.01 # add 10mV to compensate for the drop in the FET - - def setup_int_wake_up(self, rising, falling): - """ rising is for activity detection, falling for inactivity """ - wake_int = False - if rising: - self.set_bits_in_memory(IOCAP_ADDR, 1 << 5) - wake_int = True - else: - self.mask_bits_in_memory(IOCAP_ADDR, ~(1 << 5)) - - if falling: - self.set_bits_in_memory(IOCAN_ADDR, 1 << 5) - wake_int = True - else: - self.mask_bits_in_memory(IOCAN_ADDR, ~(1 << 5)) - self.wake_int = wake_int - - def setup_int_pin_wake_up(self, rising_edge = True): - """ allows wakeup to be made by the INT pin (PIC -RC1) """ - self.wake_int_pin = True - self.wake_int_pin_rising_edge = rising_edge diff --git a/pybytes/pysense/lib/pysense.py b/pybytes/pysense/lib/pysense.py deleted file mode 100644 index 04d31e7..0000000 --- a/pybytes/pysense/lib/pysense.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2019, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -from pycoproc import Pycoproc - -__version__ = '1.4.0' - -class Pysense(Pycoproc): - - def __init__(self, i2c=None, sda='P22', scl='P21'): - Pycoproc.__init__(self, i2c, sda, scl) diff --git a/pybytes/pysense/main.py b/pybytes/pysense/main.py deleted file mode 100644 index babb9c3..0000000 --- a/pybytes/pysense/main.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2019, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -# See https://docs.pycom.io for more information regarding library specifics - -from pysense import Pysense -from LIS2HH12 import LIS2HH12 -from SI7006A20 import SI7006A20 -from LTR329ALS01 import LTR329ALS01 -from MPL3115A2 import MPL3115A2,ALTITUDE,PRESSURE - -py = Pysense() -mp = MPL3115A2(py,mode=ALTITUDE) # Returns height in meters. Mode may also be set to PRESSURE, returning a value in Pascals -si = SI7006A20(py) -lt = LTR329ALS01(py) -li = LIS2HH12(py) - -import _thread -from time import sleep -import uos - -def send_sensor_data(): - while (pybytes): - pybytes.send_virtual_pin_value(True, 1, si.humidity()) - pybytes.send_virtual_pin_value(True, 2, si.temperature()) - pybytes.send_virtual_pin_value(True, 3, lt.light()) - sleep(10) - -_thread.start_new_thread(send_sensor_data, ()) diff --git a/pybytes/pytrack/lib/L76GNSS.py b/pybytes/pytrack/lib/L76GNSS.py deleted file mode 100644 index 8b6fca6..0000000 --- a/pybytes/pytrack/lib/L76GNSS.py +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2019, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -from machine import Timer -import time -import gc -import binascii - - -class L76GNSS: - - GPS_I2CADDR = const(0x10) - - def __init__(self, pytrack=None, sda='P22', scl='P21', timeout=None): - if pytrack is not None: - self.i2c = pytrack.i2c - else: - from machine import I2C - self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) - - self.chrono = Timer.Chrono() - - self.timeout = timeout - self.timeout_status = True - - self.reg = bytearray(1) - self.i2c.writeto(GPS_I2CADDR, self.reg) - - def _read(self): - self.reg = self.i2c.readfrom(GPS_I2CADDR, 64) - return self.reg - - def _convert_coords(self, gngll_s): - lat = gngll_s[1] - lat_d = (float(lat) // 100) + ((float(lat) % 100) / 60) - lon = gngll_s[3] - lon_d = (float(lon) // 100) + ((float(lon) % 100) / 60) - if gngll_s[2] == 'S': - lat_d *= -1 - if gngll_s[4] == 'W': - lon_d *= -1 - return(lat_d, lon_d) - - def coordinates(self, debug=False): - lat_d, lon_d, debug_timeout = None, None, False - if self.timeout is not None: - self.chrono.reset() - self.chrono.start() - nmea = b'' - while True: - if self.timeout is not None and self.chrono.read() >= self.timeout: - self.chrono.stop() - chrono_timeout = self.chrono.read() - self.chrono.reset() - self.timeout_status = False - debug_timeout = True - if not self.timeout_status: - gc.collect() - break - nmea += self._read().lstrip(b'\n\n').rstrip(b'\n\n') - gngll_idx = nmea.find(b'GNGLL') - gpgll_idx = nmea.find(b'GPGLL') - if gngll_idx < 0 and gpgll_idx >= 0: - gngll_idx = gpgll_idx - if gngll_idx >= 0: - gngll = nmea[gngll_idx:] - e_idx = gngll.find(b'\r\n') - if e_idx >= 0: - try: - gngll = gngll[:e_idx].decode('ascii') - gngll_s = gngll.split(',') - lat_d, lon_d = self._convert_coords(gngll_s) - except Exception: - pass - finally: - nmea = nmea[(gngll_idx + e_idx):] - gc.collect() - break - else: - gc.collect() - if len(nmea) > 410: # i suppose it can be safely changed to 82, which is longest NMEA frame - nmea = nmea[-5:] # $GNGL without last L - time.sleep(0.1) - self.timeout_status = True - if debug and debug_timeout: - print('GPS timed out after %f seconds' % (chrono_timeout)) - return(None, None) - else: - return(lat_d, lon_d) diff --git a/pybytes/pytrack/lib/LIS2HH12.py b/pybytes/pytrack/lib/LIS2HH12.py deleted file mode 100644 index 49a6b5a..0000000 --- a/pybytes/pytrack/lib/LIS2HH12.py +++ /dev/null @@ -1,178 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2019, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -import math -import time -import struct -from machine import Pin - - -FULL_SCALE_2G = const(0) -FULL_SCALE_4G = const(2) -FULL_SCALE_8G = const(3) - -ODR_POWER_DOWN = const(0) -ODR_10_HZ = const(1) -ODR_50_HZ = const(2) -ODR_100_HZ = const(3) -ODR_200_HZ = const(4) -ODR_400_HZ = const(5) -ODR_800_HZ = const(6) - -ACC_G_DIV = 1000 * 65536 - - -class LIS2HH12: - - ACC_I2CADDR = const(30) - - PRODUCTID_REG = const(0x0F) - CTRL1_REG = const(0x20) - CTRL2_REG = const(0x21) - CTRL3_REG = const(0x22) - CTRL4_REG = const(0x23) - CTRL5_REG = const(0x24) - ACC_X_L_REG = const(0x28) - ACC_X_H_REG = const(0x29) - ACC_Y_L_REG = const(0x2A) - ACC_Y_H_REG = const(0x2B) - ACC_Z_L_REG = const(0x2C) - ACC_Z_H_REG = const(0x2D) - ACT_THS = const(0x1E) - ACT_DUR = const(0x1F) - - SCALES = {FULL_SCALE_2G: 4000, FULL_SCALE_4G: 8000, FULL_SCALE_8G: 16000} - ODRS = [0, 10, 50, 100, 200, 400, 800] - - def __init__(self, pysense = None, sda = 'P22', scl = 'P21'): - if pysense is not None: - self.i2c = pysense.i2c - else: - from machine import I2C - self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) - - self.odr = 0 - self.full_scale = 0 - self.x = 0 - self.y = 0 - self.z = 0 - self.int_pin = None - self.act_dur = 0 - self.debounced = False - - whoami = self.i2c.readfrom_mem(ACC_I2CADDR , PRODUCTID_REG, 1) - if (whoami[0] != 0x41): - raise ValueError("LIS2HH12 not found") - - # enable acceleration readings at 50Hz - self.set_odr(ODR_50_HZ) - - # change the full-scale to 4g - self.set_full_scale(FULL_SCALE_4G) - - # set the interrupt pin as active low and open drain - self.set_register(CTRL5_REG, 3, 0, 3) - - # make a first read - self.acceleration() - - def acceleration(self): - x = self.i2c.readfrom_mem(ACC_I2CADDR , ACC_X_L_REG, 2) - self.x = struct.unpack(' self.SCALES[self.full_scale]: - error = "threshold %d exceeds full scale %d" % (thresold, self.SCALES[self.full_scale]) - print(error) - raise ValueError(error) - - if threshold < self.SCALES[self.full_scale] / 128: - error = "threshold %d below resolution %d" % (thresold, self.SCALES[self.full_scale]/128) - print(error) - raise ValueError(error) - - if duration > 255 * 1000 * 8 / self.ODRS[self.odr]: - error = "duration %d exceeds max possible value %d" % (duration, 255 * 1000 * 8 / self.ODRS[self.odr]) - print(error) - raise ValueError(error) - - if duration < 1000 * 8 / self.ODRS[self.odr]: - error = "duration %d below resolution %d" % (duration, 1000 * 8 / self.ODRS[self.odr]) - print(error) - raise ValueError(error) - - _ths = int(127 * threshold / self.SCALES[self.full_scale]) & 0x7F - _dur = int((duration * self.ODRS[self.odr]) / 1000 / 8) - - self.i2c.writeto_mem(ACC_I2CADDR, ACT_THS, _ths) - self.i2c.writeto_mem(ACC_I2CADDR, ACT_DUR, _dur) - - # enable the activity/inactivity interrupt - self.set_register(CTRL3_REG, 1, 5, 1) - - self._user_handler = handler - self.int_pin = Pin('P13', mode=Pin.IN) - self.int_pin.callback(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=self._int_handler) - - # return actual used thresold and duration - return (_ths * self.SCALES[self.full_scale] / 128, _dur * 8 * 1000 / self.ODRS[self.odr]) - - def activity(self): - if not self.debounced: - time.sleep_ms(self.act_dur) - self.debounced = True - if self.int_pin(): - return True - return False - - def _int_handler(self, pin_o): - if self._user_handler is not None: - self._user_handler(pin_o) - else: - if pin_o(): - print('Activity interrupt') - else: - print('Inactivity interrupt') diff --git a/pybytes/pytrack/lib/pycoproc.py b/pybytes/pytrack/lib/pycoproc.py deleted file mode 100644 index 0b7772b..0000000 --- a/pybytes/pytrack/lib/pycoproc.py +++ /dev/null @@ -1,294 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2019, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -from machine import Pin -from machine import I2C -import time -import pycom - -__version__ = '0.0.2' - -""" PIC MCU wakeup reason types """ -WAKE_REASON_ACCELEROMETER = 1 -WAKE_REASON_PUSH_BUTTON = 2 -WAKE_REASON_TIMER = 4 -WAKE_REASON_INT_PIN = 8 - -class Pycoproc: - """ class for handling the interaction with PIC MCU """ - - I2C_SLAVE_ADDR = const(8) - - CMD_PEEK = const(0x0) - CMD_POKE = const(0x01) - CMD_MAGIC = const(0x02) - CMD_HW_VER = const(0x10) - CMD_FW_VER = const(0x11) - CMD_PROD_ID = const(0x12) - CMD_SETUP_SLEEP = const(0x20) - CMD_GO_SLEEP = const(0x21) - CMD_CALIBRATE = const(0x22) - CMD_BAUD_CHANGE = const(0x30) - CMD_DFU = const(0x31) - - REG_CMD = const(0) - REG_ADDRL = const(1) - REG_ADDRH = const(2) - REG_AND = const(3) - REG_OR = const(4) - REG_XOR = const(5) - - ANSELA_ADDR = const(0x18C) - ANSELB_ADDR = const(0x18D) - ANSELC_ADDR = const(0x18E) - - ADCON0_ADDR = const(0x9D) - ADCON1_ADDR = const(0x9E) - - IOCAP_ADDR = const(0x391) - IOCAN_ADDR = const(0x392) - - INTCON_ADDR = const(0x0B) - OPTION_REG_ADDR = const(0x95) - - _ADCON0_CHS_POSN = const(0x02) - _ADCON0_ADON_MASK = const(0x01) - _ADCON1_ADCS_POSN = const(0x04) - _ADCON0_GO_nDONE_MASK = const(0x02) - - ADRESL_ADDR = const(0x09B) - ADRESH_ADDR = const(0x09C) - - TRISC_ADDR = const(0x08E) - - PORTA_ADDR = const(0x00C) - PORTC_ADDR = const(0x00E) - - WPUA_ADDR = const(0x20C) - - WAKE_REASON_ADDR = const(0x064C) - MEMORY_BANK_ADDR = const(0x0620) - - PCON_ADDR = const(0x096) - STATUS_ADDR = const(0x083) - - EXP_RTC_PERIOD = const(7000) - - def __init__(self, i2c=None, sda='P22', scl='P21'): - if i2c is not None: - self.i2c = i2c - else: - self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) - - self.sda = sda - self.scl = scl - self.clk_cal_factor = 1 - self.reg = bytearray(6) - self.wake_int = False - self.wake_int_pin = False - self.wake_int_pin_rising_edge = True - - # Make sure we are inserted into the - # correct board and can talk to the PIC - try: - self.read_fw_version() - except Exception as e: - raise Exception('Board not detected: {}'.format(e)) - - # init the ADC for the battery measurements - self.poke_memory(ANSELC_ADDR, 1 << 2) - self.poke_memory(ADCON0_ADDR, (0x06 << _ADCON0_CHS_POSN) | _ADCON0_ADON_MASK) - self.poke_memory(ADCON1_ADDR, (0x06 << _ADCON1_ADCS_POSN)) - # enable the pull-up on RA3 - self.poke_memory(WPUA_ADDR, (1 << 3)) - # make RC5 an input - self.set_bits_in_memory(TRISC_ADDR, 1 << 5) - # set RC6 and RC7 as outputs and enable power to the sensors and the GPS - self.mask_bits_in_memory(TRISC_ADDR, ~(1 << 6)) - self.mask_bits_in_memory(TRISC_ADDR, ~(1 << 7)) - - if self.read_fw_version() < 6: - raise ValueError('Firmware out of date') - - - def _write(self, data, wait=True): - self.i2c.writeto(I2C_SLAVE_ADDR, data) - if wait: - self._wait() - - def _read(self, size): - return self.i2c.readfrom(I2C_SLAVE_ADDR, size + 1)[1:(size + 1)] - - def _wait(self): - count = 0 - time.sleep_us(10) - while self.i2c.readfrom(I2C_SLAVE_ADDR, 1)[0] != 0xFF: - time.sleep_us(100) - count += 1 - if (count > 500): # timeout after 50ms - raise Exception('Board timeout') - - def _send_cmd(self, cmd): - self._write(bytes([cmd])) - - def read_hw_version(self): - self._send_cmd(CMD_HW_VER) - d = self._read(2) - return (d[1] << 8) + d[0] - - def read_fw_version(self): - self._send_cmd(CMD_FW_VER) - d = self._read(2) - return (d[1] << 8) + d[0] - - def read_product_id(self): - self._send_cmd(CMD_PROD_ID) - d = self._read(2) - return (d[1] << 8) + d[0] - - def peek_memory(self, addr): - self._write(bytes([CMD_PEEK, addr & 0xFF, (addr >> 8) & 0xFF])) - return self._read(1)[0] - - def poke_memory(self, addr, value): - self._write(bytes([CMD_POKE, addr & 0xFF, (addr >> 8) & 0xFF, value & 0xFF])) - - def magic_write_read(self, addr, _and=0xFF, _or=0, _xor=0): - self._write(bytes([CMD_MAGIC, addr & 0xFF, (addr >> 8) & 0xFF, _and & 0xFF, _or & 0xFF, _xor & 0xFF])) - return self._read(1)[0] - - def toggle_bits_in_memory(self, addr, bits): - self.magic_write_read(addr, _xor=bits) - - def mask_bits_in_memory(self, addr, mask): - self.magic_write_read(addr, _and=mask) - - def set_bits_in_memory(self, addr, bits): - self.magic_write_read(addr, _or=bits) - - def get_wake_reason(self): - """ returns the wakeup reason, a value out of constants WAKE_REASON_* """ - return self.peek_memory(WAKE_REASON_ADDR) - - def get_sleep_remaining(self): - """ returns the remaining time from sleep, as an interrupt (wakeup source) might have triggered """ - c3 = self.peek_memory(WAKE_REASON_ADDR + 3) - c2 = self.peek_memory(WAKE_REASON_ADDR + 2) - c1 = self.peek_memory(WAKE_REASON_ADDR + 1) - time_device_s = (c3 << 16) + (c2 << 8) + c1 - # this time is from PIC internal oscilator, so it needs to be adjusted with the calibration value - try: - self.calibrate_rtc() - except Exception: - pass - time_s = int((time_device_s / self.clk_cal_factor) + 0.5) # 0.5 used for round - return time_s - - def setup_sleep(self, time_s): - try: - self.calibrate_rtc() - except Exception: - pass - time_s = int((time_s * self.clk_cal_factor) + 0.5) # round to the nearest integer - if time_s >= 2**(8*3): - time_s = 2**(8*3)-1 - self._write(bytes([CMD_SETUP_SLEEP, time_s & 0xFF, (time_s >> 8) & 0xFF, (time_s >> 16) & 0xFF])) - - def go_to_sleep(self, gps=True): - # enable or disable back-up power to the GPS receiver - if gps: - self.set_bits_in_memory(PORTC_ADDR, 1 << 7) - else: - self.mask_bits_in_memory(PORTC_ADDR, ~(1 << 7)) - # disable the ADC - self.poke_memory(ADCON0_ADDR, 0) - - if self.wake_int: - # Don't touch RA3, RA5 or RC1 so that interrupt wake-up works - self.poke_memory(ANSELA_ADDR, ~((1 << 3) | (1 << 5))) - self.poke_memory(ANSELC_ADDR, ~((1 << 6) | (1 << 7) | (1 << 1))) - else: - # disable power to the accelerometer, and don't touch RA3 so that button wake-up works - self.poke_memory(ANSELA_ADDR, ~(1 << 3)) - self.poke_memory(ANSELC_ADDR, ~(1 << 7)) - - self.poke_memory(ANSELB_ADDR, 0xFF) - - # check if INT pin (PIC RC1), should be used for wakeup - if self.wake_int_pin: - if self.wake_int_pin_rising_edge: - self.set_bits_in_memory(OPTION_REG_ADDR, 1 << 6) # rising edge of INT pin - else: - self.mask_bits_in_memory(OPTION_REG_ADDR, ~(1 << 6)) # falling edge of INT pin - self.mask_bits_in_memory(ANSELC_ADDR, ~(1 << 1)) # disable analog function for RC1 pin - self.set_bits_in_memory(TRISC_ADDR, 1 << 1) # make RC1 input pin - self.mask_bits_in_memory(INTCON_ADDR, ~(1 << 1)) # clear INTF - self.set_bits_in_memory(INTCON_ADDR, 1 << 4) # enable interrupt; set INTE) - - self._write(bytes([CMD_GO_SLEEP]), wait=False) - # kill the run pin - Pin('P3', mode=Pin.OUT, value=0) - - def calibrate_rtc(self): - # the 1.024 factor is because the PIC LF operates at 31 KHz - # WDT has a frequency divider to generate 1 ms - # and then there is a binary prescaler, e.g., 1, 2, 4 ... 512, 1024 ms - # hence the need for the constant - self._write(bytes([CMD_CALIBRATE]), wait=False) - self.i2c.deinit() - Pin('P21', mode=Pin.IN) - pulses = pycom.pulses_get('P21', 100) - self.i2c.init(mode=I2C.MASTER, pins=(self.sda, self.scl)) - idx = 0 - for i in range(len(pulses)): - if pulses[i][1] > EXP_RTC_PERIOD: - idx = i - break - try: - period = pulses[idx][1] - pulses[(idx - 1)][1] - except: - period = 0 - if period > 0: - self.clk_cal_factor = (EXP_RTC_PERIOD / period) * (1000 / 1024) - if self.clk_cal_factor > 1.25 or self.clk_cal_factor < 0.75: - self.clk_cal_factor = 1 - - def button_pressed(self): - button = self.peek_memory(PORTA_ADDR) & (1 << 3) - return not button - - def read_battery_voltage(self): - self.set_bits_in_memory(ADCON0_ADDR, _ADCON0_GO_nDONE_MASK) - time.sleep_us(50) - while self.peek_memory(ADCON0_ADDR) & _ADCON0_GO_nDONE_MASK: - time.sleep_us(100) - adc_val = (self.peek_memory(ADRESH_ADDR) << 2) + (self.peek_memory(ADRESL_ADDR) >> 6) - return (((adc_val * 3.3 * 280) / 1023) / 180) + 0.01 # add 10mV to compensate for the drop in the FET - - def setup_int_wake_up(self, rising, falling): - """ rising is for activity detection, falling for inactivity """ - wake_int = False - if rising: - self.set_bits_in_memory(IOCAP_ADDR, 1 << 5) - wake_int = True - else: - self.mask_bits_in_memory(IOCAP_ADDR, ~(1 << 5)) - - if falling: - self.set_bits_in_memory(IOCAN_ADDR, 1 << 5) - wake_int = True - else: - self.mask_bits_in_memory(IOCAN_ADDR, ~(1 << 5)) - self.wake_int = wake_int - - def setup_int_pin_wake_up(self, rising_edge = True): - """ allows wakeup to be made by the INT pin (PIC -RC1) """ - self.wake_int_pin = True - self.wake_int_pin_rising_edge = rising_edge diff --git a/pybytes/pytrack/lib/pytrack.py b/pybytes/pytrack/lib/pytrack.py deleted file mode 100644 index 3e4d39b..0000000 --- a/pybytes/pytrack/lib/pytrack.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2019, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -from pycoproc import Pycoproc - -__version__ = '1.4.0' - -class Pytrack(Pycoproc): - - def __init__(self, i2c=None, sda='P22', scl='P21'): - Pycoproc.__init__(self, i2c, sda, scl) diff --git a/pymesh/pymesh_frozen/lib/cli.py b/pymesh/pymesh_frozen/lib/cli.py index 85ec0f8..69242aa 100644 --- a/pymesh/pymesh_frozen/lib/cli.py +++ b/pymesh/pymesh_frozen/lib/cli.py @@ -9,27 +9,34 @@ import time import json import sys - -try: - from pymesh_debug import print_debug -except: - from _pymesh_debug import print_debug +import _thread try: from gps import Gps except: from _gps import Gps -__version__ = '1' +__version__ = '3' """ +__version__ = '3' +* added dynamic start/stop CLI +* h = help, listing all commands +* added tx_pow and s(send) packets with repetitions +* debug can also read the current level + +__version__ = '2' +* added pause/resume and factory reset + +__version__ = '1' * initial draft """ class Cli: """ class for CLI commands """ - def __init__(self, mesh): + def __init__(self, mesh, pymesh): self.mesh = mesh + self.pymesh = pymesh # self.rpc_handler = rpc_handler # self.ble_comm = ble_comm @@ -87,6 +94,8 @@ def process(self, arg1, arg2): print('last_mesh_pairs', json.dumps(last_mesh_pairs)) elif cmd == 's': + interval = 0 + repetitions = 0 try: to = int(input('(to)<')) # typ = input('(type, 0=text, 1=file, Enter for text)<') @@ -95,6 +104,9 @@ def process(self, arg1, arg2): # else: # typ = int(typ) txt = input('(message)<') + repetitions = int(input('(repetitions)')) + if repetitions > 1: + interval = int(input('(interval in seconds)')) except: continue data = { @@ -104,7 +116,13 @@ def process(self, arg1, arg2): 'id': 12345, 'ts': int(time.time()), } - print(self.mesh.send_message(data)) + while repetitions > 0: + print(self.mesh.send_message(data)) + repetitions = repetitions - 1 + if repetitions > 0: + print("Remaining TX packets:", repetitions) + time.sleep(interval) + elif cmd == 'ws': to = int(input('(to)<')) @@ -166,7 +184,7 @@ def process(self, arg1, arg2): elif cmd == "rst": print("Mesh Reset NVM settings ... ") - self.mesh.mesh.mesh.mesh.deinit() + self.mesh.mesh.mesh.mesh.deinit(reset=True) if self.sleep: self.sleep(1) @@ -236,42 +254,70 @@ def process(self, arg1, arg2): ret = input('(debug level[0-5])<') try: level = int(ret) - self.mesh.debug_level(level) + self.pymesh.debug_level(level) except: - print_debug(1, "error parsing") + print(self.pymesh.debug_level()) elif cmd == "config": print(self.mesh.config) - else: + elif cmd == "pause": + self.pymesh.pause() + + elif cmd == "resume": + self.pymesh.resume() + + elif cmd == "tx_pow": + print("LoRa stats:", self.pymesh.mesh.mesh.mesh.lora.stats()) + tx_str = input('(tx_pow[2-20])<') + try: + tx_pow = int(tx_str) + self.pymesh.pause() + print("Change TX power to", tx_pow) + time.sleep(1) + self.pymesh.resume(tx_pow) + except: + print("Invalid value") + + elif cmd == "stop": + self.pymesh.cli = None + _thread.exit() + + elif cmd == "h": print("List of available commands") + print("br - enable/disable or display the current Border Router functionality") + print("brs - send packet for Mesh-external, to BR, if any") + print("buf - display buffer info") + print("config - print config file contents") + print("debug - set debug level") + print("gps - get/set location coordinates") + print("h - help, list of commands") print("ip - display current IPv6 unicast addresses") print("mac - set or display the current LoRa MAC address") - print("self - display all info about current node") print("mml - display the Mesh Mac List (MAC of all nodes inside this Mesh), also inquires Leader") print("mp - display the Mesh Pairs (Pairs of all nodes connections), also inquires Leader") - print("s - send message") - print("ws - verifies if message sent was acknowledged") + print("ot - sends command to openthread internal CLI") + print("pause - suspend Pymesh") + print("resume - resume Pymesh") print("rm - verifies if any message was received") + print("rst - reset NOW, including NVM Pymesh IPv6") + print("s - send message") + print("self - display all info about current node") print("sleep - deep-sleep") # print("stat - start statistics") # print("stat? - display statistics") - print("br - enable/disable or display the current Border Router functionality") - print("brs - send packet for Mesh-external, to BR, if any") - print("rst - reset NOW, including NVM Pymesh IPv6") - print("buf - display buffer info") - print("ot - sends command to openthread internal CLI") - print("debug - set debug level") - print("config - print config file contents") - print("gps - get/set location coordinates") + print("stop - close this CLI") + print("tx_pow - set LoRa TX power in dBm (2-20)") + print("ws - verifies if message sent was acknowledged") except KeyboardInterrupt: - print('cli Got Ctrl-C') + print('CLI Ctrl-C') except Exception as e: sys.print_exception(e) finally: - print('cli finally') - self.sleep(0) + print('CLI stopped') + if self.pymesh.cli is not None: + self.sleep(0) def new_br_message_cb(self, rcv_ip, rcv_port, rcv_data, dest_ip, dest_port): ''' callback triggered when a new packet arrived for the current Border Router, diff --git a/pymesh/pymesh_frozen/lib/gps.py b/pymesh/pymesh_frozen/lib/gps.py index 5573517..cd30b9c 100644 --- a/pymesh/pymesh_frozen/lib/gps.py +++ b/pymesh/pymesh_frozen/lib/gps.py @@ -6,16 +6,21 @@ # see the Pycom Licence v1.0 document supplied with this file, or # available at https://www.pycom.io/opensource/licensing -__version__ = '1' -""" -* initial version -""" - +try: + from pymesh_debug import print_debug +except: + from _pymesh_debug import print_debug + import time # from pytrack import Pytrack # from L76GNSS import L76GNSS from machine import Timer +__version__ = '1' +""" +* initial version +""" + class Gps: # Pycom office GPS coordinates lat = 51.45 @@ -34,7 +39,7 @@ def set_location(latitude, longitude): Gps.lon = longitude is_set = True else: - print("Error parsing ", latitude, longitude) + print_debug(3, "Error parsing ", latitude, longitude) @staticmethod def get_location(): @@ -48,10 +53,10 @@ def get_location(): # Gps.l76 = L76GNSS(py, timeout=30) # #l76.coordinates() # Gps._timer = Timer.Alarm(Gps.gps_periodic, 30, periodic=True) - # print("Pytrack detected") + # print_debug(3, "Pytrack detected") # except: # is_pytrack = False - # print("Pytrack NOT detected") + # print_debug(3, "Pytrack NOT detected") # #TODO: how to check if GPS is conencted # return is_pytrack @@ -61,9 +66,9 @@ def get_location(): # coord = Gps.l76.coordinates() # if coord[0] != None: # Gps.lat, Gps.lon = coord - # print("New coord ", coord) + # print_debug(3, "New coord ", coord) # dt = time.ticks_ms() - t0 - # print(" =====>>>> gps_periodic ", dt) + # print_debug(3, " =====>>>> gps_periodic ", dt) # @staticmethod # def terminate(): diff --git a/pymesh/pymesh_frozen/lib/loramesh.py b/pymesh/pymesh_frozen/lib/loramesh.py index f0f656d..5f21c09 100644 --- a/pymesh/pymesh_frozen/lib/loramesh.py +++ b/pymesh/pymesh_frozen/lib/loramesh.py @@ -25,8 +25,11 @@ except: from _gps import Gps -__version__ = '6' +__version__ = '7' """ +__version__ = '7' +* added pause/resume + __version__ = '6' * added IPv6 unicast addresses as fdde:ad00:beef:0:: """ @@ -66,14 +69,8 @@ class Loramesh: def __init__(self, config): """ Constructor """ - self.config = config - config_lora = config.get('LoRa') - self.lora = LoRa(mode=LoRa.LORA, - region = config_lora.get("region"), - frequency = config_lora.get("freq"), - bandwidth = config_lora.get("bandwidth"), - sf = config_lora.get("sf")) - self.mesh = self.lora.Mesh() #start Mesh + self.config_lora = config.get('LoRa') + self._lora_init() # get Lora MAC address #self.MAC = str(ubinascii.hexlify(lora.mac()))[2:-1] @@ -113,14 +110,33 @@ def __init__(self, config): self.connections_ts = -65535 # very old # set a new unicast address - self.unique_ip_prefix = "fdde:ad00:beef:0::" - command = "ipaddr add " + self.ip_mac_unique(self.mac_short) - self.mesh.cli(command) + self._add_ipv6_unicast() + + def _lora_init(self, tx_dBm = 14): + self.lora = LoRa(mode=LoRa.LORA, + region = self.config_lora.get("region"), + frequency = self.config_lora.get("freq"), + bandwidth = self.config_lora.get("bandwidth"), + sf = self.config_lora.get("sf"), + tx_power = tx_dBm) + self.mesh = self.lora.Mesh() #start Mesh + + def pause(self): + self.mesh.deinit() + + def resume(self, tx_dBm = 14): + self._lora_init(tx_dBm) + self._add_ipv6_unicast() def ip_mac_unique(self, mac): ip = self.unique_ip_prefix + hex(mac & 0xFFFF)[2:] return ip + def _add_ipv6_unicast(self): + self.unique_ip_prefix = "fdde:ad00:beef:0::" + command = "ipaddr add " + self.ip_mac_unique(self.mac_short) + self.mesh.cli(command) + def update_internals(self): self._state_update() self._rloc16_update() @@ -261,7 +277,7 @@ def blink(self, num = 3, period = .5, color = None): def neighbors_update(self): """ update neigh_dict from cli:'neighbor table' """ - """ >>> print(lora.cli("neighbor table")) + """ >>> print_debug(3, lora.cli("neighbor table")) | Role | RLOC16 | Age | Avg RSSI | Last RSSI |R|S|D|N| Extended MAC | +------+--------+-----+----------+-----------+-+-+-+-+------------------+ | C | 0x2801 | 219 | 0 | 0 |1|1|1|1| 0000000000000005 | @@ -297,7 +313,7 @@ def neighbors_update(self): mac = nei_rec.mac neighbor = NeighborData((mac, age, rloc16, role, rssi,)) self.router_data.add_neighbor(neighbor) - #print("new Neighbor: %s"%(neighbor.to_string())) + #print_debug(3, "new Neighbor: %s"%(neighbor.to_string())) #except: # pass # add own info in dict @@ -326,9 +342,9 @@ def routers_neigh_update(self, data_pack): def leader_dict_cleanup(self): """ cleanup the leader_dict for old entries """ - #print("Leader Data before cleanup: %s"%self.leader_data.to_string()) + #print_debug(3, "Leader Data before cleanup: %s"%self.leader_data.to_string()) self.leader_data.cleanup() - print("Leader Data : %s"%self.leader_data.to_string()) + print_debug(3, "Leader Data : %s"%self.leader_data.to_string()) def routers_rloc_list(self, age_min, resolve_mac = None): """ return list of all routers IPv6 RLOC16 @@ -336,8 +352,8 @@ def routers_rloc_list(self, age_min, resolve_mac = None): """ mac_ip = None data = self.mesh.routers() - print("Routers Table: ", data) - '''>>> print(lora.cli('router table')) + print_debug(3, "Routers Table: "+ str(data)) + '''>>> print_debug(3, lora.cli('router table')) | ID | RLOC16 | Next Hop | Path Cost | LQ In | LQ Out | Age | Extended MAC | +----+--------+----------+-----------+-------+--------+-----+------------------+ | 12 | 0x3000 | 63 | 0 | 0 | 0 | 0 | 0000000000000002 |''' @@ -375,13 +391,13 @@ def routers_rloc_list(self, age_min, resolve_mac = None): routers_list.append((last_ts, ipv6)) if resolve_mac is not None: - print("Mac found in Router %s"%str(mac_ip)) + print_debug(3, "Mac found in Router %s"%str(mac_ip)) return mac_ip # sort the list in the ascending values of timestamp routers_list.sort() - print("Routers list %s"%str(routers_list)) + print_debug(3, "Routers list %s"%str(routers_list)) return routers_list def leader_data_pack(self): @@ -392,7 +408,7 @@ def leader_data_pack(self): def leader_data_unpack(self, data): self.leader_data = LeaderData(data) - print("Leader Data : %s"%self.leader_data.to_string()) + print_debug(3, "Leader Data : %s"%self.leader_data.to_string()) return self.leader_data.ok def neighbor_resolve_mac(self, mac): @@ -401,27 +417,27 @@ def neighbor_resolve_mac(self, mac): def resolve_mac_from_leader_data(self, mac): mac_ip = self.leader_data.resolve_mac(mac) - print("Mac %x found as IP %s"%(mac, str(mac_ip))) + print_debug(3, "Mac %x found as IP %s"%(mac, str(mac_ip))) return mac_ip def macs_get(self): """ returns the set of the macs, hopefully it was received from Leader """ - #print("Macs: %s"%(str(self.macs))) + #print_debug(3, "Macs: %s"%(str(self.macs))) return (self.macs, self.macs_ts) def macs_set(self, data): MACS_FMT = '!H' field_size = calcsize(MACS_FMT) - #print("Macs pack: %s"%(str(data))) + #print_debug(3, "Macs pack: %s"%(str(data))) n, = unpack(MACS_FMT, data) - #print("Macs pack(%d): %s"%(n, str(data))) + #print_debug(3, "Macs pack(%d): %s"%(n, str(data))) index = field_size self.macs = set() for _ in range(n): mac, = unpack(MACS_FMT, data[index:]) self.macs.add(mac) - #print("Macs %d, %d: %s"%(index, mac, str(self.macs))) + #print_debug(3, "Macs %d, %d: %s"%(index, mac, str(self.macs))) index = index + field_size self.macs_ts = time.time() @@ -474,12 +490,12 @@ def node_info_set(self, data): if role is self.STATE_ROUTER: router = RouterData(data[1:]) self.leader_data.add_router(router) - print("Added as router %s"%router.to_string()) + print_debug(3, "Added as router %s"%router.to_string()) elif role is self.STATE_CHILD: node = NeighborData(data[1:]) router = RouterData(node) self.leader_data.add_router(router) - print("Added as Router-Neigh %s"%router.to_string()) + print_debug(3, "Added as Router-Neigh %s"%router.to_string()) pass class NeighborData: @@ -498,20 +514,20 @@ def __init__(self, data = None): return datatype = str(type(data)) - #print('NeighborData __init__ %s'%str(data)) + #print_debug(3, 'NeighborData __init__ %s'%str(data)) if datatype == "": self._init_tuple(data) elif datatype == "": self._init_bytes(data) - #print('NeighborData done __init__') + #print_debug(3, 'NeighborData done __init__') def _init_tuple(self, data): - #print('_init_tuple %s'%str(data)) + #print_debug(3, '_init_tuple %s'%str(data)) (self.mac, self.age, self.rloc16, self.role, self.rssi) = data return def _init_bytes(self, data): - #print('NeighborData._init_bytes %s'%str(data)) + #print_debug(3, 'NeighborData._init_bytes %s'%str(data)) self.mac, self.rloc16, self.role, self.rssi, self.age = unpack(self.PACKING_FMT, data[:self.pack_fmt_size()]) return @@ -555,7 +571,7 @@ def __init__(self, data = None): def _init_bytes(self, data_pack): - #print('RouterData._init_bytes %s'%str(data_pack)) + #print_debug(3, 'RouterData._init_bytes %s'%str(data_pack)) index = calcsize(self.PACK_HEADER_FMT) (self.mac, self.rloc16, lat, lon, neigh_num) = \ unpack(self.PACK_HEADER_FMT, data_pack[: index]) @@ -592,7 +608,7 @@ def _init_neighbordata(self, data): def add_neighbor(self, neighbor): self.dict[neighbor.mac] = neighbor - #print("add_neighbor type: %s"%str(type(neighbor))) + #print_debug(3, "add_neighbor type: %s"%str(type(neighbor))) return def neigh_num(self): @@ -613,7 +629,7 @@ def to_string(self): x = 'Router MAC 0x%X, rloc16 0x%x, coord %s, neigh_num %d, ts %d\n'\ %(self.mac, self.rloc16, str(self.coord), len(self.dict), self.ts) for mac, nei in self.dict.items(): - #print("type: %s, %s"%(str(type(nei)),str(nei))) + #print_debug(3, "type: %s, %s"%(str(type(nei)),str(nei))) x = x + nei.to_string() + '\n' return x @@ -675,7 +691,7 @@ def __init__(self, data = None): def _init_bytes(self, data_pack): - #print('LeaderData._init_bytes %s'%str(data_pack)) + #print_debug(3, 'LeaderData._init_bytes %s'%str(data_pack)) index = calcsize(self.PACK_HEADER_FMT) (self.mac, self.rloc16, routers_num) = unpack(self.PACK_HEADER_FMT, data_pack[: index]) self.ts = time.time() @@ -696,7 +712,7 @@ def add_router(self, router_data): def cleanup(self): for mac, router in self.dict.items(): if time.time() - router.ts > 300: - print("Deleted old Router %d"%mac) + print_debug(3, "Deleted old Router %d"%mac) del self.dict[mac] return @@ -729,7 +745,7 @@ def node_info_mac(self, mac): def node_info_mac_pack(self, mac): node, role = self.node_info_mac(mac) if node is None: - print("Node is None %d"%mac) + print_debug(3, "Node is None %d"%mac) return bytes() # pack type: RouterData or Child (basically NeighborData) data = pack('!B', role) @@ -763,7 +779,7 @@ def get_mesh_connections(self): def get_connections_pack(self): connections = self.get_mesh_connections() - print("Connections ", connections) + print_debug(3, "Connections "+ str(connections)) data = pack('!H', len(connections)) for record in connections: (mac1, mac2, rssi) = record @@ -782,7 +798,7 @@ def get_macs_pack(self): data = pack('!H', len(macs)) for mac in macs: data = data + pack('!H', mac) - #print("Macs pack:%s"%(str(data))) + #print_debug(3, "Macs pack:%s"%(str(data))) return data def get_mac_ts(self, mac): diff --git a/pymesh/pymesh_frozen/lib/mesh_interface.py b/pymesh/pymesh_frozen/lib/mesh_interface.py index 402c6c2..0287b5e 100644 --- a/pymesh/pymesh_frozen/lib/mesh_interface.py +++ b/pymesh/pymesh_frozen/lib/mesh_interface.py @@ -15,25 +15,25 @@ except: from _mesh_internal import MeshInternal -# try: -# from statistics import Statistics -# except: -# from _statistics import Statistics - try: from meshaging import Meshaging except: from _meshaging import Meshaging try: - from pymesh_debug import print_debug - from pymesh_debug import debug_level + from pymesh_debug import * except: - from _pymesh_debug import print_debug - from _pymesh_debug import debug_level + from _pymesh_debug import * -__version__ = '4' +__version__ = '6' """ +__version__ = '6' +* added loRa_mac field to get_node_info + +__version__ = '5' +* added pause/resume + +__version__ = '3' * added file send/receive debug __version__ = '2' @@ -58,15 +58,18 @@ def __init__(self, config, message_cb): self.single_leader_ts = 0 self.end_device_m = False - + self.is_paused = False + self._start_timer() # self.statistics = Statistics(self.meshaging) + + pass + + def _start_timer(self): self._timer = Timer.Alarm(self.periodic_cb, self.INTERVAL, periodic=True) # just run this ASAP self.periodic_cb(None) - pass - def periodic_cb(self, alarm): # wait lock forever if self.lock.acquire(): @@ -83,15 +86,15 @@ def periodic_cb(self, alarm): # if self.single_leader_ts == 0: # # first time Single Leader, record time # self.single_leader_ts = time.time() - # print("Single Leader", self.mesh.mesh.state, self.mesh.mesh.mesh.single(), + # print_debug(3, "Single Leader", self.mesh.mesh.state, self.mesh.mesh.mesh.single(), # time.time() - self.single_leader_ts) # if time.time() - self.single_leader_ts > 180: - # print("Single Leader, just reset") + # print_debug(3, "Single Leader, just reset") # if self.sleep_function: # self.sleep_function(1) # else: - # # print("Not Single Leader", self.mesh.mesh.state, self.mesh.mesh.mesh.single()) + # # print_debug(3, "Not Single Leader", self.mesh.mesh.state, self.mesh.mesh.mesh.single()) # self.single_leader_ts = 0 self.lock.release() @@ -100,9 +103,14 @@ def periodic_cb(self, alarm): pass - def timer_kill(self): + def pause(self): # with self.lock: self._timer.cancel() + self.mesh.pause() + + def resume(self, tx_dBm = 14): + self.mesh.resume(tx_dBm) + self._start_timer() def get_mesh_mac_list(self): mac_list = list() @@ -111,7 +119,7 @@ def get_mesh_mac_list(self): # mac_list.sort() mac_list = {0:list(self.mesh.get_all_macs_set())} self.lock.release() - print("get_mesh_mac_list:", str(mac_list)) + print_debug(3, "get_mesh_mac_list:" + str(mac_list)) return mac_list def get_mesh_pairs(self): @@ -119,7 +127,7 @@ def get_mesh_pairs(self): if self.lock.acquire(): mesh_pairs = self.mesh.get_mesh_pairs() self.lock.release() - #print("get_mesh_pairs: %s"%str(mesh_pairs)) + #print_debug(3, "get_mesh_pairs: %s"%str(mesh_pairs)) return mesh_pairs def set_gps(self, lng, lat): @@ -146,9 +154,10 @@ def get_node_info(self, mac_id = ""): mac = int(mac_id) except: mac = self.mesh.MAC - print("get_node_info own mac") + print_debug(3, "get_node_info own mac") if self.lock.acquire(): data = self.mesh.node_info(mac) + data['loRa_mac'] = mac self.lock.release() return data @@ -169,11 +178,11 @@ def send_message(self, data): id = int(data['id']) ts = int(data['ts']) except: - print('send_message: wrong input params') + print_debug(1, 'send_message: wrong input params') return False if self.lock.acquire(): - print("Send message to %d, typ %d, load %s"%(mac, msg_type, payload)) + print_debug(3, "Send message to %d, typ %d, load %s"%(mac, msg_type, payload)) ret = self.meshaging.send_message(mac, msg_type, payload, id, ts) # send messages ASAP self.mesh.process_messages() @@ -186,7 +195,7 @@ def mesage_was_ack(self, mac, id): if self.lock.acquire(): ret = self.meshaging.mesage_was_ack(mac, id) self.lock.release() - #print("mesage_was_ack (%X, %d): %d"%(mac, id, ret)) + #print_debug(3, "mesage_was_ack (%X, %d): %d"%(mac, id, ret)) return ret def get_rcv_message(self): @@ -212,8 +221,8 @@ def get_rcv_message(self): # num_mess = int(data['n']) # timeout = int(data['t']) # except: - # print("statistics_start failed") - # print(data) + # print_debug(3, "statistics_start failed") + # print_debug(3, data) # return 0 # if mac == self.mesh.MAC: # data['mac'] = 2 @@ -222,13 +231,13 @@ def get_rcv_message(self): # def statistics_get(self, id): # res = self.statistics.status(id) - # print(res) + # print_debug(3, res) # return res - + def br_set(self, enable, prio = 0, br_mess_cb = None): with self.lock: self.mesh.border_router(enable, prio, br_mess_cb) - + def ot_cli(self, command): """ Executes commands in Openthread CLI, see https://github.com/openthread/openthread/tree/master/src/cli """ @@ -237,14 +246,14 @@ def ot_cli(self, command): def end_device(self, state = None): if state is None: # read status of end_device - state = self.ot_cli('routerrole') + state = self.ot_cli('routerrole') return state == 'Disabled' self.end_device_m = False state_str = 'enable' if state == True: self.end_device_m = True state_str = 'disable' - ret = self.ot_cli('routerrole '+ state_str) + ret = self.ot_cli('routerrole '+ state_str) return ret == '' def leader_priority(self, weight = None): @@ -268,29 +277,16 @@ def leader_priority(self, weight = None): ret = self.ot_cli('leaderweight '+ str(weight)) return ret == '' - def debug_level(self, level = None): - if level is None: - try: - ret = pycom.nvs_get('pymesh_debug') - except: - ret = None - return ret - try: - ret = int(level) - except: - ret = self.debug_level - debug_level(ret) - # def parent(self): # """ Returns the Parent MAC for the current Child node # Returns 0 if node is not Child """ - + # if self.mesh.mesh.mesh.state() != self.mesh.mesh.STATE_CHILD: - # print("Not Child, no Parent") + # print_debug(3, "Not Child, no Parent") # return 0 # # try: # parent_mac = int(self.mesh.mesh.mesh.cli('parent').split('\r\n')[0].split('Ext Addr: ')[1], 16) # # except: # # parent_mac = 0 - # print('Parent mac is:', parent_mac) + # print_debug(3, 'Parent mac is: %s'%parent_mac) # return parent_mac diff --git a/pymesh/pymesh_frozen/lib/mesh_internal.py b/pymesh/pymesh_frozen/lib/mesh_internal.py index f32c2b6..4457239 100644 --- a/pymesh/pymesh_frozen/lib/mesh_internal.py +++ b/pymesh/pymesh_frozen/lib/mesh_internal.py @@ -29,8 +29,11 @@ except: from _pymesh_debug import print_debug -__version__ = '6' +__version__ = '7' """ +__version__ = '7' +* added pause/resume + __version__ = '6' * refactorized file send/receive in state machines @@ -138,6 +141,14 @@ def __init__(self, meshaging, config, message_cb): self.br_message_cb = None pass + def pause(self): + self.rx_cb_registered = False + self.sock = None + self.mesh.pause() + + def resume(self, tx_dBm = 14): + self.mesh.resume(tx_dBm) + def create_socket(self): """ create UDP socket """ self.sock = socket.socket(socket.AF_LORA, socket.SOCK_RAW) @@ -156,7 +167,7 @@ def process_messages(self): # elif mess.state == Message.MESS_STATE_SENT: # # try to resend # if time.time() - mess.last_tx_ts > 15: - # print("Re-transmit %x %s" % (mac, mess.ip)) + # print_debug(3, "Re-transmit %x %s" % (mac, mess.ip)) # mess.last_tx_ts = time.time() # self.send_message(mess) pass @@ -184,14 +195,14 @@ def ip(self): return self.mesh.ip() # def _process_router(self): - # print("Process Router") + # print_debug(3, "Process Router") # # just update internal neighbor table # self.mesh.neighbors_update() # # add itself and neighbors in the leader data # self.mesh.leader_add_own_neigh() def _process_leader(self): - print("Process Leader") + print_debug(3, "Process Leader") """ if state == self.mesh.STATE_LEADER_SINGLE: @@ -212,7 +223,7 @@ def _process_leader(self): for router_pair in router_list[:router_num]: (age, router) = router_pair self.send_pack(self.PACK_LEADER_ASK_NEIGH, '', router) - print("Leader inquire Router %s" % router) + print_debug(3, "Leader inquire Router %s" % router) idx = idx + 1 if idx < router_num: time.sleep(.5) @@ -222,31 +233,31 @@ def br_send(self, data): ret = False # first, make sure this node is not BR (BR data is sent directly) if len(self.mesh.mesh.border_router()) > 0: - print("Node is BR, so shouldn't send data to another BR") + print_debug(3, "Node is BR, so shouldn't send data to another BR") return False # check if we have a BR network prefix in ipaddr for ip in self.mesh.ipaddr(): if ip.startswith(self.BR_NET_ADDRESS[0:-4]): - print("found BR address: ", ip) - if time.time() - self.ext_mesh_ts > 5: + print_debug(3, "found BR address: %s"%ip) + if time.time() - self.ext_mesh_ts >= 0: ret = True try: ip = data['ip'] port = int(data['port']) payload = data['b'] except: - print("Error parsing packet for Mesh-external") + print_debug(3, "Error parsing packet for Mesh-external") ret = False if ret: self.send_pack(self.PACK_BR, payload, ip, port) # self.send_pack(self.PACK_BR, self.debug_data(False), self.EXTERNAL_IP) self.ext_mesh_ts = time.time() else: - print("BR sending too fast") + print_debug(3, "BR sending too fast") ret = False if not ret: - print("no BR (mesh-external IPv6) found") + print_debug(3, "no BR (mesh-external IPv6) found") return ret def process(self): @@ -303,33 +314,33 @@ def debug_data(self, br = True): def border_router(self, enable, prio = 0, br_mess_cb = None): """ Disables/Enables the Border Router functionality, with priority and callback """ net_list = self.mesh.mesh.border_router() - print("State:", enable, "BR: ", net_list) + print_debug(3, "State:" + str(enable) + "BR: "+ str(net_list)) if not enable: # disable all BR network registrations (possible multiple) self.br_handler = None for net in net_list: self.mesh.mesh.border_router_del(net.net) - print("Done remove BR") + print_debug(3, "Done remove BR") else: self.br_handler = br_mess_cb # check if BR already added try: - # print(net[0].net) - # print(self.BR_NET_ADDRESS) + # print_debug(3, net[0].net) + # print_debug(3, self.BR_NET_ADDRESS) # if net[0].net != self.BR_NET_ADDRESS: if not net_list[0].net.startswith(self.BR_NET_ADDRESS[0:-3]): # enable BR self.mesh.mesh.border_router(self.BR_NET_ADDRESS, prio) - print("Done add BR") + print_debug(3, "Done add BR") except: # enable BR self.mesh.mesh.border_router(self.BR_NET_ADDRESS, prio) - print("Force add BR") + print_debug(3, "Force add BR") # print again the BR, to confirm net_list = self.mesh.mesh.border_router() - print("BR: ", net_list) + print_debug(3, "BR: " + str(net_list)) pass def _check_to_send(self, pack_type, ip): @@ -339,7 +350,7 @@ def _check_to_send(self, pack_type, ip): key = (100 * pack_type) + int(ip[-4:], 16) except: # just send it - #print("just send it, ? ", ip) + #print_debug(3, "just send it, ? ", ip) send_it = False if not send_it: return True @@ -352,24 +363,24 @@ def _check_to_send(self, pack_type, ip): else: self.send_table[key] = now except: - #print("%s not in send_table"%str(key)) + #print_debug(3, "%s not in send_table"%str(key)) send_it = True if send_it: # mark packet as sent now self.send_table[key] = now - #print("Packet sent now") + #print_debug(3, "Packet sent now") return send_it # packet already send def send_pack(self, pack_type, data, ip, port=PORT_MESH_INTERNAL): if self.sock is None: return False - print("Send pack: 0x%X to IP %s" % (pack_type, ip)) + print_debug(3, "Send pack: 0x%X to IP %s" % (pack_type, ip)) # check not to send same (packet, destination) too often # if not self._check_to_send(pack_type, ip): - # print("NO send") + # print_debug(3, "NO send") # return False sent_ok = True @@ -379,7 +390,7 @@ def send_pack(self, pack_type, data, ip, port=PORT_MESH_INTERNAL): self.sock.sendto(header + data, (ip, port)) #self.mesh.blink(2, .1) except Exception as ex: - print("Socket.sendto exception: {}".format(ex)) + print_debug(3, "Socket.sendto exception: {}".format(ex)) sent_ok = False return sent_ok @@ -391,8 +402,8 @@ def get_type(self, data): len2 = len(data) if len1 != len2: - print("PACK_HEADER length not ok %d %d" % (len1, len2)) - print(data) + print_debug(3, "PACK_HEADER length not ok %d %d" % (len1, len2)) + print_debug(3, str(data)) return return (pack_type, data) @@ -445,16 +456,17 @@ def receive_all_data(self, arg): break # out of while, no packet rcv_ip = rcv_addr[0] rcv_port = rcv_addr[1] - print('Incoming %d bytes from %s (port %d):' % + print_debug(4, 'Incoming %d bytes from %s (port %d):' % (len(rcv_data), rcv_ip, rcv_port)) - # print(rcv_data) + # print_debug(3, rcv_data) + print_debug(5, str(self.mesh.lora.stats())) # check if Node is BR if self.br_handler: #check if data is for the external of the Pymesh (for Pybytes) if rcv_data[0] == self.BR_MAGIC_BYTE and len(rcv_data) >= calcsize(self.BR_HEADER_FMT): br_header = unpack(self.BR_HEADER_FMT, rcv_data) - print("BR pack, IP dest: %x:%x:%x:%x:%x:%x:%x:%x (port %d)"%( + print_debug(3, "BR pack, IP dest: %x:%x:%x:%x:%x:%x:%x:%x (port %d)"%( br_header[1],br_header[2],br_header[3],br_header[4], br_header[5],br_header[6],br_header[7],br_header[8], br_header[9])) rcv_data = rcv_data[calcsize(self.BR_HEADER_FMT):] @@ -462,12 +474,12 @@ def receive_all_data(self, arg): dest_ip = "%x:%x:%x:%x:%x:%x:%x:%x"%( br_header[1],br_header[2],br_header[3],br_header[4], br_header[5],br_header[6],br_header[7],br_header[8]) - + dest_port = br_header[9] - print(rcv_data) + print_debug(3, rcv_data) (type, rcv_data) = self.get_type(rcv_data) - print(rcv_data) + print_debug(3, rcv_data) self.br_handler(rcv_ip, rcv_port, rcv_data, dest_ip, dest_port) return # done, no more parsing as this pack was for BR @@ -476,32 +488,32 @@ def receive_all_data(self, arg): (type, rcv_data) = self.get_type(rcv_data) # LEADER if type == self.PACK_ROUTER_NEIGHBORS: - print("PACK_ROUTER_NEIGHBORS received") + print_debug(3, "PACK_ROUTER_NEIGHBORS received") self.mesh.routers_neigh_update(rcv_data) # no answer # elif type == self.PACK_ROUTER_ASK_LEADER_DATA: - # print("PACK_ROUTER_ASK_LEADER_DATA received") + # print_debug(3, "PACK_ROUTER_ASK_LEADER_DATA received") # # send answer with Leader data # pack = self.mesh.leader_data_pack() # self.send_pack(self.PACK_LEADER_DATA, pack, rcv_ip) # ROUTER elif type == self.PACK_LEADER_ASK_NEIGH: - print("PACK_LEADER_ASK_NEIGH received") + print_debug(3, "PACK_LEADER_ASK_NEIGH received") payload = self.mesh.neighbors_pack() #time.sleep(.2) self.send_pack(self.PACK_ROUTER_NEIGHBORS, payload, rcv_ip) # elif type == self.PACK_LEADER_DATA: - # print("PACK_LEADER_DATA received") + # print_debug(3, "PACK_LEADER_DATA received") # if self.mesh.leader_data_unpack(rcv_data): # self.interrogate_leader_ts = time.time() # ALL NODES elif type == self.PACK_MESSAGE: - print("PACK_MESSAGE received") + print_debug(3, "PACK_MESSAGE received") # add new pack received message = Message(rcv_data) - print(message.payload) + # print_debug(3, message.payload) message.ip = rcv_ip self.messages.add_rcv_message(message) @@ -514,59 +526,59 @@ def receive_all_data(self, arg): elif type == self.PACK_MESSAGE_ACK: - print("PACK_MESSAGE_ACK received") + print_debug(3, "PACK_MESSAGE_ACK received") # mark message as received self.messages.rcv_ack(rcv_data) elif type == self.PACK_ROUTER_ASK_MACS: - print("PACK_ROUTER_ASK_MACS received") + print_debug(3, "PACK_ROUTER_ASK_MACS received") payload = self.mesh.leader_data.get_macs_pack() self.send_pack(self.PACK_LEADER_MACS, payload, rcv_ip) elif type == self.PACK_LEADER_MACS: - print("PACK_LEADER_MACS received") + print_debug(3, "PACK_LEADER_MACS received") self.mesh.macs_set(rcv_data) elif type == self.PACK_ROUTER_ASK_CONNECTIONS: - print("PACK_ROUTER_ASK_CONNECTIONS received") + print_debug(3, "PACK_ROUTER_ASK_CONNECTIONS received") payload = self.mesh.leader_data.get_connections_pack() self.send_pack(self.PACK_LEADER_CONNECTIONS, payload, rcv_ip) elif type == self.PACK_LEADER_CONNECTIONS: - print("PACK_LEADER_CONNECTIONS received") + print_debug(3, "PACK_LEADER_CONNECTIONS received") self.mesh.connections_set(rcv_data) elif type == self.PACK_ROUTER_ASK_MAC_DETAILS: - print("PACK_ROUTER_ASK_MAC_DETAILS received") + print_debug(3, "PACK_ROUTER_ASK_MAC_DETAILS received") (mac_req, ) = unpack('!H', rcv_data) - print(mac_req) + print_debug(3, str(mac_req)) payload = self.mesh.leader_data.node_info_mac_pack(mac_req) if len(payload) > 0: self.send_pack(self.PACK_LEADER_MAC_DETAILS, payload, rcv_ip) else: - print("No info found about MAC %d"%mac_req) + print_debug(3, "No info found about MAC %d"%mac_req) elif type == self.PACK_LEADER_MAC_DETAILS: - print("PACK_LEADER_MAC_DETAILS received") + print_debug(3, "PACK_LEADER_MAC_DETAILS received") self.mesh.node_info_set(rcv_data) # elif type == self.PACK_FILE_SEND: - # print("PACK_FILE_SEND received") + # print_debug(3, "PACK_FILE_SEND received") # payload = pack("!Q", self.MAC) # self.send_pack(self.PACK_FILE_SEND_ACK, payload, rcv_ip) # # rcv data contains '!QHH' as header # chunk = len(rcv_data) -12 # self.file_size += chunk - # print("size: %d, chunk %d" % (self.file_size, chunk)) + # print_debug(3, "size: %d, chunk %d" % (self.file_size, chunk)) # file_handler = "ab" # append, by default # if chunk > self.file_packsize: # # started receiving a new file - # print("started receiving a new image") + # print_debug(3, "started receiving a new image") # file_handler = "wb" # write/create new file # self.file_packsize = chunk # elif chunk < self.file_packsize: - # print("DONE receiving the image") + # print_debug(3, "DONE receiving the image") # # done receiving the file # self.file_packsize = 0 # self.file_size = 0 @@ -576,22 +588,22 @@ def receive_all_data(self, arg): # # self.file.write(rcv_data) # with open('/flash/dog_rcv.jpg', file_handler) as file: # file.write(rcv_data[12:]) - # print("writing the image") + # print_debug(3, "writing the image") # elif type == self.PACK_FILE_SEND_ACK: # mac_rcv = unpack("!Q", rcv_data) - # print("PACK_FILE_SEND_ACK received from MAC %d"%mac_rcv) + # print_debug(3, "PACK_FILE_SEND_ACK received from MAC %d"%mac_rcv) # mac_rcv = 6 # message = self.messages.dict.get(mac_rcv, None) # if message: - # print("message found") + # print_debug(3, "message found") # self.send_message(message, rcv_data) # else: - # print("message NOT found ", mac_rcv, self.messages.dict) + # print_debug(3, "message NOT found ", mac_rcv, self.messages.dict) else: - print("Unknown packet, type: 0x%X" % (type)) - print(rcv_data) + print_debug(3, "Unknown packet, type: 0x%X" % (type)) + print_debug(3, str(rcv_data)) pass diff --git a/pymesh/pymesh_frozen/lib/meshaging.py b/pymesh/pymesh_frozen/lib/meshaging.py index 309a98d..7eef06d 100644 --- a/pymesh/pymesh_frozen/lib/meshaging.py +++ b/pymesh/pymesh_frozen/lib/meshaging.py @@ -9,6 +9,11 @@ import time from struct import * +try: + from pymesh_debug import print_debug +except: + from _pymesh_debug import print_debug + __version__ = '1' """ * initial version @@ -33,10 +38,10 @@ def send_message(self, mac, msg_type, payload, id, ts): """ send a new message """ already = self.dict.get(mac, None) if already: - print('old message deleted for %X' % mac) + print_debug(3, 'old message deleted for %X' % mac) message = Message((mac, msg_type, payload, id, ts)) self.dict[mac] = message - print("Added new message for %X: %s" % (mac, str(payload))) + print_debug(3, "Added new message for %X: %s" % (mac, str(payload))) return True @@ -48,9 +53,9 @@ def add_rcv_message(self, message): if message.payload == b'dog':#🐕': message.payload = 'Picture started receiving' - print('Rcv mess about dog, so we start receiving picture') + print_debug(3, 'Rcv mess about dog, so we start receiving picture') # else: - # print('payload is not dog') + # print_debug(3, 'payload is not dog') if self.on_rcv_message: self.on_rcv_message(message) @@ -72,7 +77,7 @@ def rcv_ack(self, data): # check if message was about picture sending, to start actual file sending mess = self.dict[message.mac] if mess.payload == 'dog': - print('ACK from dog message, start picture sending') + print_debug(3, 'ACK from dog message, start picture sending') del self.dict[message.mac] self.send_message(message.mac, message.TYPE_IMAGE, 'dog.jpg', message.id, time.time()) @@ -80,7 +85,7 @@ def rcv_ack(self, data): mess = Message((message.mac, message.TYPE_TEXT, 'Receiving the picture', message.id+1, time.time())) self.on_rcv_message(mess) else: - print(message.mac, self.dict) + print_debug(3, str(message.mac) + str(self.dict)) pass def mesage_was_ack(self, mac, id): @@ -93,7 +98,7 @@ def mesage_was_ack(self, mac, id): done = True except: pass - print("ACK? mac %x, id %d => %d" % (mac, id, done)) + print_debug(3, "ACK? mac %x, id %d => %d" % (mac, id, done)) return done def get_rcv_message(self): @@ -108,7 +113,7 @@ def get_rcv_message(self): def file_transfer_done(self, rcv_data): message = Message(rcv_data) message.payload = 'Picture was received' - print('Picture done receiving from %d', message.mac) + print_debug(3, 'Picture done receiving from %d', message.mac) message.id = message.id + 1 if self.on_rcv_message: self.on_rcv_message(message) @@ -161,7 +166,7 @@ def _init_tuple(self, data): return def _init_bytes(self, data): - #print('NeighborData._init_bytes %s'%str(data)) + #print_debug(3, 'NeighborData._init_bytes %s'%str(data)) self.mac, self.id, n = unpack(self.PACK_MESSAGE, data) self.payload = data[calcsize(self.PACK_MESSAGE):] #self.payload = unpack('!' + str(n) + 's', data[calcsize(self.PACK_MESSAGE):]) @@ -208,7 +213,7 @@ def __init__(self, filename): try: self.file = open(filename, "rb") except: - print("File %s can't be opened !!!!"%filename) + print_debug(3, "File %s can't be opened !!!!"%filename) self.state = DONE return self.size = 0 @@ -230,16 +235,16 @@ def process(self, last_response): # got ACK, send next chunk self.chunk = self.file.readinto(self.buffer) self.size = self.size + self.chunk - print("%d Bytes sent, time: %4d sec" % (self.size, time.time() - self.start)) + print_debug(3, "%d Bytes sent, time: %4d sec" % (self.size, time.time() - self.start)) if self.chunk == 0: self._end_transfer() self.retries = 0 else: - print("No answer, so retry?") + print_debug(3, "No answer, so retry?") if time.time() - self.last_ts < 5: #if we just sent the retry, don't resend anything, still wait for answer - print("No retry, too soon") + print_debug(3, "No retry, too soon") return '' self.retries = self.retries + 1 @@ -254,5 +259,5 @@ def process(self, last_response): def _end_transfer(self): self.state = DONE - print("Done sending %d B in %s sec"%(self.size, time.time() - self.start)) + print_debug(3, "Done sending %d B in %s sec"%(self.size, time.time() - self.start)) self.file.close() diff --git a/pymesh/pymesh_frozen/lib/pymesh.py b/pymesh/pymesh_frozen/lib/pymesh.py index 33721f0..cf7ddd2 100644 --- a/pymesh/pymesh_frozen/lib/pymesh.py +++ b/pymesh/pymesh_frozen/lib/pymesh.py @@ -24,31 +24,50 @@ from _cli import Cli try: - from pymesh_debug import print_debug + from pymesh_debug import * except: - from _pymesh_debug import print_debug + from _pymesh_debug import * + +try: + from pymesh_config import PymeshConfig +except: + from _pymesh_config import PymeshConfig + + +__version__ = '3' +""" +__version__ = '3' +* CLI can start/stop dynamically +* replaced all print with print_debug + +__version__ = '2' +* added pause/resume + +__version__ = '1' +* not-versioned prior 5th of Febr 2020 + +""" class Pymesh: def __init__(self, config, message_cb): # print MAC, set MAC is given and restart - + self.config = config - self.mesh = MeshInterface(config, message_cb) + self.mesh = MeshInterface(self.config, message_cb) self.kill_all = False self.deepsleep_timeout = 0 self.new_lora_mac = None - # watchdog = Watchdog(meshaging, mesh) - + # self.mesh.statistics.sleep_function = self.deepsleep_init self.mesh.sleep_function = self.deepsleep_init - self.cli = Cli(self.mesh) - self.cli.sleep = self.deepsleep_init - _thread.start_new_thread(self.process, (1,2)) - _thread.start_new_thread(self.cli.process, (1, 2)) - + self.is_paused = False + self._threads_start() + + self.cli = None + self.ble_rpc = None if config.get("ble_api", False): try: @@ -58,25 +77,31 @@ def __init__(self, config, message_cb): self.ble_rpc = BleRpc(self.config, self.mesh) - + def cli_start(self): + if self.cli is None: + self.cli = Cli(self.mesh, self) + self.cli.sleep = self.deepsleep_init + # self.cli_thread = _thread.start_new_thread(self.cli.process, (1, 2)) + self.cli.process(None, None) + def deepsleep_now(self): """ prepare scripts for graceful exit, deepsleeps if case """ - print("deepsleep_now") - self.mesh.timer_kill() + print_debug(1, "deepsleep_now") + self.mesh.pause() if self.ble_rpc: self.ble_rpc.terminate() # watchdog.timer_kill() # Gps.terminate() # self.mesh.statistics.save_all() - print('Cleanup code, all Alarms cb should be stopped') + print_debug(1, 'Cleanup code, all Alarms cb should be stopped') if self.new_lora_mac: fo = open("/flash/sys/lpwan.mac", "wb") mac_write=bytes([0, 0, 0, 0, 0, 0, (self.new_lora_mac >> 8) & 0xFF, self.new_lora_mac & 0xFF]) fo.write(mac_write) fo.close() - print("Really LoRa MAC set to", self.new_lora_mac) + print_debug(1, "Really LoRa MAC set to " + str(self.new_lora_mac)) if self.deepsleep_timeout > 0: - print('Going to deepsleep for %d seconds'%self.deepsleep_timeout) + print_debug(1, 'Going to deepsleep for %d seconds'%self.deepsleep_timeout) time.sleep(1) machine.deepsleep(self.deepsleep_timeout * 1000) else: @@ -85,7 +110,7 @@ def deepsleep_now(self): def deepsleep_init(self, timeout, new_MAC = None): """ initializes an deep-sleep sequence, that will be performed later """ - print("deepsleep_init") + print_debug(3, "deepsleep_init") self.deepsleep_timeout = timeout self.kill_all = True if new_MAC: @@ -97,19 +122,48 @@ def process(self, arg1, arg2): while True: if self.kill_all: self.deepsleep_now() + if self.is_paused: + # break + _thread.exit() time.sleep(.5) pass except KeyboardInterrupt: - print('Got Ctrl-C') + print_debug(1, 'Got Ctrl-C') except Exception as e: sys.print_exception(e) - finally: - print('finally') - self.deepsleep_now() + + def _threads_start(self): + _thread.start_new_thread(self.process, (1,2)) + + def pause(self): + if self.is_paused: + # print_debug(5, "Pymesh already paused") + return + + print_debug(3, "Pymesh pausing") + + self.mesh.pause() + if self.ble_rpc: + self.ble_rpc.terminate() + + self.is_paused = True + return + + def resume(self, tx_dBm = 14): + if not self.is_paused: + # print_debug(5, "Pymesh can't be resumed, not paused") + return + + print_debug(3, "Pymesh resuming") + self.is_paused = False + self._threads_start() + self.mesh.resume(tx_dBm) + + return def send_mess(self, mac, mess): - """ send mess to specified MAC address + """ send mess to specified MAC address data is dictionary data = { 'to': 0x5, 'b': 'text', @@ -123,7 +177,7 @@ def send_mess(self, mac, mess): 'ts': time.time(), } return self.mesh.send_message(data) - + def br_set(self, prio, br_mess_cb): """ Enable BR functionality on this Node, with priority and callback """ return self.mesh.br_set(True, prio, br_mess_cb) @@ -140,9 +194,9 @@ def status_str(self): def is_connected(self): return self.mesh.is_connected() - + def send_mess_external(self, ip, port, payload): - """ send mess to specified IP+port address + """ send mess to specified IP+port address data is dictionary data = { 'ip': '1:2:3::4', 'port': 12345, @@ -157,13 +211,17 @@ def send_mess_external(self, ip, port, payload): 'b': payload } return self.mesh.send_message(data) - + def config_get(self): return self.config - + + def config_set(self, config_json_dict): + PymeshConfig.write_config(config_json_dict) + return self.config + def mac(self): return self.mesh.mesh.MAC - + def ot_cli(self, command): """ Call OpenThread internal CLI """ return self.mesh.ot_cli(command) @@ -186,5 +244,4 @@ def debug_level(self, level = None): DEBUG_WARN = const(2) DEBUG_CRIT = const(1) DEBUG_NONE = const(0) """ - return self.mesh.debug_level(level) - + return debug_level(level) diff --git a/pymesh/pymesh_frozen/lib/pymesh_config.py b/pymesh/pymesh_frozen/lib/pymesh_config.py index 8678614..74a0292 100644 --- a/pymesh/pymesh_frozen/lib/pymesh_config.py +++ b/pymesh/pymesh_frozen/lib/pymesh_config.py @@ -5,15 +5,29 @@ see the Pycom Licence v1.0 document supplied with this file, or available at https://www.pycom.io/opensource/licensing ''' -import ubinascii import json from network import LoRa import machine import time +try: + from pymesh_debug import print_debug +except: + from _pymesh_debug import print_debug + +__version__ = '2' +""" +__version__ = '2' +* added BR_ena, BR_prio + +__version__ = '' +* first release + +""" + class PymeshConfig: - + CONFIG_FILENAME = "/flash/pymesh_config.json" ############################################################ @@ -24,10 +38,10 @@ class PymeshConfig: # frequency expressed in Hz, for EU868 863000000 Hz, for US915 904600000 Hz LORA_FREQ = const(869000000) - + # bandwidth options are: LoRa.BW_125KHZ, LoRa.BW_250KHZ or LoRa.BW_500KHZ LORA_BW = LoRa.BW_500KHZ - + # spreading factor options are 7 to 12 LORA_SF = const(7) @@ -41,48 +55,50 @@ class PymeshConfig: # if true, it will start as BLE Server, to be connected with mobile app BLE_API = False BLE_NAME_PREFIX = "PyGo " + ############################################################ - # Border router preference priority BR_PRIORITY_NORM = const(0) BR_PRIORITY_LOW = const(-1) BR_PRIORITY_HIGH = const(1) - + + # if true then this node is Border Router + BR_ENABLE = False + # the default BR priority + BR_PRIORITY = BR_PRIORITY_NORM + def write_config(pymesh_config, force_restart = False): cf = open(PymeshConfig.CONFIG_FILENAME, 'w') cf.write(json.dumps(pymesh_config)) cf.close() if force_restart: - print("write_config force restart") + print_debug(3, "write_config force restart") time.sleep(1) machine.deepsleep(1000) - def check_mac(pymesh_config): - lora = LoRa(mode=LoRa.LORA, region= LoRa.EU868) - MAC = int(str(ubinascii.hexlify(lora.mac()))[2:-1], 16) - + def check_mac(pymesh_config, MAC): if pymesh_config.get('MAC') is None: # if MAC config unspecified, set it to LoRa MAC - print("Set MAC in config file as ", MAC) + print_debug(3, "Set MAC in config file as " + str(MAC)) pymesh_config['MAC'] = MAC PymeshConfig.write_config(pymesh_config, False) else: mac_from_config = pymesh_config.get('MAC') if mac_from_config != MAC: - print("MAC different", mac_from_config, MAC) + print_debug(3, "MAC different"+ str(mac_from_config) + str(MAC)) pymesh_config['MAC'] = MAC # if MAC in config different than LoRa MAC, set LoRa MAC as in config file fo = open("/flash/sys/lpwan.mac", "wb") 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]) fo.write(mac_write) fo.close() - print("reset") - PymeshConfig.write_config(pymesh_config, True) - - print("MAC ok", MAC) - - def read_config(): + # print_debug(3, "reset") + PymeshConfig.write_config(pymesh_config, False) + + print_debug(3, "MAC ok" + str(MAC)) + + def read_config(MAC): file = PymeshConfig.CONFIG_FILENAME pymesh_config = {} error_file = True @@ -97,14 +113,14 @@ def read_config(): # pymesh_config['cfg_msg'] = "Pymesh configuration read from {}".format(file) error_file = False except Exception as ex: - print("Error reading {} file!\n Exception: {}".format(file, ex)) + print_debug(1, "Error reading {} file!\n Exception: {}".format(file, ex)) except Exception as ex: - print("Final error reading {} file!\n Exception: {}".format(file, ex)) - + print_debug(1, "Final error reading {} file!\n Exception: {}".format(file, ex)) + if error_file: # config file can't be read, so it needs to be created and saved pymesh_config = {} - print("Can't find",file, ", or can't be parsed as json; Set default settings and reset") + print_debug(3, "Can't find" +str(file) + ", or can't be parsed as json; Set default settings and reset") # don't write MAC, just to use the hardware one pymesh_config['LoRa'] = {"region": PymeshConfig.LORA_REGION, "freq": PymeshConfig.LORA_FREQ, @@ -115,13 +131,13 @@ def read_config(): pymesh_config['debug'] = PymeshConfig.DEBUG_LEVEL pymesh_config['ble_api'] = PymeshConfig.BLE_API pymesh_config['ble_name_prefix'] = PymeshConfig.BLE_NAME_PREFIX - print("Default settings:", pymesh_config) - PymeshConfig.check_mac(pymesh_config) - print("Default settings:", pymesh_config) + pymesh_config['br_ena'] = PymeshConfig.BR_ENABLE + pymesh_config['br_prio'] = PymeshConfig.BR_PRIORITY + + PymeshConfig.check_mac(pymesh_config, MAC) + print_debug(3, "Default settings:" + str(pymesh_config)) PymeshConfig.write_config(pymesh_config, True) - PymeshConfig.check_mac(pymesh_config) - print("Settings:", pymesh_config) + PymeshConfig.check_mac(pymesh_config, MAC) + print_debug(3, "Settings:" + str(pymesh_config)) return pymesh_config - - diff --git a/pymesh/pymesh_frozen/lib/pymesh_debug.py b/pymesh/pymesh_frozen/lib/pymesh_debug.py index b109e50..ee1e515 100644 --- a/pymesh/pymesh_frozen/lib/pymesh_debug.py +++ b/pymesh/pymesh_frozen/lib/pymesh_debug.py @@ -29,6 +29,17 @@ def print_debug(level, msg): def debug_level(level): global DEBUG - DEBUG = level + if level is None: + try: + ret = pycom.nvs_get('pymesh_debug') + except: + ret = None + return ret + try: + ret = int(level) + except: + return + + DEBUG = ret pycom.nvs_set('pymesh_debug', DEBUG) \ No newline at end of file diff --git a/pymesh/pymesh_frozen/lorawan/lorawan.py b/pymesh/pymesh_frozen/lorawan/lorawan.py new file mode 100644 index 0000000..4199f20 --- /dev/null +++ b/pymesh/pymesh_frozen/lorawan/lorawan.py @@ -0,0 +1,59 @@ +""" OTAA Node example compatible with the LoPy Nano Gateway """ + +from network import LoRa +from network import WLAN +import socket +import binascii +import struct +import time +from machine import RTC + +class Lorawan: + + def __init__(self): + # create an OTA authentication params + self.dev_eui = binascii.unhexlify('007926C9EAE4C922') + self.app_eui = binascii.unhexlify('70B3D57ED001D8C8') + self.app_key = binascii.unhexlify('2C4D6AE9CEBA8B0EB4430C33C17750CB') + + def send(self): + t0 = time.time() + print("LoRaWAN start") + lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.EU868) + + # set the 3 default channels to the same frequency (must be before sending the OTAA join request) + lora.add_channel(0, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=5) + lora.add_channel(1, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=5) + lora.add_channel(2, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=5) + + # join a network using OTAA + lora.join(activation=LoRa.OTAA, auth=(self.dev_eui, self.app_eui, self.app_key), timeout=0, dr=config.LORA_NODE_DR) + + # wait until the module has joined the network + while not lora.has_joined(): + time.sleep(2.5) + print('Not joined yet...', time.localtime()) + + # remove all the non-default channels + for i in range(3, 16): + lora.remove_channel(i) + + # create a LoRa socket + s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) + + # set the LoRaWAN data rate + s.setsockopt(socket.SOL_LORA, socket.SO_DR, config.LORA_NODE_DR) + + # make the socket non-blocking + s.setblocking(False) + + pkt = b'PKT #' + bytes([i]) + print('Sending:', pkt) + s.send(pkt) + print("LoRaWAN done in ", time.time() - t0, " seconds") + +class config: + # for EU868 + LORA_FREQUENCY = 867500000 + LORA_GW_DR = "SF7BW125" # DR_5 + LORA_NODE_DR = 5 diff --git a/pymesh/pymesh_frozen/lorawan/main.py b/pymesh/pymesh_frozen/lorawan/main.py new file mode 100644 index 0000000..33f33da --- /dev/null +++ b/pymesh/pymesh_frozen/lorawan/main.py @@ -0,0 +1,66 @@ +import time +import ubinascii +import pycom +from network import LoRa + +try: + from pymesh_config import PymeshConfig +except: + from _pymesh_config import PymeshConfig + +try: + from pymesh import Pymesh +except: + from _pymesh import Pymesh + +def new_message_cb(rcv_ip, rcv_port, rcv_data): + ''' callback triggered when a new packet arrived ''' + print('Incoming %d bytes from %s (port %d):' % + (len(rcv_data), rcv_ip, rcv_port)) + print(rcv_data) + + # user code to be inserted, to send packet to the designated Mesh-external interface + for _ in range(3): + pycom.rgbled(0x888888) + time.sleep(.2) + pycom.rgbled(0) + time.sleep(.1) + return + + +pycom.heartbeat(False) + +lora = LoRa(mode=LoRa.LORA, region= LoRa.EU868) +lora_mac = int(str(ubinascii.hexlify(lora.mac()))[2:-1], 16) + +# read config file, or set default values +pymesh_config = PymeshConfig.read_config(lora_mac) + +#initialize Pymesh +pymesh = Pymesh(pymesh_config, new_message_cb) + +while not pymesh.is_connected(): + print(pymesh.status_str()) + time.sleep(3) + +# send message to the Node having MAC address 5 +pymesh.send_mess(20, "Hello World") + +print("done Pymesh init, forever loop, exit/stop with Ctrl+C multiple times") + +from lorawan import Lorawan +lorawan = Lorawan() +t0 = time.time() + +while True: + if time.time() - t0 > 60: + pymesh.pause() + + lorawan.send() + + pymesh.resume() + t0 = time.time() + if time.time() - t0 > 35: + pymesh.send_mess(20, "heloo again, #22 here, " + str(time.time())) + + time.sleep(5) diff --git a/pymesh/pymesh_frozen/main-pybytes.py b/pymesh/pymesh_frozen/main-pybytes.py new file mode 100644 index 0000000..f38d127 --- /dev/null +++ b/pymesh/pymesh_frozen/main-pybytes.py @@ -0,0 +1,22 @@ +import time +import pycom + +# todo: add try/except for checking pybytes object exists +pymesh = pybytes.__pymesh.__pymesh + +print("Set maximum debug level, disable debug using pymesh.debug_level(0)") +pymesh.debug_level(5) + +while not pymesh.is_connected(): + print(pymesh.status_str()) + time.sleep(3) + +print(pymesh.status_str()) +# send message to the Node having MAC address 5 +pymesh.send_mess(18, "Hello World") + +print("done Pymesh init, CLI is started, h - help/command list, stop - CLI will be stopped") +pymesh.cli_start() + +# send a packet to Pybytes, thru the BR (if any enabled) +# pybytes.send_signal(100, "Hello from device" + str(pymesh.mac())) diff --git a/pymesh/pymesh_frozen/main.py b/pymesh/pymesh_frozen/main.py index a22bd2f..5d7c17a 100644 --- a/pymesh/pymesh_frozen/main.py +++ b/pymesh/pymesh_frozen/main.py @@ -1,15 +1,17 @@ -import pycom import time +import ubinascii +import pycom +from network import LoRa -# try: -from pymesh_config import PymeshConfig -# except: -# from _pymesh_config import PymeshConfig +try: + from pymesh_config import PymeshConfig +except: + from _pymesh_config import PymeshConfig -# try: -from pymesh import Pymesh -# except: -# from _pymesh import Pymesh +try: + from pymesh import Pymesh +except: + from _pymesh import Pymesh def new_message_cb(rcv_ip, rcv_port, rcv_data): ''' callback triggered when a new packet arrived ''' @@ -28,8 +30,11 @@ def new_message_cb(rcv_ip, rcv_port, rcv_data): pycom.heartbeat(False) +lora = LoRa(mode=LoRa.LORA, region= LoRa.EU868) +lora_mac = int(str(ubinascii.hexlify(lora.mac()))[2:-1], 16) + # read config file, or set default values -pymesh_config = PymeshConfig.read_config() +pymesh_config = PymeshConfig.read_config(lora_mac) #initialize Pymesh pymesh = Pymesh(pymesh_config, new_message_cb) @@ -69,8 +74,8 @@ def new_message_cb(rcv_ip, rcv_port, rcv_data): # port = 5555 # pymesh.send_mess_external(ip, port, "Hello World") -print("done Pymesh init, forever loop, exit/stop with Ctrl+C multiple times") -# set BR with callback +print("done Pymesh init, CLI is started, h - help/command list, stop - CLI will be stopped") +pymesh.cli_start() -while True: - time.sleep(3) +# while True: +# time.sleep(3) diff --git a/pymesh/pymesh_frozen/main_BR.py b/pymesh/pymesh_frozen/main_BR.py index e72560a..fdb9780 100644 --- a/pymesh/pymesh_frozen/main_BR.py +++ b/pymesh/pymesh_frozen/main_BR.py @@ -1,8 +1,11 @@ import time +import ubinascii import pycom +from network import LoRa # 2 = test pybytes OTA feature -__VERSION__ = 3 +# 4 = added device_id (pybytes token) in the packets to BR +__VERSION__ = 4 try: from pymesh_config import PymeshConfig @@ -14,8 +17,8 @@ except: from _pymesh import Pymesh -# LoRa mac that will be self-defined as Border Routers -MAC_BR = {2,4} +PACK_TOCKEN_PREFIX = "tkn" +PACK_TOCKEN_SEP = "#" print("Scripts version ", __VERSION__) @@ -51,15 +54,25 @@ def new_br_message_cb(rcv_ip, rcv_port, rcv_data, dest_ip, dest_port): # time.sleep(.2) if pybytes is not None and pybytes.isconnected(): - pkt = 'BR %d B from %s (%d), to %s ( %d): %s'%(len(rcv_data), rcv_ip, rcv_port, dest_ip, dest_port, str(rcv_data)) + # try to find Pybytes Token if include in rcv_data + token = "" + if rcv_data.startswith(PACK_TOCKEN_PREFIX): + x = rcv_data.split(PACK_TOCKEN_SEP.encode()) + if len(x)>2: + token = x[1] + rcv_data = rcv_data[len(PACK_TOCKEN_PREFIX) + len(token) + len(PACK_TOCKEN_SEP):] + pkt = 'BR %d B from %s (%s), to %s ( %d): %s'%(len(rcv_data), token, rcv_ip, dest_ip, dest_port, str(rcv_data)) pybytes.send_signal(1, pkt) return pycom.heartbeat(False) +lora = LoRa(mode=LoRa.LORA, region= LoRa.EU868) +lora_mac = int(str(ubinascii.hexlify(lora.mac()))[2:-1], 16) + # read config file, or set default values -pymesh_config = PymeshConfig.read_config() +pymesh_config = PymeshConfig.read_config(lora_mac) #initialize Pymesh pymesh = Pymesh(pymesh_config, new_message_cb) @@ -85,7 +98,13 @@ def new_br_message_cb(rcv_ip, rcv_port, rcv_data, dest_ip, dest_port): pyb_port = pymesh.mac() & 0xFFFF pyb_ip = '1:2:3::' + hex(pyb_port)[2:] -pkt_start = "Hello, from " + str(pymesh.mac()) + ", time " +pkt_start = "" +# add pybytes token +if pybytes is not None: + pyb_dev_id = pybytes.get_config().get("device_id", "None") + pkt_start = PACK_TOCKEN_PREFIX + PACK_TOCKEN_SEP + pyb_dev_id + PACK_TOCKEN_SEP +pkt_start = pkt_start + "Hello, from " + str(pymesh.mac()) + ", time " + br_enabled = False @@ -94,7 +113,7 @@ def new_br_message_cb(rcv_ip, rcv_port, rcv_data, dest_ip, dest_port): free_mem = pycom.get_free_heap() - if pymesh.mac() in MAC_BR: + if pymesh_config.get("br_ena", False): if pybytes is not None and pybytes.isconnected(): if not br_enabled: br_enabled = True diff --git a/pymesh/readme.md b/pymesh/readme.md new file mode 100644 index 0000000..0be1a7a --- /dev/null +++ b/pymesh/readme.md @@ -0,0 +1,3 @@ +# For reference only + +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. diff --git a/pyscan/lib/LIS2HH12.py b/pyscan/lib/LIS2HH12.py deleted file mode 100644 index 49a6b5a..0000000 --- a/pyscan/lib/LIS2HH12.py +++ /dev/null @@ -1,178 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2019, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -import math -import time -import struct -from machine import Pin - - -FULL_SCALE_2G = const(0) -FULL_SCALE_4G = const(2) -FULL_SCALE_8G = const(3) - -ODR_POWER_DOWN = const(0) -ODR_10_HZ = const(1) -ODR_50_HZ = const(2) -ODR_100_HZ = const(3) -ODR_200_HZ = const(4) -ODR_400_HZ = const(5) -ODR_800_HZ = const(6) - -ACC_G_DIV = 1000 * 65536 - - -class LIS2HH12: - - ACC_I2CADDR = const(30) - - PRODUCTID_REG = const(0x0F) - CTRL1_REG = const(0x20) - CTRL2_REG = const(0x21) - CTRL3_REG = const(0x22) - CTRL4_REG = const(0x23) - CTRL5_REG = const(0x24) - ACC_X_L_REG = const(0x28) - ACC_X_H_REG = const(0x29) - ACC_Y_L_REG = const(0x2A) - ACC_Y_H_REG = const(0x2B) - ACC_Z_L_REG = const(0x2C) - ACC_Z_H_REG = const(0x2D) - ACT_THS = const(0x1E) - ACT_DUR = const(0x1F) - - SCALES = {FULL_SCALE_2G: 4000, FULL_SCALE_4G: 8000, FULL_SCALE_8G: 16000} - ODRS = [0, 10, 50, 100, 200, 400, 800] - - def __init__(self, pysense = None, sda = 'P22', scl = 'P21'): - if pysense is not None: - self.i2c = pysense.i2c - else: - from machine import I2C - self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) - - self.odr = 0 - self.full_scale = 0 - self.x = 0 - self.y = 0 - self.z = 0 - self.int_pin = None - self.act_dur = 0 - self.debounced = False - - whoami = self.i2c.readfrom_mem(ACC_I2CADDR , PRODUCTID_REG, 1) - if (whoami[0] != 0x41): - raise ValueError("LIS2HH12 not found") - - # enable acceleration readings at 50Hz - self.set_odr(ODR_50_HZ) - - # change the full-scale to 4g - self.set_full_scale(FULL_SCALE_4G) - - # set the interrupt pin as active low and open drain - self.set_register(CTRL5_REG, 3, 0, 3) - - # make a first read - self.acceleration() - - def acceleration(self): - x = self.i2c.readfrom_mem(ACC_I2CADDR , ACC_X_L_REG, 2) - self.x = struct.unpack(' self.SCALES[self.full_scale]: - error = "threshold %d exceeds full scale %d" % (thresold, self.SCALES[self.full_scale]) - print(error) - raise ValueError(error) - - if threshold < self.SCALES[self.full_scale] / 128: - error = "threshold %d below resolution %d" % (thresold, self.SCALES[self.full_scale]/128) - print(error) - raise ValueError(error) - - if duration > 255 * 1000 * 8 / self.ODRS[self.odr]: - error = "duration %d exceeds max possible value %d" % (duration, 255 * 1000 * 8 / self.ODRS[self.odr]) - print(error) - raise ValueError(error) - - if duration < 1000 * 8 / self.ODRS[self.odr]: - error = "duration %d below resolution %d" % (duration, 1000 * 8 / self.ODRS[self.odr]) - print(error) - raise ValueError(error) - - _ths = int(127 * threshold / self.SCALES[self.full_scale]) & 0x7F - _dur = int((duration * self.ODRS[self.odr]) / 1000 / 8) - - self.i2c.writeto_mem(ACC_I2CADDR, ACT_THS, _ths) - self.i2c.writeto_mem(ACC_I2CADDR, ACT_DUR, _dur) - - # enable the activity/inactivity interrupt - self.set_register(CTRL3_REG, 1, 5, 1) - - self._user_handler = handler - self.int_pin = Pin('P13', mode=Pin.IN) - self.int_pin.callback(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=self._int_handler) - - # return actual used thresold and duration - return (_ths * self.SCALES[self.full_scale] / 128, _dur * 8 * 1000 / self.ODRS[self.odr]) - - def activity(self): - if not self.debounced: - time.sleep_ms(self.act_dur) - self.debounced = True - if self.int_pin(): - return True - return False - - def _int_handler(self, pin_o): - if self._user_handler is not None: - self._user_handler(pin_o) - else: - if pin_o(): - print('Activity interrupt') - else: - print('Inactivity interrupt') diff --git a/pyscan/lib/LTR329ALS01.py b/pyscan/lib/LTR329ALS01.py deleted file mode 100644 index 49e3439..0000000 --- a/pyscan/lib/LTR329ALS01.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2019, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -import time -from machine import I2C - -class LTR329ALS01: - ALS_I2CADDR = const(0x29) # The device's I2C address - - ALS_CONTR_REG = const(0x80) - ALS_MEAS_RATE_REG = const(0x85) - - ALS_DATA_CH1_LOW = const(0x88) - ALS_DATA_CH1_HIGH = const(0x89) - ALS_DATA_CH0_LOW = const(0x8A) - ALS_DATA_CH0_HIGH = const(0x8B) - - ALS_GAIN_1X = const(0x00) - ALS_GAIN_2X = const(0x01) - ALS_GAIN_4X = const(0x02) - ALS_GAIN_8X = const(0x03) - ALS_GAIN_48X = const(0x06) - ALS_GAIN_96X = const(0x07) - - ALS_INT_50 = const(0x01) - ALS_INT_100 = const(0x00) - ALS_INT_150 = const(0x04) - ALS_INT_200 = const(0x02) - ALS_INT_250 = const(0x05) - ALS_INT_300 = const(0x06) - ALS_INT_350 = const(0x07) - ALS_INT_400 = const(0x03) - - ALS_RATE_50 = const(0x00) - ALS_RATE_100 = const(0x01) - ALS_RATE_200 = const(0x02) - ALS_RATE_500 = const(0x03) - ALS_RATE_1000 = const(0x04) - ALS_RATE_2000 = const(0x05) - - def __init__(self, pysense = None, sda = 'P22', scl = 'P21', gain = ALS_GAIN_1X, integration = ALS_INT_100, rate = ALS_RATE_500): - if pysense is not None: - self.i2c = pysense.i2c - else: - self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) - - contr = self._getContr(gain) - self.i2c.writeto_mem(ALS_I2CADDR, ALS_CONTR_REG, bytearray([contr])) - - measrate = self._getMeasRate(integration, rate) - self.i2c.writeto_mem(ALS_I2CADDR, ALS_MEAS_RATE_REG, bytearray([measrate])) - - time.sleep(0.01) - - def _getContr(self, gain): - return ((gain & 0x07) << 2) + 0x01 - - def _getMeasRate(self, integration, rate): - return ((integration & 0x07) << 3) + (rate & 0x07) - - def _getWord(self, high, low): - return ((high & 0xFF) << 8) + (low & 0xFF) - - def light(self): - ch1low = self.i2c.readfrom_mem(ALS_I2CADDR , ALS_DATA_CH1_LOW, 1) - ch1high = self.i2c.readfrom_mem(ALS_I2CADDR , ALS_DATA_CH1_HIGH, 1) - data1 = int(self._getWord(ch1high[0], ch1low[0])) - - ch0low = self.i2c.readfrom_mem(ALS_I2CADDR , ALS_DATA_CH0_LOW, 1) - ch0high = self.i2c.readfrom_mem(ALS_I2CADDR , ALS_DATA_CH0_HIGH, 1) - data0 = int(self._getWord(ch0high[0], ch0low[0])) - - return (data0, data1) diff --git a/pyscan/lib/pyscan.py b/pyscan/lib/pyscan.py deleted file mode 100644 index 0e956c7..0000000 --- a/pyscan/lib/pyscan.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2020, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -from pycoproc import Pycoproc - -__version__ = '1.0.1' - -class Pyscan(Pycoproc): - - def __init__(self, i2c=None, sda='P22', scl='P21'): - Pycoproc.__init__(self, Pycoproc.PYSCAN, i2c, sda, scl) diff --git a/pyscan/main.py b/pyscan/main.py deleted file mode 100644 index c0d7b4f..0000000 --- a/pyscan/main.py +++ /dev/null @@ -1,85 +0,0 @@ -''' -Simple Pyscan NFC / MiFare Classic Example -Copyright (c) 2019, Pycom Limited. - -This example runs the NFC discovery loop in a thread. -If a card is detected it will read the UID and compare it to VALID_CARDS -RGB LED is BLUE while waiting for card, -GREEN if card is valid, RED if card is invalid -''' - -DEBUG = False # change to True to see debug messages - -from pyscan import Pyscan -from MFRC630 import MFRC630 -from LIS2HH12 import LIS2HH12 -from LTR329ALS01 import LTR329ALS01 -import binascii -import time -import pycom -import _thread - -VALID_CARDS = [[0x43, 0x95, 0xDD, 0xF8], - [0x43, 0x95, 0xDD, 0xF9]] - -py = Pyscan() -nfc = MFRC630(py) -lt = LTR329ALS01(py) -li = LIS2HH12(py) - -RGB_BRIGHTNESS = 0x8 - -RGB_RED = (RGB_BRIGHTNESS << 16) -RGB_GREEN = (RGB_BRIGHTNESS << 8) -RGB_BLUE = (RGB_BRIGHTNESS) - -# Make sure heartbeat is disabled before setting RGB LED -pycom.heartbeat(False) - -# Initialise the MFRC630 with some settings -nfc.mfrc630_cmd_init() - -def check_uid(uid, len): - return VALID_CARDS.count(uid[:len]) - -def print_debug(msg): - if DEBUG: - print(msg) - -def send_sensor_data(name, timeout): - while(True): - print(lt.light()) - print(li.acceleration()) - time.sleep(timeout) - -def discovery_loop(nfc, id): - while True: - # Send REQA for ISO14443A card type - print_debug('Sending REQA for ISO14443A card type...') - atqa = nfc.mfrc630_iso14443a_WUPA_REQA(nfc.MFRC630_ISO14443_CMD_REQA) - print_debug('Response: {}'.format(atqa)) - if (atqa != 0): - # A card has been detected, read UID - print_debug('A card has been detected, read UID...') - uid = bytearray(10) - uid_len = nfc.mfrc630_iso14443a_select(uid) - print_debug('UID has length: {}'.format(uid_len)) - if (uid_len > 0): - print_debug('Checking if card with UID: [{:s}] is listed in VALID_CARDS...'.format(binascii.hexlify(uid[:uid_len],' ').upper())) - if (check_uid(list(uid), uid_len)) > 0: - print_debug('Card is listed, turn LED green') - pycom.rgbled(RGB_GREEN) - else: - print_debug('Card is not listed, turn LED red') - pycom.rgbled(RGB_RED) - else: - # No card detected - print_debug('Did not detect any card...') - pycom.rgbled(RGB_BLUE) - nfc.mfrc630_cmd_reset() - time.sleep(.5) - nfc.mfrc630_cmd_init() - -# This is the start of our main execution... start the thread -_thread.start_new_thread(discovery_loop, (nfc, 0)) -_thread.start_new_thread(send_sensor_data, ('Thread 2', 10)) diff --git a/pysense/lib/pysense.py b/pysense/lib/pysense.py deleted file mode 100644 index 964ae24..0000000 --- a/pysense/lib/pysense.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2020, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -# See https://docs.pycom.io for more information regarding library specifics - -from pycoproc import Pycoproc - -__version__ = '1.4.1' - -class Pysense(Pycoproc): - - def __init__(self, i2c=None, sda='P22', scl='P21'): - Pycoproc.__init__(self, Pycoproc.PYSENSE, i2c, sda, scl) diff --git a/pytrack/lib/LIS2HH12.py b/pytrack/lib/LIS2HH12.py deleted file mode 100644 index af2812d..0000000 --- a/pytrack/lib/LIS2HH12.py +++ /dev/null @@ -1,178 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2020, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -import math -import time -import struct -from machine import Pin - - -FULL_SCALE_2G = const(0) -FULL_SCALE_4G = const(2) -FULL_SCALE_8G = const(3) - -ODR_POWER_DOWN = const(0) -ODR_10_HZ = const(1) -ODR_50_HZ = const(2) -ODR_100_HZ = const(3) -ODR_200_HZ = const(4) -ODR_400_HZ = const(5) -ODR_800_HZ = const(6) - -ACC_G_DIV = 1000 * 65536 - - -class LIS2HH12: - - ACC_I2CADDR = const(30) - - PRODUCTID_REG = const(0x0F) - CTRL1_REG = const(0x20) - CTRL2_REG = const(0x21) - CTRL3_REG = const(0x22) - CTRL4_REG = const(0x23) - CTRL5_REG = const(0x24) - ACC_X_L_REG = const(0x28) - ACC_X_H_REG = const(0x29) - ACC_Y_L_REG = const(0x2A) - ACC_Y_H_REG = const(0x2B) - ACC_Z_L_REG = const(0x2C) - ACC_Z_H_REG = const(0x2D) - ACT_THS = const(0x1E) - ACT_DUR = const(0x1F) - - SCALES = {FULL_SCALE_2G: 4000, FULL_SCALE_4G: 8000, FULL_SCALE_8G: 16000} - ODRS = [0, 10, 50, 100, 200, 400, 800] - - def __init__(self, pysense = None, sda = 'P22', scl = 'P21'): - if pysense is not None: - self.i2c = pysense.i2c - else: - from machine import I2C - self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) - - self.odr = 0 - self.full_scale = 0 - self.x = 0 - self.y = 0 - self.z = 0 - self.int_pin = None - self.act_dur = 0 - self.debounced = False - - whoami = self.i2c.readfrom_mem(ACC_I2CADDR , PRODUCTID_REG, 1) - if (whoami[0] != 0x41): - raise ValueError("LIS2HH12 not found") - - # enable acceleration readings at 50Hz - self.set_odr(ODR_50_HZ) - - # change the full-scale to 4g - self.set_full_scale(FULL_SCALE_4G) - - # set the interrupt pin as active low and open drain - self.set_register(CTRL5_REG, 3, 0, 3) - - # make a first read - self.acceleration() - - def acceleration(self): - x = self.i2c.readfrom_mem(ACC_I2CADDR , ACC_X_L_REG, 2) - self.x = struct.unpack(' self.SCALES[self.full_scale]: - error = "threshold %d exceeds full scale %d" % (thresold, self.SCALES[self.full_scale]) - print(error) - raise ValueError(error) - - if threshold < self.SCALES[self.full_scale] / 128: - error = "threshold %d below resolution %d" % (thresold, self.SCALES[self.full_scale]/128) - print(error) - raise ValueError(error) - - if duration > 255 * 1000 * 8 / self.ODRS[self.odr]: - error = "duration %d exceeds max possible value %d" % (duration, 255 * 1000 * 8 / self.ODRS[self.odr]) - print(error) - raise ValueError(error) - - if duration < 1000 * 8 / self.ODRS[self.odr]: - error = "duration %d below resolution %d" % (duration, 1000 * 8 / self.ODRS[self.odr]) - print(error) - raise ValueError(error) - - _ths = int(127 * threshold / self.SCALES[self.full_scale]) & 0x7F - _dur = int((duration * self.ODRS[self.odr]) / 1000 / 8) - - self.i2c.writeto_mem(ACC_I2CADDR, ACT_THS, _ths) - self.i2c.writeto_mem(ACC_I2CADDR, ACT_DUR, _dur) - - # enable the activity/inactivity interrupt - self.set_register(CTRL3_REG, 1, 5, 1) - - self._user_handler = handler - self.int_pin = Pin('P13', mode=Pin.IN) - self.int_pin.callback(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=self._int_handler) - - # return actual used thresold and duration - return (_ths * self.SCALES[self.full_scale] / 128, _dur * 8 * 1000 / self.ODRS[self.odr]) - - def activity(self): - if not self.debounced: - time.sleep_ms(self.act_dur) - self.debounced = True - if self.int_pin(): - return True - return False - - def _int_handler(self, pin_o): - if self._user_handler is not None: - self._user_handler(pin_o) - else: - if pin_o(): - print('Activity interrupt') - else: - print('Inactivity interrupt') diff --git a/pytrack/lib/pytrack.py b/pytrack/lib/pytrack.py deleted file mode 100644 index 07d600d..0000000 --- a/pytrack/lib/pytrack.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2020, Pycom Limited. -# -# This software is licensed under the GNU GPL version 3 or any -# later version, with permitted additional terms. For more information -# see the Pycom Licence v1.0 document supplied with this file, or -# available at https://www.pycom.io/opensource/licensing -# - -from pycoproc import Pycoproc - -__version__ = '1.4.1' - -class Pytrack(Pycoproc): - - def __init__(self, i2c=None, sda='P22', scl='P21'): - Pycoproc.__init__(self, Pycoproc.PYTRACK, i2c, sda, scl) diff --git a/shields/README.md b/shields/README.md new file mode 100644 index 0000000..0586192 --- /dev/null +++ b/shields/README.md @@ -0,0 +1,13 @@ +

+ +# Pytrack, Pysense and Pyscan libraries + +## Introduction +This directory contains libraries and out of the box examples for Pysense, Pytrack, and Pyscan, both versions 1 and 2.0x of these shields. + +1. Upload all the files to your module +2. Open Pymakr +3. Run an example that matches to your shield + +Note: For using Pyscan, you need to upload either MFRC630.mpy or MFRC630.py. +It is always recommended to use MFRC630.mpy as it takes less space on your module. diff --git a/pytrack/lib/L76GNSS.py b/shields/lib/L76GNSS.py similarity index 76% rename from pytrack/lib/L76GNSS.py rename to shields/lib/L76GNSS.py index 8b6fca6..c493eb1 100644 --- a/pytrack/lib/L76GNSS.py +++ b/shields/lib/L76GNSS.py @@ -18,7 +18,7 @@ class L76GNSS: GPS_I2CADDR = const(0x10) - def __init__(self, pytrack=None, sda='P22', scl='P21', timeout=None): + def __init__(self, pytrack=None, sda='P22', scl='P21', timeout=None, buffer=64): if pytrack is not None: self.i2c = pytrack.i2c else: @@ -29,12 +29,13 @@ def __init__(self, pytrack=None, sda='P22', scl='P21', timeout=None): self.timeout = timeout self.timeout_status = True + self.buffer = buffer self.reg = bytearray(1) self.i2c.writeto(GPS_I2CADDR, self.reg) def _read(self): - self.reg = self.i2c.readfrom(GPS_I2CADDR, 64) + self.reg = self.i2c.readfrom(GPS_I2CADDR, self.buffer) return self.reg def _convert_coords(self, gngll_s): @@ -94,3 +95,25 @@ def coordinates(self, debug=False): return(None, None) else: return(lat_d, lon_d) + + def dump_nmea(self): + nmea = b'' + while True: + nmea = self._read().lstrip(b'\n\n').rstrip(b'\n\n') + start_idx = nmea.find(b'$') + #print('raw[{}]: {}'.format(start_idx, nmea)) + if nmea is not None and len(nmea) > 0: + if start_idx != 0: + if len(nmea[:start_idx]) > 1: + print('{}'.format(nmea[:start_idx].decode('ASCII')), end='') + if len(nmea[start_idx:]) > 1: + print('{}'.format(nmea[start_idx:].decode('ASCII')), end='') + + def _checksum(self, nmeadata): + calc_cksum = 0 + for s in nmeadata: + calc_cksum ^= ord(s) + return('{:02X}'.format(calc_cksum)) + + def write(self, data): + self.i2c.writeto(GPS_I2CADDR, '${}*{}\r\n'.format(data, self._checksum(data)) ) diff --git a/pysense/lib/LIS2HH12.py b/shields/lib/LIS2HH12.py similarity index 100% rename from pysense/lib/LIS2HH12.py rename to shields/lib/LIS2HH12.py diff --git a/pysense/lib/LTR329ALS01.py b/shields/lib/LTR329ALS01.py similarity index 65% rename from pysense/lib/LTR329ALS01.py rename to shields/lib/LTR329ALS01.py index 9087156..8915ac4 100644 --- a/pysense/lib/LTR329ALS01.py +++ b/shields/lib/LTR329ALS01.py @@ -28,6 +28,14 @@ class LTR329ALS01: ALS_GAIN_8X = const(0x03) ALS_GAIN_48X = const(0x06) ALS_GAIN_96X = const(0x07) + ALS_GAIN_VALUES = { + ALS_GAIN_1X: 1, + ALS_GAIN_2X: 2, + ALS_GAIN_4X: 4, + ALS_GAIN_8X: 8, + ALS_GAIN_48X: 48, + ALS_GAIN_96X: 96 + } ALS_INT_50 = const(0x01) ALS_INT_100 = const(0x00) @@ -37,6 +45,16 @@ class LTR329ALS01: ALS_INT_300 = const(0x06) ALS_INT_350 = const(0x07) ALS_INT_400 = const(0x03) + ALS_INT_VALUES = { + ALS_INT_50: 0.5, + ALS_INT_100: 1, + ALS_INT_150: 1.5, + ALS_INT_200: 2, + ALS_INT_250: 2.5, + ALS_INT_300: 3, + ALS_INT_350: 3.5, + ALS_INT_400: 4 + } ALS_RATE_50 = const(0x00) ALS_RATE_100 = const(0x01) @@ -51,6 +69,9 @@ def __init__(self, pysense = None, sda = 'P22', scl = 'P21', gain = ALS_GAIN_1X, else: self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl)) + self.gain = gain + self.integration = integration + contr = self._getContr(gain) self.i2c.writeto_mem(ALS_I2CADDR, ALS_CONTR_REG, bytearray([contr])) @@ -78,3 +99,19 @@ def light(self): data0 = int(self._getWord(ch0high[0], ch0low[0])) return (data0, data1) + + def lux(self): + # Calculate Lux value from formular in Appendix A of the datasheet + light_level = self.light() + if light_level[0]+light_level[1] > 0: + ratio = light_level[1]/(light_level[0]+light_level[1]) + if ratio < 0.45: + return (1.7743 * light_level[0] + 1.1059 * light_level[1]) / self.ALS_GAIN_VALUES[self.gain] / self.ALS_INT_VALUES[self.integration] + elif ratio < 0.64 and ratio >= 0.45: + return (4.2785 * light_level[0] - 1.9548 * light_level[1]) / self.ALS_GAIN_VALUES[self.gain] / self.ALS_INT_VALUES[self.integration] + elif ratio < 0.85 and ratio >= 0.64: + return (0.5926 * light_level[0] + 0.1185 * light_level[1]) / self.ALS_GAIN_VALUES[self.gain] / self.ALS_INT_VALUES[self.integration] + else: + return 0 + else: + return 0 diff --git a/shields/lib/MFRC630.mpy b/shields/lib/MFRC630.mpy new file mode 100644 index 0000000..f6af944 Binary files /dev/null and b/shields/lib/MFRC630.mpy differ diff --git a/pyscan/lib/MFRC630.mpy b/shields/lib/MFRC630.mpy-1.18 similarity index 99% rename from pyscan/lib/MFRC630.mpy rename to shields/lib/MFRC630.mpy-1.18 index 51076eb..7baa005 100644 Binary files a/pyscan/lib/MFRC630.mpy and b/shields/lib/MFRC630.mpy-1.18 differ diff --git a/lib/MFRC630/MFRC630.py b/shields/lib/MFRC630.py similarity index 97% rename from lib/MFRC630/MFRC630.py rename to shields/lib/MFRC630.py index dc4aff8..0d6c2f2 100644 --- a/lib/MFRC630/MFRC630.py +++ b/shields/lib/MFRC630.py @@ -370,7 +370,7 @@ def mfrc630_MF_read_block(self, block_address, dest): buffer_length = self.mfrc630_fifo_length() rx_len = buffer_length if (buffer_length <= 16) else 16 dest = self.mfrc630_read_fifo(rx_len) - return rx_len + return dest def mfrc630_iso14443a_WUPA_REQA(self, instruction): @@ -702,7 +702,7 @@ def mfrc630_iso14443a_select(self, uid): uid[(cascade_level - 1) * 3 + UIDn] = uid_this_level[UIDn]; # Finally, return the length of the UID that's now at the uid "pointer". - return cascade_level * 3 + 1; + return cascade_level * 3 + 1 self.print_debug("Exit cascade loop nr. %d: " % cascade_level) self.mfrc630_print_block(uid, 10) diff --git a/pysense/lib/MPL3115A2.py b/shields/lib/MPL3115A2.py similarity index 92% rename from pysense/lib/MPL3115A2.py rename to shields/lib/MPL3115A2.py index b0a44c7..ce90631 100644 --- a/pysense/lib/MPL3115A2.py +++ b/shields/lib/MPL3115A2.py @@ -79,8 +79,10 @@ def __init__(self, pysense = None, sda = 'P22', scl = 'P21', mode = PRESSURE): raise MPL3115A2exception("Error with MPL3115A2") def _read_status(self): - while True: + read_attempts = 0 + while read_attempts < 500: self.i2c.readfrom_mem_into(MPL3115_I2CADDR, MPL3115_STATUS, self.STA_reg) + read_attempts += 1 if(self.STA_reg[0] == 0): time.sleep(0.01) @@ -90,6 +92,11 @@ def _read_status(self): else: return False + # If we get here the sensor isn't responding. Reset it so next time in it should work + self.i2c.writeto_mem(MPL3115_I2CADDR, MPL3115_CTRL_REG1, bytes([0x00])) # put into standby + self.i2c.writeto_mem(MPL3115_I2CADDR, MPL3115_CTRL_REG1, bytes([0x04])) # reset + return False + def pressure(self): if self.mode == ALTITUDE: raise MPL3115A2exception("Incorrect Measurement Mode MPL3115A2") diff --git a/pysense/lib/SI7006A20.py b/shields/lib/SI7006A20.py similarity index 79% rename from pysense/lib/SI7006A20.py rename to shields/lib/SI7006A20.py index d9a4025..f49a302 100644 --- a/pysense/lib/SI7006A20.py +++ b/shields/lib/SI7006A20.py @@ -22,8 +22,18 @@ class SI7006A20: SI7006A20_I2C_ADDR = const(0x40) + # I2C commands TEMP_NOHOLDMASTER = const(0xF3) HUMD_NOHOLDMASTER = const(0xF5) + WRITE_USER_REG1 = const(0xE6) + READ_USER_REG1 = const(0xE7) + WRITE_HEATER_CTRL_REG = const(0x51) + READ_HEATER_CTRL_REG = const(0x11) + + # Register masks and offsets + USER_REG1_HTR_ENABLE_MASK = const(0b00000100) + USER_REG1_HTR_ENABLE_OFFSET = const(0x02) + HTR_CTRL_REG_MASK = const(0b00001111) def __init__(self, pysense = None, sda = 'P22', scl = 'P21'): if pysense is not None: @@ -55,18 +65,32 @@ def humidity(self): def read_user_reg(self): """ reading the user configuration register """ - self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([0xE7])) + self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([READ_USER_REG1])) time.sleep(0.5) data = self.i2c.readfrom(SI7006A20_I2C_ADDR, 1) return data[0] def read_heater_reg(self): """ reading the heater configuration register """ - self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([0x11])) + self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([READ_HEATER_CTRL_REG])) time.sleep(0.5) data = self.i2c.readfrom(SI7006A20_I2C_ADDR, 1) return data[0] + def write_heater_reg(self, heater_value): + """ writing the heater configuration register """ + # We should only set the bottom four bits of this register + heater_setting = heater_value & HTR_CTRL_REG_MASK + self.write_reg(WRITE_HEATER_CTRL_REG, heater_setting) + + def heater_control(self, on_off): + """ turn the heater on or off """ + # Get current settings for everything else + user_reg = self.read_user_reg() + # Set the heater bit + user_reg = (user_reg & ~USER_REG1_HTR_ENABLE_MASK) | (on_off << USER_REG1_HTR_ENABLE_OFFSET) + self.write_reg(WRITE_USER_REG1, user_reg) + def read_electronic_id(self): """ reading electronic identifier """ self.i2c.writeto(SI7006A20_I2C_ADDR, bytearray([0xFA]) + bytearray([0x0F])) diff --git a/lib/pycoproc/pycoproc.py b/shields/lib/pycoproc_1.py similarity index 99% rename from lib/pycoproc/pycoproc.py rename to shields/lib/pycoproc_1.py index 48fa051..a8f7e82 100644 --- a/lib/pycoproc/pycoproc.py +++ b/shields/lib/pycoproc_1.py @@ -127,7 +127,7 @@ def __init__(self, board_type, i2c=None, sda='P22', scl='P21'): self.mask_bits_in_memory(TRISC_ADDR, ~(1 << 7)) if self.read_fw_version() < 6: - raise ValueError('Firmware out of date') + raise ValueError('Firmware for Shield1 out of date') def _write(self, data, wait=True): diff --git a/shields/lib/pycoproc_2.py b/shields/lib/pycoproc_2.py new file mode 100644 index 0000000..a1ead51 --- /dev/null +++ b/shields/lib/pycoproc_2.py @@ -0,0 +1,381 @@ +#!/usr/bin/env python +# +# Copyright (c) 2020, Pycom Limited. +# +# This software is licensed under the GNU GPL version 3 or any +# later version, with permitted additional terms. For more information +# see the Pycom Licence v1.0 document supplied with this file, or +# available at https://www.pycom.io/opensource/licensing +# + +# See https://docs.pycom.io for more information regarding library specifics + +from machine import Pin +from machine import I2C +import time +import pycom + +__version__ = '0.0.5' + +""" PIC MCU wakeup reason types """ +WAKE_REASON_ACCELEROMETER = 1 +WAKE_REASON_PUSH_BUTTON = 2 +WAKE_REASON_TIMER = 4 +WAKE_REASON_INT_PIN = 8 + +class Pycoproc: + """ class for handling the interaction with PIC MCU """ + + I2C_SLAVE_ADDR = const(8) + + CMD_PEEK = const(0x0) + CMD_POKE = const(0x01) + CMD_MAGIC = const(0x02) + CMD_HW_VER = const(0x10) + CMD_FW_VER = const(0x11) #define SW_VERSION (15) + CMD_PROD_ID = const(0x12) #USB product ID, e.g. PYSENSE (0xF012) + CMD_SETUP_SLEEP = const(0x20) + CMD_GO_SLEEP = const(0x21) + CMD_CALIBRATE = const(0x22) + CMD_GO_NAP = const(0x23) + CMD_BAUD_CHANGE = const(0x30) + CMD_DFU = const(0x31) + CMD_RESET = const(0x40) + # CMD_GO_NAP options + SD_CARD_OFF = const(0x1) + SENSORS_OFF = const(0x2) + ACCELEROMETER_OFF = const(0x4) + FIPY_OFF = const(0x8) + + + REG_CMD = const(0) + REG_ADDRL = const(1) + REG_ADDRH = const(2) + REG_AND = const(3) + REG_OR = const(4) + REG_XOR = const(5) + + ANSELA_ADDR = const(0x18C) + ANSELB_ADDR = const(0x18D) + ANSELC_ADDR = const(0x18E) + + ADCON0_ADDR = const(0x9D) + ADCON1_ADDR = const(0x9E) + + IOCAP_ADDR = const(0x391) + IOCAN_ADDR = const(0x392) + + INTCON_ADDR = const(0x0B) + OPTION_REG_ADDR = const(0x95) + + _ADCON0_CHS_POSN = const(0x02) + _ADCON0_CHS_AN5 = const(0x5) # AN5 / RC1 + _ADCON0_CHS_AN6 = const(0x6) # AN6 / RC2 + _ADCON0_ADON_MASK = const(0x01) + _ADCON0_ADCS_F_OSC_64 = const(0x6) # A/D Conversion Clock + _ADCON0_GO_nDONE_MASK = const(0x02) + _ADCON1_ADCS_POSN = const(0x04) + + ADRESL_ADDR = const(0x09B) + ADRESH_ADDR = const(0x09C) + + TRISA_ADDR = const(0x08C) + TRISB_ADDR = const(0x08D) + TRISC_ADDR = const(0x08E) + + LATA_ADDR = const(0x10C) + LATB_ADDR = const(0x10D) + LATC_ADDR = const(0x10E) + + PORTA_ADDR = const(0x00C) + PORTB_ADDR = const(0x00C) + PORTC_ADDR = const(0x00E) + + WPUA_ADDR = const(0x20C) + + WAKE_REASON_ADDR = const(0x064C) + MEMORY_BANK_ADDR = const(0x0620) + + PCON_ADDR = const(0x096) + STATUS_ADDR = const(0x083) + + EXP_RTC_PERIOD = const(7000) + + USB_PID_PYSENSE = const(0xf012) + USB_PID_PYTRACK = const(0xf013) + + @staticmethod + def wake_up(): + # P9 is connected to RC1, make P9 an output + p9 = Pin("P9", mode=Pin.OUT) + # toggle rc1 to trigger wake up + p9(1) + time.sleep(0.1) + p9(0) + time.sleep(0.1) + + def __init__(self, i2c=None, sda='P22', scl='P21'): + if i2c is not None: + self.i2c = i2c + else: + self.i2c = I2C(0, mode=I2C.MASTER, pins=(sda, scl), baudrate=100000) + + self.sda = sda + self.scl = scl + self.clk_cal_factor = 1 + self.reg = bytearray(6) + + # Make sure we are inserted into the + # correct board and can talk to the PIC + retry = 0 + while True: + try: + self.read_fw_version() + break + except Exception as e: + if retry > 10: + raise Exception('Board not detected: {}'.format(e)) + print("Couldn't init Pycoproc. Maybe the PIC is still napping. Try to wake it. ({}, {})".format(retry, e)) + Pycoproc.wake_up() + # # p9 is connected to RC1, toggle it to wake PIC + # p9 = Pin("P9", mode=Pin.OUT) + # p9(1) + # time.sleep(0.1) + # p9(0) + # time.sleep(0.1) + # Pin("P9", mode=Pin.IN) + retry += 1 + + usb_pid=self.read_product_id() + if usb_pid != USB_PID_PYSENSE and usb_pid != USB_PID_PYTRACK: + raise ValueError('Not a Pysense2/Pytrack2 ({})'.format(hex(usb_pid))) + # for Pysense/Pytrack 2.0, the minimum firmware version is 15 + fw = self.read_fw_version() + if fw < 16: + raise ValueError('Firmware for Shield2 out of date', fw) + + # init the ADC for the battery measurements + self.write_byte(ANSELC_ADDR, 1 << 2) # RC2 analog input + self.write_byte(ADCON0_ADDR, (_ADCON0_CHS_AN6 << _ADCON0_CHS_POSN) | _ADCON0_ADON_MASK) # select analog channel and enable ADC + self.write_byte(ADCON1_ADDR, (_ADCON0_ADCS_F_OSC_64 << _ADCON1_ADCS_POSN)) # ADC conversion clock + + # enable the pull-up on RA3 + self.write_byte(WPUA_ADDR, (1 << 3)) + + # set RC6 and RC7 as outputs + self.write_bit(TRISC_ADDR, 6, 0) # 3V3SENSOR_A, power to Accelerometer + self.write_bit(TRISC_ADDR, 7, 0) # PWR_CTRL power to other sensors + + # enable power to the sensors and the GPS + self.gps_standby(False) # GPS, RC4 + self.sensor_power() # PWR_CTRL, RC7 + self.sd_power() # LP_CTRL, RA5 + + + def _write(self, data, wait=True): + self.i2c.writeto(I2C_SLAVE_ADDR, data) + if wait: + self._wait() + + def _read(self, size): + return self.i2c.readfrom(I2C_SLAVE_ADDR, size + 1)[1:(size + 1)] + + def _wait(self): + count = 0 + time.sleep_us(10) + while self.i2c.readfrom(I2C_SLAVE_ADDR, 1)[0] != 0xFF: + time.sleep_us(100) + count += 1 + if (count > 500): # timeout after 50ms + raise Exception('Board timeout') + + def _send_cmd(self, cmd): + self._write(bytes([cmd])) + + def read_hw_version(self): + self._send_cmd(CMD_HW_VER) + d = self._read(2) + return (d[1] << 8) + d[0] + + def read_fw_version(self): + self._send_cmd(CMD_FW_VER) + d = self._read(2) + return (d[1] << 8) + d[0] + + def read_product_id(self): + self._send_cmd(CMD_PROD_ID) + d = self._read(2) + return (d[1] << 8) + d[0] + + def read_byte(self, addr): + self._write(bytes([CMD_PEEK, addr & 0xFF, (addr >> 8) & 0xFF])) + return self._read(1)[0] + + def write_byte(self, addr, value): + self._write(bytes([CMD_POKE, addr & 0xFF, (addr >> 8) & 0xFF, value & 0xFF])) + + def magic_write_read(self, addr, _and=0xFF, _or=0, _xor=0): + self._write(bytes([CMD_MAGIC, addr & 0xFF, (addr >> 8) & 0xFF, _and & 0xFF, _or & 0xFF, _xor & 0xFF])) + return self._read(1)[0] + + def toggle_bits_in_memory(self, addr, bits): + self.magic_write_read(addr, _xor=bits) + + def mask_bits_in_memory(self, addr, mask): + self.magic_write_read(addr, _and=mask) + + def set_bits_in_memory(self, addr, bits): + self.magic_write_read(addr, _or=bits) + + def read_bit(self, address, bit): + b = self.read_byte(address) + # print("{0:08b}".format(b)) + mask = (1<= 2**(8*3): + time_s = 2**(8*3)-1 + self._write(bytes([CMD_SETUP_SLEEP, time_s & 0xFF, (time_s >> 8) & 0xFF, (time_s >> 16) & 0xFF])) + + def go_to_sleep(self, gps=True, pycom_module_off=True, accelerometer_off=True, wake_interrupt=False): + # enable or disable back-up power to the GPS receiver + self.gps_standby(gps) + + # disable the ADC + self.write_byte(ADCON0_ADDR, 0) + + # RC0, RC1, RC2, analog input + self.set_bits_in_memory(TRISC_ADDR, (1<<2) | (1<<1) | (1<<0) ) + self.set_bits_in_memory(ANSELC_ADDR, (1<<2) | (1<<1) | (1<<0) ) + + # RA4 analog input + self.set_bits_in_memory(TRISA_ADDR, (1<<4) ) + self.set_bits_in_memory(ANSELA_ADDR, (1<<4) ) + + # RB4, RB5 analog input + self.set_bits_in_memory(TRISB_ADDR, (1<<5) | (1<<4) ) + self.set_bits_in_memory(ANSELB_ADDR, (1<<5) | (1<<4) ) + + if wake_interrupt: + # print("enable wake up PIC from RC1") + self.set_bits_in_memory(OPTION_REG_ADDR, 1 << 6) # rising edge of INT pin + self.mask_bits_in_memory(ANSELC_ADDR, ~(1 << 1)) # disable analog function for RC1 pin + self.set_bits_in_memory(TRISC_ADDR, 1 << 1) # make RC1 input pin + self.mask_bits_in_memory(INTCON_ADDR, ~(1 << 1)) # clear INTF + self.set_bits_in_memory(INTCON_ADDR, 1 << 4) # enable interrupt; set INTE) + + nap_options = SD_CARD_OFF | SENSORS_OFF + if pycom_module_off: + nap_options |= FIPY_OFF + if accelerometer_off: + nap_options |= ACCELEROMETER_OFF + + # print("CMD_GO_NAP {0:08b}".format(nap_options)) + self._write(bytes([CMD_GO_NAP, nap_options]), wait=False) + + def calibrate_rtc(self): + # the 1.024 factor is because the PIC LF operates at 31 KHz + # WDT has a frequency divider to generate 1 ms + # and then there is a binary prescaler, e.g., 1, 2, 4 ... 512, 1024 ms + # hence the need for the constant + self._write(bytes([CMD_CALIBRATE]), wait=False) + self.i2c.deinit() + Pin('P21', mode=Pin.IN) + pulses = pycom.pulses_get('P21', 100) + self.i2c.init(mode=I2C.MASTER, pins=(self.sda, self.scl), baudrate=100000) + idx = 0 + for i in range(len(pulses)): + if pulses[i][1] > EXP_RTC_PERIOD: + idx = i + break + try: + period = pulses[idx][1] - pulses[(idx - 1)][1] + except: + period = 0 + if period > 0: + self.clk_cal_factor = (EXP_RTC_PERIOD / period) * (1000 / 1024) + if self.clk_cal_factor > 1.25 or self.clk_cal_factor < 0.75: + self.clk_cal_factor = 1 + time.sleep(0.5) + + def button_pressed(self): + retry = 0 + while True: + try: + button = self.read_bit(PORTA_ADDR, 3) + return not button + except Exception as e: + if retry > 10: + raise Exception('Failed to read button state: {}'.format(e)) + print("Failed to read button state, retry ... ({}, {})".format(retry, e)) + retry += 1 + + def read_battery_voltage(self): + self.set_bits_in_memory(ADCON0_ADDR, _ADCON0_GO_nDONE_MASK) + time.sleep_us(50) + while self.read_byte(ADCON0_ADDR) & _ADCON0_GO_nDONE_MASK: + time.sleep_us(100) + adc_val = (self.read_byte(ADRESH_ADDR) << 2) + (self.read_byte(ADRESL_ADDR) >> 6) + return (((adc_val * 3.3 * 280) / 1023) / 180) + 0.01 # add 10mV to compensate for the drop in the FET + + def gps_standby(self, enabled=True): + if enabled: + # make RC4 input + self.set_bits_in_memory(TRISC_ADDR, 1 << 4) + else: + # make RC4 an output + self.mask_bits_in_memory(TRISC_ADDR, ~(1 << 4)) + # drive RC4 high + self.set_bits_in_memory(PORTC_ADDR, 1 << 4) + time.sleep(0.2) + # drive RC4 low + self.mask_bits_in_memory(PORTC_ADDR, ~(1 << 4)) + time.sleep(0.2) + # drive RC4 high + self.set_bits_in_memory(PORTC_ADDR, 1 << 4) + time.sleep(0.2) + + def sensor_power(self, enabled=True): + # make RC7 an output + self.write_bit(TRISC_ADDR, 7, 0) + if enabled: + # drive RC7 high + self.write_bit(LATC_ADDR, 7, 1) + else: + # drive RC7 low + self.write_bit(LATC_ADDR, 7, 0) + + def sd_power(self, enabled=True): + # make RA5 an output + self.write_bit(TRISA_ADDR, 5, 0) + if enabled: + # drive RA5 high + self.write_bit(LATA_ADDR, 5, 1) + else: + # drive RA5 low + self.write_bit(LATA_ADDR, 5, 0) + + def reset_cmd(self): + self._send_cmd(CMD_RESET) + return diff --git a/pyscan/nfc.py b/shields/pyscan_1.py similarity index 63% rename from pyscan/nfc.py rename to shields/pyscan_1.py index d4ccc4f..9e9c263 100644 --- a/pyscan/nfc.py +++ b/shields/pyscan_1.py @@ -8,17 +8,33 @@ If authentication succeeds will attempt to read sectors from the card ''' -from pyscan import Pyscan +from pycoproc_1 import Pycoproc from MFRC630 import MFRC630 +from LIS2HH12 import LIS2HH12 +from LTR329ALS01 import LTR329ALS01 import time import pycom +#add your card UID here +VALID_CARDS = [[0x43, 0x95, 0xDD, 0xF8], + [0x43, 0x95, 0xDD, 0xF9], + [0x46, 0x5A, 0xEB, 0x7D, 0x8A, 0x08, 0x04]] + + # This is the default key for an unencrypted MiFare card CARDkey = [ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ] DECODE_CARD = False -py = Pyscan() +py = Pycoproc(Pycoproc.PYSCAN) nfc = MFRC630(py) +lt = LTR329ALS01(py) +li = LIS2HH12(py) + +pybytes_enabled = False +if 'pybytes' in globals(): + if(pybytes.isconnected()): + print('Pybytes is connected, sending signals to Pybytes') + pybytes_enabled = True RGB_BRIGHTNESS = 0x8 @@ -28,19 +44,32 @@ counter = 0 +def check_uid(uid, len): + return VALID_CARDS.count(uid[:len]) + +def send_sensor_data(name, timeout): + if(pybytes_enabled): + while(True): + pybytes.send_signal(2, lt.light()) + pybytes.send_signal(3, li.acceleration()) + time.sleep(timeout) + # Make sure heartbeat is disabled before setting RGB LED pycom.heartbeat(False) # Initialise the MFRC630 with some settings nfc.mfrc630_cmd_init() +print('Scanning for cards') while True: # Send REQA for ISO14443A card type atqa = nfc.mfrc630_iso14443a_WUPA_REQA(nfc.MFRC630_ISO14443_CMD_REQA) if (atqa != 0): # A card has been detected, read UID + print('A card has been detected, reading its UID ...') uid = bytearray(10) uid_len = nfc.mfrc630_iso14443a_select(uid) + print('UID has length {}'.format(uid_len)) if (uid_len > 0): # A valid UID has been detected, print details counter += 1 @@ -64,8 +93,18 @@ # Although this is also handled by the reset / init cycle nfc.mfrc630_MF_deauth() else: - # If we're not trying to authenticate, show green when a UID > 0 has been detected - pycom.rgbled(RGB_GREEN) + #check if card uid is listed in VALID_CARDS + if (check_uid(list(uid), uid_len)) > 0: + print('Card is listed, turn LED green') + pycom.rgbled(RGB_GREEN) + if(pybytes_enabled): + pybytes.send_signal(1, ('Card is listed', uid)) + else: + print('Card is not listed, turn LED red') + pycom.rgbled(RGB_RED) + if(pybytes_enabled): + pybytes.send_signal(1, ('Unauthorized card detected', uid)) + else: pycom.rgbled(RGB_BLUE) # We could go into power saving mode here... to be investigated diff --git a/pysense/main.py b/shields/pysense_1.py similarity index 66% rename from pysense/main.py rename to shields/pysense_1.py index 695aa79..2a4e126 100644 --- a/pysense/main.py +++ b/shields/pysense_1.py @@ -12,7 +12,7 @@ import time import pycom -from pysense import Pysense +from pycoproc_1 import Pycoproc import machine from LIS2HH12 import LIS2HH12 @@ -23,7 +23,13 @@ pycom.heartbeat(False) pycom.rgbled(0x0A0A08) # white -py = Pysense() +py = Pycoproc(Pycoproc.PYSENSE) + +pybytes_enabled = False +if 'pybytes' in globals(): + if(pybytes.isconnected()): + print('Pybytes is connected, sending signals to Pybytes') + pybytes_enabled = True mp = MPL3115A2(py,mode=ALTITUDE) # Returns height in meters. Mode may also be set to PRESSURE, returning a value in Pascals print("MPL3115A2 temperature: " + str(mp.temperature())) @@ -31,7 +37,6 @@ mpp = MPL3115A2(py,mode=PRESSURE) # Returns pressure in Pa. Mode may also be set to ALTITUDE, returning a value in meters print("Pressure: " + str(mpp.pressure())) - si = SI7006A20(py) print("Temperature: " + str(si.temperature())+ " deg C and Relative Humidity: " + str(si.humidity()) + " %RH") print("Dew point: "+ str(si.dew_point()) + " deg C") @@ -47,8 +52,20 @@ print("Roll: " + str(li.roll())) print("Pitch: " + str(li.pitch())) -print("Battery voltage: " + str(py.read_battery_voltage())) +# set your battery voltage limits here +vmax = 4.2 +vmin = 3.3 +battery_voltage = py.read_battery_voltage() +battery_percentage = (battery_voltage - vmin / (vmax - vmin))*100 +print("Battery voltage: " + str(py.read_battery_voltage()), " percentage: ", battery_percentage) +if(pybytes_enabled): + pybytes.send_signal(1, mpp.pressure()) + pybytes.send_signal(2, si.temperature()) + pybytes.send_signal(3, lt.light()) + pybytes.send_signal(4, li.acceleration()) + pybytes.send_battery_level(int(battery_percentage)) + print("Sent data to pybytes") -time.sleep(3) +time.sleep(5) py.setup_sleep(10) py.go_to_sleep() diff --git a/shields/pysense_2.py b/shields/pysense_2.py new file mode 100644 index 0000000..ff793e7 --- /dev/null +++ b/shields/pysense_2.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +# +# Copyright (c) 2020, Pycom Limited. +# +# This software is licensed under the GNU GPL version 3 or any +# later version, with permitted additional terms. For more information +# see the Pycom Licence v1.0 document supplied with this file, or +# available at https://www.pycom.io/opensource/licensing +# + +# See https://docs.pycom.io for more information regarding library specifics + +import time +import pycom +from pycoproc_2 import Pycoproc +import machine + +from LIS2HH12 import LIS2HH12 +from SI7006A20 import SI7006A20 +from LTR329ALS01 import LTR329ALS01 +from MPL3115A2 import MPL3115A2,ALTITUDE,PRESSURE + +pycom.heartbeat(False) +pycom.rgbled(0x0A0A08) # white + +py = Pycoproc() +if py.read_product_id() != Pycoproc.USB_PID_PYSENSE: + raise Exception('Not a Pysense') + +pybytes_enabled = False +if 'pybytes' in globals(): + if(pybytes.isconnected()): + print('Pybytes is connected, sending signals to Pybytes') + pybytes_enabled = True + +mp = MPL3115A2(py,mode=ALTITUDE) # Returns height in meters. Mode may also be set to PRESSURE, returning a value in Pascals +print("MPL3115A2 temperature: " + str(mp.temperature())) +print("Altitude: " + str(mp.altitude())) +mpp = MPL3115A2(py,mode=PRESSURE) # Returns pressure in Pa. Mode may also be set to ALTITUDE, returning a value in meters +print("Pressure: " + str(mpp.pressure())) + + +si = SI7006A20(py) +print("Temperature: " + str(si.temperature())+ " deg C and Relative Humidity: " + str(si.humidity()) + " %RH") +print("Dew point: "+ str(si.dew_point()) + " deg C") +t_ambient = 24.4 +print("Humidity Ambient for " + str(t_ambient) + " deg C is " + str(si.humid_ambient(t_ambient)) + "%RH") + + +lt = LTR329ALS01(py) +print("Light (channel Blue, channel Red): " + str(lt.light())," Lux: ", str(lt.lux()), "lx") + +li = LIS2HH12(py) +print("Acceleration: " + str(li.acceleration())) +print("Roll: " + str(li.roll())) +print("Pitch: " + str(li.pitch())) + +print("Battery voltage: " + str(py.read_battery_voltage())) + +# set your battery voltage limits here +vmax = 4.2 +vmin = 3.3 +battery_voltage = py.read_battery_voltage() +battery_percentage = (battery_voltage - vmin / (vmax - vmin))*100 +print("Battery voltage: " + str(py.read_battery_voltage()), " percentage: ", battery_percentage) +if(pybytes_enabled): + pybytes.send_signal(1, mpp.pressure()) + pybytes.send_signal(2, si.temperature()) + pybytes.send_signal(3, lt.light()) + pybytes.send_signal(4, li.acceleration()) + pybytes.send_battery_level(int(battery_percentage)) + print("Sent data to pybytes") diff --git a/pytrack/main.py b/shields/pytrack_1.py similarity index 71% rename from pytrack/main.py rename to shields/pytrack_1.py index ddb44af..cab1cb9 100644 --- a/pytrack/main.py +++ b/shields/pytrack_1.py @@ -18,7 +18,7 @@ from machine import RTC from machine import SD from L76GNSS import L76GNSS -from pytrack import Pytrack +from pycoproc_1 import Pycoproc time.sleep(2) gc.enable() @@ -31,9 +31,15 @@ utime.timezone(7200) print('Adjusted from UTC to EST timezone', utime.localtime(), '\n') -py = Pytrack() +py = Pycoproc(Pycoproc.PYTRACK) l76 = L76GNSS(py, timeout=30) +pybytes_enabled = False +if 'pybytes' in globals(): + if(pybytes.isconnected()): + print('Pybytes is connected, sending signals to Pybytes') + pybytes_enabled = True + # sd = SD() # os.mount(sd, '/sd') # f = open('/sd/gps-record.txt', 'w') @@ -42,3 +48,13 @@ coord = l76.coordinates() #f.write("{} - {}\n".format(coord, rtc.now())) print("{} - {} - {}".format(coord, rtc.now(), gc.mem_free())) + if(pybytes_enabled): + pybytes.send_signal(1, coord) + time.sleep(10) + +""" +# sleep procedure +time.sleep(3) +py.setup_sleep(10) +py.go_to_sleep() +""" diff --git a/pybytes/pytrack/main.py b/shields/pytrack_2.py similarity index 50% rename from pybytes/pytrack/main.py rename to shields/pytrack_2.py index 33361d6..52fe547 100644 --- a/pybytes/pytrack/main.py +++ b/shields/pytrack_2.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright (c) 2019, Pycom Limited. +# Copyright (c) 2020, Pycom Limited. # # This software is licensed under the GNU GPL version 3 or any # later version, with permitted additional terms. For more information @@ -14,15 +14,15 @@ import os import time import utime +import gc +import pycom from machine import RTC from machine import SD -from machine import Timer from L76GNSS import L76GNSS -from pytrack import Pytrack -from LIS2HH12 import LIS2HH12 -# setup as a station +from pycoproc_2 import Pycoproc -import gc +pycom.heartbeat(False) +pycom.rgbled(0x0A0A08) # white time.sleep(2) gc.enable() @@ -34,18 +34,35 @@ print('\nRTC Set from NTP to UTC:', rtc.now()) utime.timezone(7200) print('Adjusted from UTC to EST timezone', utime.localtime(), '\n') -py = Pytrack() -l76 = L76GNSS(py, timeout=30) -chrono = Timer.Chrono() -chrono.start() -li = LIS2HH12(py) -#sd = SD() -#os.mount(sd, '/sd') -#f = open('/sd/gps-record.txt', 'w') -while (pybytes): + +py = Pycoproc() +if py.read_product_id() != Pycoproc.USB_PID_PYTRACK: + raise Exception('Not a Pytrack') + +time.sleep(1) +l76 = L76GNSS(py, timeout=30, buffer=512) + +pybytes_enabled = False +if 'pybytes' in globals(): + if(pybytes.isconnected()): + print('Pybytes is connected, sending signals to Pybytes') + pybytes_enabled = True + +# sd = SD() +# os.mount(sd, '/sd') +# f = open('/sd/gps-record.txt', 'w') + +while (True): coord = l76.coordinates() #f.write("{} - {}\n".format(coord, rtc.now())) - print('Sending data') - pybytes.send_virtual_pin_value(True, 1, coord) - pybytes.send_virtual_pin_value(True, 2, li.acceleration()) + print("{} - {} - {}".format(coord, rtc.now(), gc.mem_free())) + if(pybytes_enabled): + pybytes.send_signal(1, coord) time.sleep(10) + +""" +# sleep procedure +time.sleep(3) +py.setup_sleep(10) +py.go_to_sleep() +""" diff --git a/shields/shield_2.py b/shields/shield_2.py new file mode 100644 index 0000000..8fb74e0 --- /dev/null +++ b/shields/shield_2.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python +# +# Copyright (c) 2020, Pycom Limited. +# +# This software is licensed under the GNU GPL version 3 or any +# later version, with permitted additional terms. For more information +# see the Pycom Licence v1.0 document supplied with this file, or +# available at https://www.pycom.io/opensource/licensing +# + +# This script support Pysense 2 and Pytrack 2 +# It demonstrates two examples: +# * 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 +# * 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 + +# See https://docs.pycom.io for more information regarding library specifics + +import time +import pycom +import struct +from machine import Pin +from pycoproc_2 import Pycoproc +import machine +from LIS2HH12 import LIS2HH12 + +def accelerometer(): + 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 ) + +def activity_int_handler(pin_o): + if pin_o(): + print('[Activity]') + pycom.rgbled(0x00000A) # blue + else: + print('[Inactivity]') + pycom.rgbled(0x0A0A00) # yellow + +def activity_int_handler_none(pin_o): + pass + +def blink(color=0x0a0a0a, ct=5, on_ms=100, off_ms=100 ): + while ct >= 0 : + ct -= 1 + pycom.rgbled(color) + time.sleep_ms(on_ms) + pycom.rgbled(0x000000) + time.sleep_ms(off_ms) + +def wait(color=0x0a0a0a, hold_timeout_ms=3000): + print(" - tap MCLR button to go to ultra low power mode (everything off)") + print(" - hold MCLR button down for", round(hold_timeout_ms/1000,1), "sec to go to low power mode and wake from accelerometer") + print("wait for button ...") + ct = 0 + pressed_time_ms = 0 + dot = '.' + while True: + if pycoproc.button_pressed(): + if pressed_time_ms == 0: + # the button just started to be pressed + pressed_time_ms = time.ticks_ms() + print("button pressed") + pycom.rgbled(color) + dot = '*' + else: + # the button is still being held down + if time.ticks_ms() - pressed_time_ms > hold_timeout_ms: + pycom.rgbled(0) + dot = '_' + else: + if pressed_time_ms != 0: + # the button was released + print("button released") + if time.ticks_ms() - pressed_time_ms > hold_timeout_ms: + return True + else: + return False + time.sleep(0.1) + ct += 1 + if ct % 10 == 0: + print(dot, end='') + +def pretty_reset_cause(): + mrc = machine.reset_cause() + print('reset_cause', mrc, end=' ') + if mrc == machine.PWRON_RESET: + print("PWRON_RESET") + # plug in + # press reset button on module + # reset button on JTAG board + # core dump + elif mrc == machine.HARD_RESET: + print("HARD_RESET") + elif mrc == machine.WDT_RESET: + print("WDT_RESET") + # machine.reset() + elif mrc == machine.DEEPSLEEP_RESET: + print("DEEPSLEEP_RESET") + # machine.deepsleep() + elif mrc == machine.SOFT_RESET: + print("SOFT_RESET") + # Ctrl-D + elif mrc == machine.BROWN_OUT_RESET: + print("BROWN_OUT_RESET") + +def pretty_wake_reason(): + mwr = machine.wake_reason() + print("wake_reason", mwr, end=' ') + if mwr[0] == machine.PWRON_WAKE: + print("PWRON_WAKE") + # reset button + elif mwr[0] == machine.PIN_WAKE: + print("PIN_WAKE") + elif mwr[0] == machine.RTC_WAKE: + print("RTC_WAKE") + # from deepsleep + elif mwr[0] == machine.ULP_WAKE: + print("ULP_WAKE") + + +############################################################### +sleep_time_s = 300 # 5 min +pycom.heartbeat(False) +pycom.rgbled(0x0a0a0a) # white +import binascii +import machine +print(os.uname().sysname.lower() + '-' + binascii.hexlify(machine.unique_id()).decode("utf-8")[-4:], "pysense2") + +pretty_wake_reason() +pretty_reset_cause() +print("pycoproc init") +pycoproc = Pycoproc() +print("battery {:.2f} V".format(pycoproc.read_battery_voltage())) + +# init accelerometer +accelerometer_sensor = LIS2HH12() +# read accelerometer sensor values +accelerometer() +print("enable accelerometer interrupt") + +# enable_activity_interrupt( [mG], [ms], callback) +# accelerometer_sensor.enable_activity_interrupt(8000, 200, activity_int_handler) # low sensitivty +# accelerometer_sensor.enable_activity_interrupt(2000, 200, activity_int_handler) # medium sensitivity +accelerometer_sensor.enable_activity_interrupt( 100, 200, activity_int_handler) # high sensitivity +# accelerometer_sensor.enable_activity_interrupt(63, 160, activity_int_handler) # ultra sensitivty + +if wait(0x0A000A): # purple + print("button was held") + blink(0x000a00) # green + print("enable pycom module to wake up from accelerometer interrupt") + wake_pins = [Pin('P13', mode=Pin.IN, pull=Pin.PULL_DOWN)] + machine.pin_sleep_wakeup(wake_pins, machine.WAKEUP_ANY_HIGH, True) + + print("put pycoproc to sleep and pycom module to deepsleep for", round(sleep_time_s/60,1), "minutes") + pycoproc.setup_sleep(sleep_time_s) + pycoproc.go_to_sleep(pycom_module_off=False, accelerometer_off=False, wake_interrupt=True) + machine.deepsleep(sleep_time_s * 1000) +else: + print("button was tapped") + blink(0x100600) # orange + print("put pycoproc to sleep and turn pycom module off for", round(sleep_time_s/60,1), "minutes") + pycoproc.setup_sleep(sleep_time_s) + pycoproc.go_to_sleep() + +print("we never reach here!")