├── .gitignore ├── examples ├── README.md ├── adc │ ├── boot.py │ └── main.py ├── aws │ ├── boot.py │ ├── demo.py │ ├── main.py │ └── mqttclient.py ├── bluetooth │ ├── boot.py │ └── main.py ├── https │ ├── boot.py │ └── main.py ├── i2c │ ├── bh1750fvi.py │ ├── boot.py │ └── main.py ├── lopy-lopy │ ├── lopy-A │ │ ├── boot.py │ │ └── main.py │ └── lopy-B │ │ ├── boot.py │ │ └── main.py ├── loraNanoGateway │ ├── gateway │ │ ├── boot.py │ │ └── main.py │ └── node │ │ ├── boot.py │ │ └── main.py ├── loraabp │ ├── boot.py │ └── main.py ├── loramac │ ├── boot.py │ └── main.py ├── lorawan-nano-gateway │ ├── KPN_abp_node_data_decrypt │ │ ├── config.py │ │ ├── db_config.py │ │ ├── db_store.py │ │ ├── drivers │ │ │ ├── mysql-connector-python-2.1.6-py2.7-win32.msi │ │ │ └── mysql-connector-python-2.1.6-py2.7-winx64.msi │ │ ├── get_loggly_data.py │ │ ├── loramac_decrypt.py │ │ ├── readme.md │ │ └── runme.py │ ├── TTN-abp-puckjs │ │ ├── Send_BT_to_LoPy.js │ │ ├── boot.py │ │ ├── lib │ │ │ └── config.py │ │ ├── main.py │ │ ├── payloadfunction.js │ │ └── readme.md │ ├── TTN-abp_node │ │ ├── boot.py │ │ ├── lib │ │ │ └── config.py │ │ └── main.py │ ├── TTN-abp_node_GPS │ │ ├── boot.py │ │ ├── lib │ │ │ ├── config.py │ │ │ ├── micropyGPS.py │ │ │ ├── readme.md │ │ │ └── tools.py │ │ ├── main.py │ │ └── payload_decoder_ttn.txt │ ├── TTN-lorawan-nano-gateway │ │ ├── inc │ │ │ ├── config.py │ │ │ └── nanogateway.py │ │ └── main.py │ ├── TTN-otaa_node-cpg-sensor │ │ ├── Circuit Playground │ │ │ └── CPG_Serial_hardware_sensors │ │ │ │ └── CPG_Serial_hardware_sensors.ino │ │ └── LoPy-flash │ │ │ ├── boot.py │ │ │ ├── lib │ │ │ └── config.py │ │ │ └── main.py │ └── TTN-otaa_node │ │ ├── boot.py │ │ ├── lib │ │ └── config.py │ │ └── main.py ├── mqtt │ ├── boot.py │ ├── main.py │ └── mqtt.py ├── onewire │ ├── boot.py │ ├── main.py │ └── onewire.py ├── onlineLog │ ├── __init__.py │ ├── boot.py │ ├── main.py │ └── onewire.py ├── sigfoxUplink │ ├── boot.py │ └── main.py └── threading │ ├── boot.py │ └── main.py └── lib └── onewire └── onewire.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.e6t 2 | *.e4q 3 | *.e4p 4 | *.1 -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | This folder contains several samples that demonstrate various Pycom board functions. The samples are currently provided with the boot.py file to copy directly on the board. 3 | 4 | ## Installation 5 | 6 | This section explains the individual steps to run example application using the Pymakr. 7 | Steps: 8 | - Open Pymakr 9 | - In the menu, go to Project > New 10 | - Fill project name, select example folder for Project directory and select project type "Python project" 11 | - On popup add existing files to the project. 12 | -------------------------------------------------------------------------------- /examples/adc/boot.py: -------------------------------------------------------------------------------- 1 | from machine import UART 2 | import machine 3 | import os 4 | 5 | uart = UART(0, baudrate=115200) 6 | os.dupterm(uart) 7 | 8 | machine.main('main.py') 9 | -------------------------------------------------------------------------------- /examples/adc/main.py: -------------------------------------------------------------------------------- 1 | from machine import ADC 2 | import time 3 | 4 | adc = ADC(0) 5 | adc_c = adc.channel(pin='P13') 6 | 7 | while True: 8 | value = adc_c.value() 9 | print("ADC value:" + value) 10 | time.sleep(1) 11 | -------------------------------------------------------------------------------- /examples/aws/boot.py: -------------------------------------------------------------------------------- 1 | from machine import UART 2 | import machine 3 | import os 4 | 5 | uart = UART(0, baudrate=115200) 6 | os.dupterm(uart) 7 | 8 | machine.main('main.py') 9 | -------------------------------------------------------------------------------- /examples/aws/demo.py: -------------------------------------------------------------------------------- 1 | import time 2 | from mqttclient import MQTTClient 3 | 4 | DISCONNECTED = 0 5 | CONNECTING = 1 6 | CONNECTED = 2 7 | DEVICE_ID = "12345" 8 | HOST = "random_id.us-west-2.amazonaws.com" 9 | TOPIC_DOWNLOAD = "Download" 10 | TOPIC_UPLOAD = "Upload" 11 | 12 | 13 | state = DISCONNECTED 14 | connection = None 15 | 16 | def _recv_msg_callback(topic, msg): 17 | print("Received: {} from Topic: {}".format(msg, topic)) 18 | 19 | def _send_msg(msg): 20 | global connection 21 | connection.publish(TOPIC_UPLOAD, msg) 22 | 23 | def run(): 24 | global state 25 | global connection 26 | 27 | while True: 28 | # Wait for connection 29 | while state != CONNECTED: 30 | try: 31 | state = CONNECTING 32 | connection = MQTTClient(DEVICE_ID, server=HOST, port=8883) 33 | connection.connect(ssl=True, certfile='/flash/cert/certificate.crt', keyfile='/flash/cert/privateKey.key', ca_certs='/flash/cert/root-CA.cer') 34 | state = CONNECTED 35 | except: 36 | print('Error connecting to the server') 37 | time.sleep(0.5) 38 | continue 39 | 40 | print('Connected!') 41 | 42 | # Subscribe for messages 43 | connection.set_callback(_recv_msg_callback) 44 | connection.subscribe(TOPIC_DOWNLOAD) 45 | 46 | while state == CONNECTED: 47 | connection.check_msg() 48 | msg = '{"Name":"Pycom", "Data":"Test"}' 49 | print('Sending: ' + msg) 50 | _send_msg(msg) 51 | time.sleep(2.0) 52 | 53 | 54 | run() 55 | -------------------------------------------------------------------------------- /examples/aws/main.py: -------------------------------------------------------------------------------- 1 | import demo 2 | demo.run() -------------------------------------------------------------------------------- /examples/aws/mqttclient.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import struct 3 | import select 4 | from binascii import hexlify 5 | 6 | class MQTTException(Exception): 7 | pass 8 | 9 | class MQTTClient: 10 | 11 | def __init__(self, client_id, server, port=1883, user=None, password=None): 12 | self.client_id = client_id.encode('utf8') 13 | self.sock = None 14 | self.addr = socket.getaddrinfo(server, port)[0][-1] 15 | self.pid = 0 16 | self.cb = None 17 | self.poll = select.poll() 18 | self.__will_message = None 19 | if user: 20 | self.__user = user.encode('utf8') 21 | else: 22 | self.__user = None 23 | self.__password = password 24 | 25 | def __encode_varlen_length(self, length): 26 | i = 0 27 | buff = bytearray() 28 | while 1: 29 | buff.append(length % 128) 30 | length = length // 128 31 | if length > 0: 32 | buff[i] = buff[i] | 0x80 33 | i += 1 34 | else: 35 | break 36 | 37 | return buff 38 | 39 | def __encode_16(self, x): 40 | return struct.pack("!H", x) 41 | 42 | def __pascal_string(self, s): 43 | return struct.pack("!H", len(s)) + s 44 | 45 | def __recv_varlen_length(self): 46 | m = 1 47 | val = 0 48 | while 1: 49 | b = self.sock.recv(1)[0] 50 | val += (b & 0x7F) * m 51 | m *= 128 52 | if m > 2097152: # 128 * 128 * 128 53 | raise MQTTException() 54 | if (b & 0x80) == 0: 55 | break 56 | return val 57 | 58 | def set_callback(self, f): 59 | self.cb = f 60 | 61 | def set_will(self, will_topic, will_message, will_qos=0, will_retain=0): 62 | if will_topic: 63 | self.__will_topic = will_topic.encode('utf8') 64 | self.__will_message = will_message 65 | self.__will_qos = will_qos 66 | self.__will_retain = will_retain 67 | 68 | def connect(self, clean_session=True, ssl=False, certfile=None, keyfile=None, ca_certs=None): 69 | try: 70 | self.poll.unregister(self.sock) 71 | except: 72 | pass 73 | self.sock = socket.socket() 74 | 75 | if ssl: 76 | import ssl 77 | self.sock = ssl.wrap_socket(self.sock, certfile=certfile, keyfile=keyfile, ca_certs=ca_certs, cert_reqs=ssl.CERT_REQUIRED) 78 | 79 | self.sock.connect(self.addr) 80 | self.poll.register(self.sock, select.POLLIN) 81 | 82 | pkt_len = (12 + len(self.client_id) + # 10 + 2 + len(client_id) 83 | (2 + len(self.__user) if self.__user else 0) + 84 | (2 + len(self.__password) if self.__password else 0)) 85 | 86 | flags = (0x80 if self.__user else 0x00) | (0x40 if self.__password else 0x00) | (0x02 if clean_session else 0x00) 87 | 88 | if self.__will_message: 89 | flags |= (self.__will_retain << 3 | self.__will_qos << 1 | 1) << 2 90 | pkt_len += 4 + len(self.__will_topic) + len(self.__will_message) 91 | 92 | pkt = bytearray([0x10]) # connect 93 | pkt.extend(self.__encode_varlen_length(pkt_len)) # len of the remaining 94 | pkt.extend(b'\x00\x04MQTT\x04') # len of "MQTT" (16 bits), protocol name, and protocol version 95 | pkt.append(flags) 96 | pkt.extend(b'\x00\x00') # disable keepalive 97 | pkt.extend(self.__pascal_string(self.client_id)) 98 | if self.__will_message: 99 | pkt.extend(self.__pascal_string(self.__will_topic)) 100 | pkt.extend(self.__pascal_string(self.__will_message)) 101 | if self.__user: 102 | pkt.extend(self.__pascal_string(self.__user)) 103 | if self.__password: 104 | pkt.extend(self.__pascal_string(self.__password)) 105 | 106 | self.sock.send(pkt) 107 | resp = self.sock.recv(4) 108 | assert resp[0] == 0x20 and resp[1] == 0x02 109 | if resp[3] != 0: 110 | raise MQTTException(resp[3]) 111 | return resp[2] & 1 112 | 113 | def disconnect(self): 114 | self.sock.send(b"\xe0\0") 115 | self.sock.close() 116 | 117 | def ping(self): 118 | self.sock.send(b"\xc0\0") 119 | 120 | def publish(self, topic, msg, retain=False, qos=0, dup=0): 121 | topic = topic.encode('utf8') 122 | hdr = 0x30 | (dup << 3) | (qos << 1) | retain 123 | pkt_len = (2 + len(topic) + 124 | (2 if qos else 0) + 125 | (len(msg))) 126 | 127 | pkt = bytearray() 128 | pkt.append(hdr) 129 | pkt.extend(self.__encode_varlen_length(pkt_len)) # len of the remaining 130 | pkt.extend(self.__pascal_string(topic)) 131 | if qos: 132 | self.pid += 1 #todo: I don't think this is the way to deal with the packet id 133 | pkt.extend(self.__encode_16(self.pid)) 134 | 135 | self.sock.send(pkt) 136 | self.sock.send(msg) 137 | 138 | #todo: check next part of the code 139 | if qos == 1: 140 | while 1: 141 | rcv_pid = self.recv_pubconf(0) 142 | if pid == rcv_pid: 143 | return 144 | elif qos == 2: 145 | assert 0 146 | def recv_pubconf(self, t): 147 | headers = [0x40, 0x50, 0x62, 0x70] 148 | header = headers[t] 149 | while 1: 150 | op = self.wait_msg() 151 | if op == header: 152 | sz = self.sock.recv(1) 153 | assert sz == b"\x02" 154 | return 155 | 156 | def subscribe(self, topic, qos=0): 157 | assert self.cb is not None, "Subscribe callback is not set" 158 | 159 | topic = topic.encode('utf8') 160 | pkt_len = 2 + 2 + len(topic) + 1 # packet identifier + len of topic (16 bits) + topic len + QOS 161 | 162 | self.pid += 1 163 | pkt = bytearray([0x82]) 164 | pkt.extend(self.__encode_varlen_length(pkt_len)) # len of the remaining 165 | pkt.extend(self.__encode_16(self.pid)) 166 | pkt.extend(self.__pascal_string(topic)) 167 | pkt.append(qos) 168 | 169 | self.sock.send(pkt) 170 | resp = self.sock.recv(5) 171 | #print(resp) 172 | assert resp[0] == 0x90 173 | assert resp[2] == pkt[2] and resp[3] == pkt[3] 174 | if resp[4] == 0x80: 175 | raise MQTTException(resp[4]) 176 | 177 | # Wait for a single incoming MQTT message and process it. 178 | # Subscribed messages are delivered to a callback previously 179 | # set by .set_callback() method. Other (internal) MQTT 180 | # messages processed internally. 181 | def wait_msg(self): 182 | res = self.sock.recv(1) 183 | self.sock.setblocking(True) 184 | if res is None or res == b"": 185 | return None 186 | #if res == b"": 187 | # raise OSError(-1) 188 | if res == b"\xd0": # PINGRESP 189 | sz = self.sock.recv(1)[0] 190 | assert sz == 0 191 | return None 192 | op = res[0] 193 | if op & 0xf0 != 0x30: 194 | return op 195 | sz = self.__recv_varlen_length() 196 | topic_len = self.sock.recv(2) 197 | topic_len = (topic_len[0] << 8) | topic_len[1] 198 | topic = self.sock.recv(topic_len) 199 | sz -= topic_len + 2 200 | if op & 6: 201 | pid = self.sock.recv(2) 202 | pid = pid[0] << 8 | pid[1] 203 | sz -= 2 204 | msg = self.sock.recv(sz) 205 | self.cb(topic, msg) 206 | if op & 6 == 2: 207 | pkt = bytearray(b"\x40\x02\0\0") 208 | struct.pack_into("!H", pkt, 2, pid) 209 | self.sock.send(pkt) 210 | elif op & 6 == 4: 211 | assert 0 212 | 213 | # Checks whether a pending message from server is available. 214 | # If not, returns immediately with None. Otherwise, does 215 | # the same processing as wait_msg. 216 | def check_msg(self): 217 | self.sock.setblocking(False) 218 | return self.wait_msg() 219 | -------------------------------------------------------------------------------- /examples/bluetooth/boot.py: -------------------------------------------------------------------------------- 1 | from machine import UART 2 | import machine 3 | import os 4 | 5 | uart = UART(0, baudrate=115200) 6 | os.dupterm(uart) 7 | 8 | machine.main('main.py') 9 | -------------------------------------------------------------------------------- /examples/bluetooth/main.py: -------------------------------------------------------------------------------- 1 | from network import Bluetooth 2 | import time 3 | bt = Bluetooth() 4 | bt.start_scan(-1) 5 | 6 | while True: 7 | adv = bt.get_adv() 8 | if adv 9 | # try to get the complete name 10 | print(bluetooth.resolve_adv_data(adv.data, Bluetooth.ADV_NAME_CMPL)) 11 | 12 | # try to get the manufacturer data (Apple's iBeacon data is sent here) 13 | print(binascii.hexlify(bluetooth.resolve_adv_data(adv.data, Bluetooth.ADV_MANUFACTURER_DATA))) 14 | 15 | 16 | if bt.resolve_adv_data(adv.data, Bluetooth.ADV_NAME_CMPL) == 'Heart Rate': 17 | conn = bt.connect(adv.mac) 18 | services = conn.services() 19 | for service in services: 20 | time.sleep(0.050) 21 | if type(service.uuid()) == bytes: 22 | print('Reading chars from service = {}'.format(service.uuid())) 23 | else: 24 | print('Reading chars from service = %x' % service.uuid()) 25 | chars = service.characteristics() 26 | for char in chars: 27 | if (char.properties() & Bluetooth.PROP_READ): 28 | print('char {} value = {}'.format(char.uuid(), char.read())) 29 | conn.disconnect() 30 | break 31 | else: 32 | time.sleep(0.050) -------------------------------------------------------------------------------- /examples/https/boot.py: -------------------------------------------------------------------------------- 1 | from machine import UART 2 | import machine 3 | import os 4 | 5 | uart = UART(0, baudrate=115200) 6 | os.dupterm(uart) 7 | 8 | machine.main('main.py') 9 | -------------------------------------------------------------------------------- /examples/https/main.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import ssl 3 | 4 | # Connect without a certificate 5 | s = socket() 6 | ss = ssl.wrap_socket(s) 7 | ss.connect(socket.getaddrinfo('www.google.com', 443)[0][-1]) 8 | 9 | # Connect with a certificate 10 | s = socket.socket() 11 | ss = ssl.wrap_socket(s, cert_reqs=ssl.CERT_REQUIRED, ca_certs='/flash/cert/ca.pem') 12 | ss.connect(socket.getaddrinfo('cloud.blynk.cc', 8441)[0][-1]) -------------------------------------------------------------------------------- /examples/i2c/bh1750fvi.py: -------------------------------------------------------------------------------- 1 | # Simple driver for the BH1750FVI digital light sensor 2 | 3 | class BH1750FVI: 4 | MEASUREMENT_TIME = const(120) 5 | 6 | def __init__(self, i2c, addr=0x23, period=150): 7 | self.i2c = i2c 8 | self.period = period 9 | self.addr = addr 10 | self.time = 0 11 | self.value = 0 12 | self.i2c.writeto(addr, bytes([0x10])) # start continuos 1 Lux readings every 120ms 13 | 14 | def read(self): 15 | self.time += self.period 16 | if self.time >= MEASUREMENT_TIME: 17 | self.time = 0 18 | data = self.i2c.readfrom(self.addr, 2) 19 | self.value = (((data[0] << 8) + data[1]) * 1200) // 1000 20 | return self.value -------------------------------------------------------------------------------- /examples/i2c/boot.py: -------------------------------------------------------------------------------- 1 | from machine import UART 2 | import machine 3 | import os 4 | 5 | uart = UART(0, baudrate=115200) 6 | os.dupterm(uart) 7 | 8 | machine.main('main.py') 9 | -------------------------------------------------------------------------------- /examples/i2c/main.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import time 3 | import pycom 4 | import struct 5 | from network import LoRa 6 | from machine import I2C 7 | import bh1750fvi 8 | 9 | LORA_PKG_FORMAT = "!BH" 10 | LORA_CONFIRM_FORMAT = "!BB" 11 | 12 | DEVICE_ID = 1 13 | 14 | pycom.heartbeat(False) 15 | 16 | lora = LoRa(mode=LoRa.LORA, tx_iq=True, frequency = 863000000) 17 | lora_sock = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 18 | lora_sock.setblocking(False) 19 | 20 | i2c = I2C(0, I2C.MASTER, baudrate=100000) 21 | light_sensor = bh1750fvi.BH1750FVI(i2c, addr=i2c.scan()[0]) 22 | 23 | while(True): 24 | msg = struct.pack(LORA_PKG_FORMAT, DEVICE_ID, light_sensor.read()) 25 | lora_sock.send(msg) 26 | 27 | pycom.rgbled(0x150000) 28 | 29 | wait = 5 30 | while (wait > 0): 31 | wait = wait - 0.1 32 | time.sleep(0.1) 33 | recv_data = lora_sock.recv(64) 34 | 35 | if (len (recv_data) >= 2): 36 | status, device_id = struct.unpack(LORA_CONFIRM_FORMAT, recv_data) 37 | 38 | if (device_id == DEVICE_ID and status == 200): 39 | pycom.rgbled(0x001500) 40 | wait = 0 41 | 42 | time.sleep(1) -------------------------------------------------------------------------------- /examples/lopy-lopy/lopy-A/boot.py: -------------------------------------------------------------------------------- 1 | from machine import UART 2 | import machine 3 | import os 4 | 5 | uart = UART(0, baudrate=115200) 6 | os.dupterm(uart) 7 | 8 | machine.main('main.py') 9 | -------------------------------------------------------------------------------- /examples/lopy-lopy/lopy-A/main.py: -------------------------------------------------------------------------------- 1 | from network import LoRa 2 | import socket 3 | import time 4 | 5 | lora = LoRa(mode=LoRa.LORA, frequency=863000000) 6 | s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 7 | s.setblocking(False) 8 | 9 | while True: 10 | if s.recv(64) == b'Ping': 11 | s.send('Pong') 12 | time.sleep(5) 13 | -------------------------------------------------------------------------------- /examples/lopy-lopy/lopy-B/boot.py: -------------------------------------------------------------------------------- 1 | from machine import UART 2 | import machine 3 | import os 4 | 5 | uart = UART(0, baudrate=115200) 6 | os.dupterm(uart) 7 | 8 | machine.main('main.py') 9 | -------------------------------------------------------------------------------- /examples/lopy-lopy/lopy-B/main.py: -------------------------------------------------------------------------------- 1 | from network import LoRa 2 | import socket 3 | import time 4 | 5 | lora = LoRa(mode=LoRa.LORA, frequency=863000000) 6 | s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 7 | s.setblocking(False) 8 | 9 | while True: 10 | s.send('Ping') 11 | time.sleep(5) 12 | -------------------------------------------------------------------------------- /examples/loraNanoGateway/gateway/boot.py: -------------------------------------------------------------------------------- 1 | from machine import UART 2 | import machine 3 | import os 4 | 5 | uart = UART(0, baudrate=115200) 6 | os.dupterm(uart) 7 | 8 | machine.main('main.py') 9 | -------------------------------------------------------------------------------- /examples/loraNanoGateway/gateway/main.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import struct 3 | from network import LoRa 4 | 5 | # A basic package header, B: 1 byte for the deviceId, B: 1 byte for the pkg size, %ds: Formated string for string 6 | _LORA_PKG_FORMAT = "!BB%ds" 7 | # A basic ack package, B: 1 byte for the deviceId, B: 1 bytes for the pkg size, B: 1 byte for the Ok (200) or error messages 8 | _LORA_PKG_ACK_FORMAT = "BBB" 9 | 10 | # Open a LoRa Socket, use rx_iq to avoid listening to our own messages 11 | lora = LoRa(mode=LoRa.LORA, rx_iq=True) 12 | lora_sock = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 13 | lora_sock.setblocking(False) 14 | 15 | while (True): 16 | recv_pkg = lora_sock.recv(512) 17 | if (len(recv_pkg) > 2): 18 | recv_pkg_len = recv_pkg[1] 19 | 20 | device_id, pkg_len, msg = struct.unpack(_LORA_PKG_FORMAT % recv_pkg_len, recv_pkg) 21 | 22 | # If the uart = machine.UART(0, 115200) and os.dupterm(uart) are set in the boot.py this print should appear in the serial port 23 | print('Device: %d - Pkg: %s' % (device_id, msg)) 24 | 25 | ack_pkg = struct.pack(_LORA_PKG_ACK_FORMAT, device_id, 1, 200) 26 | lora_sock.send(ack_pkg) 27 | -------------------------------------------------------------------------------- /examples/loraNanoGateway/node/boot.py: -------------------------------------------------------------------------------- 1 | from machine import UART 2 | import machine 3 | import os 4 | 5 | uart = UART(0, baudrate=115200) 6 | os.dupterm(uart) 7 | 8 | machine.main('main.py') 9 | -------------------------------------------------------------------------------- /examples/loraNanoGateway/node/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import socket 3 | import time 4 | import struct 5 | from network import LoRa 6 | 7 | # A basic package header, B: 1 byte for the deviceId, B: 1 bytes for the pkg size 8 | _LORA_PKG_FORMAT = "BB%ds" 9 | _LORA_PKG_ACK_FORMAT = "BBB" 10 | DEVICE_ID = 0x01 11 | 12 | 13 | # Open a Lora Socket, use tx_iq to avoid listening to our own messages 14 | lora = LoRa(mode=LoRa.LORA, tx_iq=True) 15 | lora_sock = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 16 | lora_sock.setblocking(False) 17 | 18 | while(True): 19 | # Package send containing a simple string 20 | msg = "Device 1 Here" 21 | pkg = struct.pack(_LORA_PKG_FORMAT % len(msg), DEVICE_ID, len(msg), msg) 22 | lora_sock.send(pkg) 23 | 24 | # Wait for the response from the gateway. NOTE: For this demo the device does an infinite loop for while waiting the response. Introduce a max_time_waiting for you application 25 | waiting_ack = True 26 | while(waiting_ack): 27 | recv_ack = lora_sock.recv(256) 28 | 29 | if (len(recv_ack) > 0): 30 | device_id, pkg_len, ack = struct.unpack(_LORA_PKG_ACK_FORMAT, recv_ack) 31 | if (device_id == DEVICE_ID): 32 | if (ack == 200): 33 | waiting_ack = False 34 | # If the uart = machine.UART(0, 115200) and os.dupterm(uart) are set in the boot.py this print should appear in the serial port 35 | print("ACK") 36 | else: 37 | waiting_ack = False 38 | # If the uart = machine.UART(0, 115200) and os.dupterm(uart) are set in the boot.py this print should appear in the serial port 39 | print("Message Failed") 40 | 41 | time.sleep(5) 42 | 43 | -------------------------------------------------------------------------------- /examples/loraabp/boot.py: -------------------------------------------------------------------------------- 1 | from machine import UART 2 | import machine 3 | import os 4 | 5 | uart = UART(0, baudrate=115200) 6 | os.dupterm(uart) 7 | 8 | machine.main('main.py') 9 | -------------------------------------------------------------------------------- /examples/loraabp/main.py: -------------------------------------------------------------------------------- 1 | from network import LoRa 2 | import socket 3 | import binascii 4 | import struct 5 | 6 | # Initialize LoRa in LORAWAN mode. 7 | lora = LoRa(mode=LoRa.LORAWAN) 8 | 9 | # create an ABP authentication params 10 | dev_addr = struct.unpack(">l", binascii.unhexlify('00 00 00 05'.replace(' ','')))[0] 11 | nwk_swkey = binascii.unhexlify('2B 7E 15 16 28 AE D2 A6 AB F7 15 88 09 CF 4F 3C'.replace(' ','')) 12 | app_swkey = binascii.unhexlify('2B 7E 15 16 28 AE D2 A6 AB F7 15 88 09 CF 4F 3C'.replace(' ','')) 13 | 14 | # join a network using ABP (Activation By Personalization) 15 | lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey)) 16 | 17 | # create a LoRa socket 18 | s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 19 | 20 | # set the LoRaWAN data rate 21 | s.setsockopt(socket.SOL_LORA, socket.SO_DR, 5) 22 | 23 | # make the socket non-blocking 24 | s.setblocking(False) 25 | 26 | # send some data 27 | s.send(bytes([0x01, 0x02, 0x03])) 28 | 29 | # get any data received... 30 | data = s.recv(64) 31 | print(data) 32 | -------------------------------------------------------------------------------- /examples/loramac/boot.py: -------------------------------------------------------------------------------- 1 | from machine import UART 2 | import machine 3 | import os 4 | 5 | uart = UART(0, baudrate=115200) 6 | os.dupterm(uart) 7 | 8 | machine.main('main.py') 9 | -------------------------------------------------------------------------------- /examples/loramac/main.py: -------------------------------------------------------------------------------- 1 | from network import LoRa 2 | import socket 3 | 4 | # Initialize LoRa in LORA mode. 5 | # More params can be given, like frequency, tx power and spreading factor. 6 | lora = LoRa(mode=LoRa.LORA) 7 | 8 | # create a raw LoRa socket 9 | s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 10 | s.setblocking(False) 11 | 12 | # send some data 13 | s.send(bytes([0x01, 0x02, 0x03]) 14 | 15 | # get any data received... 16 | data = s.recv(64) 17 | print(data) 18 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/KPN_abp_node_data_decrypt/config.py: -------------------------------------------------------------------------------- 1 | # -------------------------------------------------- 2 | # change! 3 | # -------------------------------------------------- 4 | # see: https://www.loggly.com 5 | LOGGLY_USER = '' 6 | LOGGLY_PASSWORD = '' 7 | 8 | # Device to use: 9 | # You can get this info from the KPN LoRa Developer Portal page 10 | # Goto the "Edit Device" popup for the device 11 | APPSKEY = '' 12 | DEVADDRESS = '' 13 | DEVEUI = '' 14 | 15 | # -------------------------------------------------- -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/KPN_abp_node_data_decrypt/db_config.py: -------------------------------------------------------------------------------- 1 | DB_USER = '' 2 | DB_PASS = '' 3 | DB_HOST = '' 4 | DB_DB = '' 5 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/KPN_abp_node_data_decrypt/db_store.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from datetime import datetime 4 | import mysql.connector as mysql 5 | import db_config 6 | 7 | 8 | class DbWriter(): 9 | """ 10 | Class for writing data to a MySQL database. 11 | User specific settings can be entered in db_config.py 12 | """ 13 | 14 | def __init__(self): 15 | try: 16 | self.cnx = mysql.connect(user=db_config.DB_USER, 17 | password=db_config.DB_PASS, 18 | host=db_config.DB_HOST, 19 | database=db_config.DB_DB) 20 | self.cursor = self.cnx.cursor() 21 | except mysql.connector.Error as err: 22 | if err.errno == errorcode.ER_ACCESS_DENIED_ERROR: 23 | print("Wrong user name or password") 24 | elif err.errno == errorcode.ER_BAD_DB_ERROR: 25 | print("Database does not exist") 26 | else: 27 | print(err) 28 | 29 | def insert_row(self, query): 30 | self.cursor.execute(query) 31 | self.cnx.commit() 32 | 33 | def close_conn(self): 34 | self.cursor.close() 35 | self.cnx.close() 36 | 37 | 38 | if __name__ == "__main__": 39 | """ 40 | This section is for testing. 41 | 42 | In this example I have created a simple table called lopy_data 43 | id: int(11) primary key, auto increment 44 | date_time: datetime 45 | payload: varchar(255) 46 | """ 47 | queries = [ 48 | """INSERT INTO lopy_data VALUES(null, '{}', '{}'); """.format( 49 | datetime.now(), "23.75"), 50 | """INSERT INTO lopy_data VALUES(null, '{}', '{}'); """.format( 51 | datetime.now(), "21.38") 52 | ] 53 | 54 | # create a database writer instance 55 | db = DbWriter() 56 | 57 | # test some queries (above) 58 | for q in queries: 59 | print q 60 | db.insert_row(q) 61 | 62 | # when we're done, close the connection 63 | db.close_conn() 64 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/KPN_abp_node_data_decrypt/drivers/mysql-connector-python-2.1.6-py2.7-win32.msi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PiAir/pycom-libraries/266e8a1fadb1a6ed1cf68e0b77c24cf9ae70cf29/examples/lorawan-nano-gateway/KPN_abp_node_data_decrypt/drivers/mysql-connector-python-2.1.6-py2.7-win32.msi -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/KPN_abp_node_data_decrypt/drivers/mysql-connector-python-2.1.6-py2.7-winx64.msi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PiAir/pycom-libraries/266e8a1fadb1a6ed1cf68e0b77c24cf9ae70cf29/examples/lorawan-nano-gateway/KPN_abp_node_data_decrypt/drivers/mysql-connector-python-2.1.6-py2.7-winx64.msi -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/KPN_abp_node_data_decrypt/get_loggly_data.py: -------------------------------------------------------------------------------- 1 | from base64 import b64encode 2 | from urllib.parse import urlencode 3 | from urllib.request import Request, urlopen 4 | import json 5 | import time 6 | import loramac_decrypt 7 | import config 8 | 9 | def get_loggly(): 10 | 11 | user = config.LOGGLY_USER 12 | password = config.LOGGLY_PASSWORD 13 | key = config.APPSKEY 14 | dev_addr = config.DEVADDRESS 15 | dev_eui = config.DEVEUI 16 | 17 | dataset = get_loggly_data(user, password, dev_eui, dev_addr, key) 18 | # print(dataset) 19 | # log response to file for debugging purposes 20 | file = open("dataset.txt","w") 21 | file.write(json.dumps(dataset)) 22 | file.close() 23 | return dataset 24 | 25 | 26 | def get_loggly_data(user, password, dev_eui, dev_addr, key, since = '-1d'): 27 | 28 | # retrieving the data from loggly is a 2-step process 29 | # first we need to send a search query that gets scheduled on the server 30 | # then we need to retrieve the results using the rsid that we get from the 31 | # first call. 32 | # if we do this all too quick or too often we get a 403 back so we have to try 33 | # until there is an result 34 | # see: https://www.loggly.com/docs/api-retrieving-data/ 35 | 36 | # user = str(user) 37 | # password = str(password) 38 | # dev_eui = str(dev_eui) 39 | # dev_addr = str(dev_addr) 40 | # key = str(key) 41 | # since = str(since) 42 | 43 | url1 = 'http://'+user+'.loggly.com/apiv2/search' 44 | url2 = 'http://'+user+'.loggly.com/apiv2/events' 45 | 46 | # schedule query and get rsid 47 | data = {} 48 | data['q'] = dev_eui 49 | data['from'] = since 50 | data['until'] = 'now' 51 | data['size'] = 1 # it looks like loggly API ignores this ? 52 | url_values = urlencode(data).encode('ascii') 53 | headers = {'Authorization': b'Basic ' + b64encode((user + ':' + password).encode('utf-8'))} 54 | print('Scheduling query...') 55 | attempts = 0 56 | while attempts < 10: 57 | try: 58 | response = urlopen(Request(url1, url_values, headers)) 59 | break 60 | except: 61 | attempts += 1 62 | print('Schedule request failed. Waiting 10 seconds for retry...') 63 | time.sleep(10) # delays for 10 seconds 64 | if attempts == 10: 65 | print('Oops, did not get rsid in time!') 66 | return 'failed to get rsid' 67 | 68 | js = response.read().decode() 69 | jsonresponse = json.loads(js) 70 | rsid = jsonresponse["rsid"]["id"] 71 | 72 | print('rsid= '+ str(rsid)) 73 | print('Waiting for 10 seconds before getting result...') 74 | time.sleep(10) # delays for 10 seconds 75 | 76 | # try to get result 77 | data = {} 78 | data['rsid'] = str(rsid) 79 | url_values = urlencode(data).encode('ascii') 80 | headers = {'Authorization': b'Basic ' + b64encode((user + ':' + password).encode('utf-8'))} 81 | 82 | attempts = 0 83 | while attempts < 10: 84 | try: 85 | response = urlopen(Request(url2, url_values, headers)) 86 | break 87 | except: 88 | attempts += 1 89 | print('Open failed on attempt '+str(attempts)+'. Waiting for 10 more seconds...') 90 | time.sleep(10) # delays for 10 seconds 91 | 92 | if attempts == 10: 93 | print('Oops, did not get result in time!') 94 | return 'failed to get query result' 95 | 96 | js = response.read().decode() 97 | jsonresponse = json.loads(js) 98 | # log response to file for debugging purposes 99 | # file = open("responselog.txt","w") 100 | # file.write(js) 101 | # file.close() 102 | 103 | 104 | num_results = int(jsonresponse["total_events"]) 105 | datasample = {} 106 | for x in range(0, num_results-1): 107 | datatuple = {} 108 | DevEUI_uplink = jsonresponse["events"][x]["event"]["json"]["DevEUI_uplink"] 109 | datatuple['payload_hex'] = DevEUI_uplink["payload_hex"] 110 | datatuple['FCntUp'] = DevEUI_uplink["FCntUp"] 111 | datatuple['Time'] = DevEUI_uplink["Time"] 112 | datatuple['LrrLON'] = DevEUI_uplink["LrrLON"] 113 | datatuple['LrrLAT'] = DevEUI_uplink["LrrLAT"] 114 | datatuple['Lrrid'] = DevEUI_uplink["Lrrid"] 115 | datatuple['LrrRSSI'] = DevEUI_uplink["LrrRSSI"] 116 | datatuple['LrrSNR'] = DevEUI_uplink["LrrSNR"] 117 | datatuple['SpFact'] = DevEUI_uplink["SpFact"] 118 | datatuple['DevEUI'] = DevEUI_uplink["DevEUI"] 119 | 120 | print('payload_hex = ' + datatuple['payload_hex']) 121 | print('FCntUp = ' + datatuple['FCntUp']) 122 | decrypted = loramac_decrypt.loramac_decrypt(datatuple['payload_hex'], int(datatuple['FCntUp']), key, dev_addr) 123 | 124 | print('Decrypted message:', decrypted ) 125 | decoded = '' 126 | current = 0 127 | while current < len(decrypted): 128 | try: 129 | character = chr(int(decrypted[current])) 130 | except: 131 | character = '' 132 | decoded += character 133 | current += 1 134 | decoded = decoded.encode('utf-8') 135 | print('Decoded message: ', decoded) 136 | 137 | datatuple['payload_decrypted'] = str(decrypted) 138 | datatuple['payload_decoded'] = str(decoded) 139 | datasample[x] = datatuple 140 | 141 | return datasample -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/KPN_abp_node_data_decrypt/loramac_decrypt.py: -------------------------------------------------------------------------------- 1 | 2 | import random 3 | import sys 4 | from binascii import unhexlify 5 | 6 | from cryptography.hazmat.backends import default_backend 7 | from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes 8 | 9 | UP_LINK = 0 10 | DOWN_LINK = 1 11 | 12 | 13 | def to_bytes(s): 14 | '''PY2/PY3 compatible way to convert to something cryptography understands''' 15 | if sys.version_info < (3, ): 16 | return ''.join(map(chr, s)) 17 | else: 18 | return bytes(s) 19 | 20 | 21 | def loramac_decrypt(payload_hex, sequence_counter, key, dev_addr, direction=UP_LINK): 22 | ''' 23 | LoraMac decrypt 24 | 25 | Which is actually encrypting a predefined 16-byte block (ref LoraWAN 26 | specification 4.3.3.1) and XORing that with each block of data. 27 | 28 | payload_hex: hex-encoded payload (FRMPayload) 29 | sequence_counter: integer, sequence counter (FCntUp) 30 | key: 16-byte hex-encoded AES key. (i.e. AABBCCDDEEFFAABBCCDDEEFFAABBCCDD) 31 | dev_addr: 4-byte hex-encoded DevAddr (i.e. AABBCCDD) 32 | direction: 0 for uplink packets, 1 for downlink packets 33 | 34 | returns an array of byte values. 35 | 36 | This method is based on `void LoRaMacPayloadEncrypt()` in 37 | https://github.com/Lora-net/LoRaMac-node/blob/master/src/mac/LoRaMacCrypto.c#L108 38 | ''' 39 | key = unhexlify(key) 40 | dev_addr = unhexlify(dev_addr) 41 | buffer = bytearray(unhexlify(payload_hex)) 42 | size = len(buffer) 43 | 44 | bufferIndex = 0 45 | # block counter 46 | ctr = 1 47 | 48 | # output buffer, initialize to input buffer size. 49 | encBuffer = [0x00] * size 50 | 51 | cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=default_backend()) 52 | 53 | def aes_encrypt_block(aBlock): 54 | ''' 55 | AES encrypt a block. 56 | aes.encrypt expects a string, so we convert the input to string and 57 | the return value to bytes again. 58 | ''' 59 | encryptor = cipher.encryptor() 60 | 61 | return bytearray( 62 | encryptor.update(to_bytes(aBlock)) + encryptor.finalize() 63 | ) 64 | 65 | # For the exact definition of this block refer to 66 | # 'chapter 4.3.3.1 Encryption in LoRaWAN' in the LoRaWAN specification 67 | aBlock = bytearray([ 68 | 0x01, # 0 always 0x01 69 | 0x00, # 1 always 0x00 70 | 0x00, # 2 always 0x00 71 | 0x00, # 3 always 0x00 72 | 0x00, # 4 always 0x00 73 | direction, # 5 dir, 0 for uplink, 1 for downlink 74 | dev_addr[3], # 6 devaddr, lsb 75 | dev_addr[2], # 7 devaddr 76 | dev_addr[1], # 8 devaddr 77 | dev_addr[0], # 9 devaddr, msb 78 | sequence_counter & 0xff, # 10 sequence counter (FCntUp) lsb 79 | (sequence_counter >> 8) & 0xff, # 11 sequence counter 80 | (sequence_counter >> 16) & 0xff, # 12 sequence counter 81 | (sequence_counter >> 24) & 0xff, # 13 sequence counter (FCntUp) msb 82 | 0x00, # 14 always 0x01 83 | 0x00 # 15 block counter 84 | ]) 85 | 86 | # complete blocks 87 | while size >= 16: 88 | aBlock[15] = ctr & 0xFF 89 | ctr += 1 90 | sBlock = aes_encrypt_block(aBlock) 91 | for i in range(16): 92 | encBuffer[bufferIndex + i] = buffer[bufferIndex + i] ^ sBlock[i] 93 | 94 | size -= 16 95 | bufferIndex += 16 96 | 97 | # partial blocks 98 | if size > 0: 99 | aBlock[15] = ctr & 0xFF 100 | sBlock = aes_encrypt_block(aBlock) 101 | for i in range(size): 102 | encBuffer[bufferIndex + i] = buffer[bufferIndex + i] ^ sBlock[i] 103 | 104 | return encBuffer 105 | 106 | 107 | def generate_appskey(): 108 | '''Generate a random secret key''' 109 | return ''.join('{:02X}'.format(x) for x in random.sample(range(255), 16)) 110 | 111 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/KPN_abp_node_data_decrypt/readme.md: -------------------------------------------------------------------------------- 1 | Python script to fetch data send by a LoPy via the KPN LoRaWAN network to the Loggly service. 2 | The script not only fetches the data but also handles de decryption. 3 | 4 | Credits for the actual decryption script: 5 | https://zakelijkforum.kpn.com/lora-forum-16/lora-decryption-on-application-server-8416 6 | Without that post by Michiel Jol and the Python code provided there, this would not have been possible. 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/KPN_abp_node_data_decrypt/runme.py: -------------------------------------------------------------------------------- 1 | import get_loggly_data 2 | print ('Make sure you\'ve changed all the needed settings in config.py') 3 | dataset = get_loggly_data.get_loggly() 4 | print ('If we got to this point without error you should have seen 1 or more decrypted messages on your screen.') 5 | print ('That output has also been stored in a file "dataset.txt", it is in JSON format.') 6 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-abp-puckjs/Send_BT_to_LoPy.js: -------------------------------------------------------------------------------- 1 | var puckID = '690d'; // change!! 2 | 3 | // Are we busy? 4 | var busy = false; 5 | 6 | // The device, if we're connected 7 | var connected = false; 8 | 9 | // The 'tx' characteristic, if connected 10 | var txCharacteristic = false; 11 | 12 | function pad(num, size) { 13 | var s = "000000000" + num; 14 | return s.substr(s.length-size); 15 | } 16 | 17 | 18 | function getHEXValue() { 19 | light = Puck.light() * 1000; 20 | lightHEX = Math.round(light).toString(16); 21 | lightHEX = pad(lightHEX,3); 22 | 23 | temperature = E.getTemperature() * 10; 24 | temperatureHEX = Math.round(temperature).toString(16); 25 | temperatureHEX = pad(temperatureHEX,3); 26 | 27 | battery = NRF.getBattery() * 100; 28 | batteryHEX = Math.round(battery).toString(16); 29 | batteryHEX = pad(batteryHEX,3); 30 | 31 | return lightHEX+temperatureHEX+batteryHEX; 32 | } 33 | 34 | // Function to call 'toggle' on the other Puck 35 | function sendToggle() { 36 | if (!busy) { 37 | busy = true; 38 | if (!connected) { 39 | NRF.requestDevice({ filters: [{ name: 'LoPy01' }] }).then(function(device) { 40 | return device.gatt.connect(); 41 | }).then(function(d) { 42 | digitalPulse(LED3, 1, 1500); // light blue 43 | connected = d; 44 | return d.getPrimaryService("36353433-3231-3039-3837-363534333231"); 45 | }).then(function(s) { 46 | return s.getCharacteristic("36353433-3231-3039-3837-363534336261"); 47 | }).then(function(c) { 48 | hexValue = puckID + getHEXValue(); 49 | console.log(hexValue); 50 | c.writeValue(hexValue); 51 | busy = false; 52 | 53 | }).then(function() { 54 | if (connected) connected.disconnect(); 55 | connected=false; 56 | digitalPulse(LED2, 1, 1000); // light green 57 | }).catch(function() { 58 | if (connected) connected.disconnect(); 59 | connected=false; 60 | digitalPulse(LED1, 1, 1000); // light red if we had a problem 61 | busy = false; 62 | }); 63 | } else { 64 | console.log("already connected!"); 65 | if (connected) connected.disconnect(); 66 | connected=false; 67 | busy = false; 68 | } 69 | } else { 70 | console.log("already busy!"); 71 | if (connected) connected.disconnect(); 72 | connected=false; 73 | busy = false; 74 | } 75 | } 76 | 77 | // Call this function when the button is pressed 78 | setWatch(sendToggle, BTN, { edge:"rising", debounce:50, repeat: true }); 79 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-abp-puckjs/boot.py: -------------------------------------------------------------------------------- 1 | # boot.py -- run on boot-up 2 | import os 3 | import time 4 | import pycom 5 | import machine 6 | import config 7 | from network import WLAN 8 | from machine import UART 9 | 10 | uart = UART(0, 115200) 11 | os.dupterm(uart) 12 | 13 | uart1 = UART(1, 9600) 14 | 15 | pycom.heartbeat(False) 16 | pycom.rgbled(0x7f7f00) # yellow 17 | 18 | wlan = WLAN() # get current object, without changing the mode 19 | 20 | if machine.reset_cause() != machine.SOFT_RESET: 21 | wlan.init(mode=WLAN.STA) 22 | # configuration below MUST match your home router settings!! 23 | wlan.ifconfig(config=(config.WIFI_IP, config.WIFI_SUBNET, config.WIFI_GATEWAY, config.WIFI_DNS1)) 24 | 25 | nets = wlan.scan() 26 | 27 | for net in nets: 28 | if net.ssid == config.WIFI_SSID: 29 | if not wlan.isconnected(): 30 | wlan.connect(config.WIFI_SSID, auth=(WLAN.WPA2, config.WIFI_PASS), timeout=5000) 31 | while not wlan.isconnected(): 32 | machine.idle() # save power while waiting 33 | pycom.rgbled(0x007f00) # green 34 | time.sleep(3) 35 | pycom.rgbled(0) 36 | break 37 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-abp-puckjs/lib/config.py: -------------------------------------------------------------------------------- 1 | """ LoPy LoRaWAN Nodeconfiguration options """ 2 | 3 | # These you need to replace! 4 | DEV_ADDR = '' 5 | NWK_SWKEY = '' 6 | APP_SWKEY = '' 7 | 8 | # Leave them empty or unchanged if you don't want the 9 | # node to connect to WiFi 10 | WIFI_SSID = '' 11 | WIFI_PASS = '' 12 | WIFI_IP = '' # fixed IP 13 | WIFI_SUBNET = '' 14 | WIFI_GATEWAY = '' 15 | WIFI_DNS1 = '' -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-abp-puckjs/main.py: -------------------------------------------------------------------------------- 1 | from network import Bluetooth 2 | from network import LoRa 3 | import socket 4 | import binascii 5 | import struct 6 | import time 7 | import config 8 | import pycom 9 | 10 | # Setup the LoRaWAN part 11 | 12 | # Initialize LoRa in LORAWAN mode. 13 | lora = LoRa(mode=LoRa.LORAWAN) 14 | 15 | # create an ABP authentication params 16 | dev_addr = struct.unpack(">l", binascii.unhexlify(config.DEV_ADDR.replace(' ','')))[0] 17 | nwk_swkey = binascii.unhexlify(config.NWK_SWKEY.replace(' ','')) 18 | app_swkey = binascii.unhexlify(config.APP_SWKEY.replace(' ','')) 19 | 20 | # join a network using ABP (Activation By Personalization) 21 | lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey)) 22 | 23 | # remove all the non-default channels 24 | for i in range(3, 16): 25 | lora.remove_channel(i) 26 | 27 | # set the 3 default channels to the same frequency 28 | lora.add_channel(0, frequency=868100000, dr_min=0, dr_max=5) 29 | lora.add_channel(1, frequency=868100000, dr_min=0, dr_max=5) 30 | lora.add_channel(2, frequency=868100000, dr_min=0, dr_max=5) 31 | 32 | # create a LoRa socket 33 | s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 34 | 35 | # set the LoRaWAN data rate 36 | s.setsockopt(socket.SOL_LORA, socket.SO_DR, 5) 37 | 38 | # make the socket blocking 39 | s.setblocking(False) 40 | 41 | # Setup the Bluetooth part 42 | 43 | hexValue = ""; 44 | 45 | bluetooth = Bluetooth() 46 | bluetooth.set_advertisement(name='LoPy01', service_uuid=b'1234567890123456') 47 | 48 | def conn_cb (bt_o): 49 | events = bt_o.events() 50 | if events & Bluetooth.CLIENT_CONNECTED: 51 | print("Client connected") 52 | hexValue = ""; 53 | elif events & Bluetooth.CLIENT_DISCONNECTED: 54 | print("Client disconnected") 55 | 56 | bluetooth.callback(trigger=Bluetooth.CLIENT_CONNECTED | Bluetooth.CLIENT_DISCONNECTED, handler=conn_cb) 57 | 58 | bluetooth.advertise(True) 59 | 60 | srv1 = bluetooth.service(uuid=b'1234567890123456', isprimary=True) 61 | 62 | chr1 = srv1.characteristic(uuid=b'ab34567890123456', value=5) 63 | 64 | def char1_cb(chr): 65 | print("Write request with value = {}".format(chr.value())) 66 | hexValue = chr.value() 67 | s.send(bytes(hexValue)) 68 | print('sending hexValue to LoRaWAN') 69 | 70 | char1_cb = chr1.callback(trigger=Bluetooth.CHAR_WRITE_EVENT, handler=char1_cb) 71 | 72 | srv2 = bluetooth.service(uuid=1234, isprimary=True) 73 | 74 | chr2 = srv2.characteristic(uuid=4567) 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-abp-puckjs/payloadfunction.js: -------------------------------------------------------------------------------- 1 | function Decoder(bytes, port) { 2 | 3 | var decoded = {}; 4 | var str = ''; 5 | for (var i = 0; i < bytes.length; i += 1) 6 | str += String.fromCharCode(parseInt(bytes[i])); 7 | 8 | decoded.byteslength = bytes.length; 9 | decoded.raw = bytes; 10 | decoded.hexstring = str; 11 | decoded.puckid = str.substring(0, 4); 12 | decoded.light = parseInt(str.substring(4, 7), 16)/1000; 13 | decoded.temperature = parseInt(str.substring(7, 10), 16)/10; 14 | decoded.battery = parseInt(str.substring(10, 13), 16)/100; 15 | 16 | return decoded; 17 | } 18 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-abp-puckjs/readme.md: -------------------------------------------------------------------------------- 1 | Connect the puck-js puck to TTN via the LoPy 2 | 3 | Upload the Send_BT_to_LoPy.js script to the puck-js 4 | 5 | The other files go unto the LoPy 6 | 7 | Make sure to edit the lib\config.py 8 | 9 | 10 | ** UUID ** 11 | A bit more info on the use of the UUID used by the Lopy was provided by Jose at the Pycom forum, here: 12 | https://forum.pycom.io/topic/1004/connect-the-puck-js-to-the-things-network-using-a-pycom-lopy/2 13 | So, as a general advise it is better to generate your own UUID's if you want your services to be mistaken for the ones created by others. 14 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-abp_node/boot.py: -------------------------------------------------------------------------------- 1 | # boot.py -- run on boot-up 2 | import os 3 | import time 4 | import pycom 5 | import machine 6 | import config 7 | from network import WLAN 8 | from machine import UART 9 | 10 | uart = UART(0, 115200) 11 | os.dupterm(uart) 12 | 13 | pycom.heartbeat(False) 14 | pycom.rgbled(0x7f7f00) # yellow 15 | 16 | wlan = WLAN() # get current object, without changing the mode 17 | 18 | if machine.reset_cause() != machine.SOFT_RESET: 19 | wlan.init(mode=WLAN.STA) 20 | # configuration below MUST match your home router settings!! 21 | wlan.ifconfig(config=(config.WIFI_IP, config.WIFI_SUBNET, config.WIFI_GATEWAY, config.WIFI_DNS1)) 22 | 23 | nets = wlan.scan() 24 | 25 | for net in nets: 26 | if net.ssid == config.WIFI_SSID: 27 | if not wlan.isconnected(): 28 | wlan.connect(config.WIFI_SSID, auth=(WLAN.WPA2, config.WIFI_PASS), timeout=5000) 29 | while not wlan.isconnected(): 30 | machine.idle() # save power while waiting 31 | pycom.rgbled(0x007f00) # green 32 | time.sleep(3) 33 | pycom.rgbled(0) 34 | break 35 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-abp_node/lib/config.py: -------------------------------------------------------------------------------- 1 | """ LoPy LoRaWAN Nodeconfiguration options """ 2 | 3 | # These you need to replace! 4 | DEV_ADDR = '' 5 | NWK_SWKEY = '' 6 | APP_SWKEY = '' 7 | 8 | # Leave them empty or unchanged if you don't want the 9 | # node to connect to WiFi 10 | WIFI_SSID = '' 11 | WIFI_PASS = '' 12 | WIFI_IP = '' # fixed IP 13 | WIFI_SUBNET = '' 14 | WIFI_GATEWAY = '' 15 | WIFI_DNS1 = '' 16 | 17 | 18 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-abp_node/main.py: -------------------------------------------------------------------------------- 1 | from network import LoRa 2 | import socket 3 | import binascii 4 | import struct 5 | import time 6 | import config 7 | 8 | # Initialize LoRa in LORAWAN mode. 9 | lora = LoRa(mode=LoRa.LORAWAN) 10 | 11 | # create an ABP authentication params 12 | dev_addr = struct.unpack(">l", binascii.unhexlify(config.DEV_ADDR.replace(' ','')))[0] 13 | nwk_swkey = binascii.unhexlify(config.NWK_SWKEY.replace(' ','')) 14 | app_swkey = binascii.unhexlify(config.APP_SWKEY.replace(' ','')) 15 | 16 | # join a network using ABP (Activation By Personalization) 17 | lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey)) 18 | 19 | # remove all the non-default channels 20 | for i in range(3, 16): 21 | lora.remove_channel(i) 22 | 23 | # set the 3 default channels to the same frequency 24 | lora.add_channel(0, frequency=868100000, dr_min=0, dr_max=5) 25 | lora.add_channel(1, frequency=868100000, dr_min=0, dr_max=5) 26 | lora.add_channel(2, frequency=868100000, dr_min=0, dr_max=5) 27 | 28 | # create a LoRa socket 29 | s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 30 | 31 | # set the LoRaWAN data rate 32 | s.setsockopt(socket.SOL_LORA, socket.SO_DR, 5) 33 | 34 | # make the socket blocking 35 | s.setblocking(False) 36 | 37 | for i in range (2000): 38 | pycom.rgbled(0x007f700) # green 39 | s.send(b'PKT #' + bytes([i])) 40 | print('sending ' + str(i)) 41 | time.sleep(.5) 42 | pycom.rgbled(0) 43 | time.sleep(3.5) 44 | rx = s.recv(256) 45 | if rx: 46 | pycom.rgbled(0x00007F) # blue 47 | print(rx) 48 | time.sleep(1) 49 | pycom.rgbled(0) 50 | time.sleep(30) -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-abp_node_GPS/boot.py: -------------------------------------------------------------------------------- 1 | # boot.py -- run on boot-up 2 | import os 3 | import time 4 | import pycom 5 | import machine 6 | import config 7 | from network import WLAN 8 | from machine import UART 9 | 10 | uart = UART(0, 115200) 11 | os.dupterm(uart) 12 | 13 | pycom.heartbeat(False) 14 | pycom.rgbled(0x7f7f00) # yellow 15 | 16 | wlan = WLAN() # get current object, without changing the mode 17 | 18 | if machine.reset_cause() != machine.SOFT_RESET: 19 | wlan.init(mode=WLAN.STA) 20 | # configuration below MUST match your home router settings!! 21 | wlan.ifconfig(config=(config.WIFI_IP, config.WIFI_SUBNET, config.WIFI_GATEWAY, config.WIFI_DNS1)) 22 | 23 | nets = wlan.scan() 24 | 25 | for net in nets: 26 | if net.ssid == config.WIFI_SSID: 27 | if not wlan.isconnected(): 28 | wlan.connect(config.WIFI_SSID, auth=(WLAN.WPA2, config.WIFI_PASS), timeout=5000) 29 | while not wlan.isconnected(): 30 | machine.idle() # save power while waiting 31 | pycom.rgbled(0x007f00) # green 32 | time.sleep(3) 33 | pycom.rgbled(0) 34 | break 35 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-abp_node_GPS/lib/config.py: -------------------------------------------------------------------------------- 1 | """ LoPy LoRaWAN Nodeconfiguration options """ 2 | from array import array 3 | 4 | DEBUG = 1 5 | 6 | WIFI_SSID = '' 7 | WIFI_PASS = '' 8 | WIFI_IP = '' # fixed IP 9 | WIFI_SUBNET = '' 10 | WIFI_GATEWAY = '' 11 | WIFI_DNS1 = '' 12 | 13 | # TTN 14 | # These you need to replace! 15 | DEV_ADDR = '' 16 | NWK_SWKEY = '' 17 | APP_SWKEY = '' 18 | 19 | # set the port, one is used during debugging 20 | # you can filter that port out in the TTN mapper integration 21 | TTN_FPort_debug = 1 22 | TTN_FPort = 2 23 | 24 | # CREATE LOG? 25 | LOG = 1 26 | FILENAME = 'log.txt' 27 | 28 | # GPS UART connection 29 | TX = "P11" 30 | RX = "P12" 31 | 32 | # TIMEZONE / DAYLIGHT SAVING 33 | TIMEZONE = 1 34 | DAYLIGHT = 1 35 | 36 | # GPS SPEED variables 37 | AUTO = 0 38 | STAT = 1 39 | WALK = 2 40 | CYCLE = 3 41 | CAR = 4 42 | 43 | # update speed in seconds 44 | UPDATE = array('H', [30,30,20,10,10]) 45 | MAXSPEED = array('H', [0,1,6,30,150]) 46 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-abp_node_GPS/lib/micropyGPS.py: -------------------------------------------------------------------------------- 1 | # MicropyGPS - a GPS NMEA sentence parser for Micropython/Python 3.X 2 | 3 | # 4 | # The MIT License (MIT) 5 | 6 | # Copyright (c) 2014 Michael Calvin McCoy (calvin.mccoy@gmail.com) 7 | # 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 9 | # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 10 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit 11 | # persons to whom the Software is furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 14 | # Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 17 | # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | 21 | 22 | # TODO: 23 | # Time Since First Fix 24 | # Distance/Time to Target 25 | # Logging 26 | # More Helper Functions 27 | # Dynamically limit sentences types to parse 28 | 29 | from math import floor 30 | 31 | # Import pyb or time for fix time handling 32 | try: 33 | # Assume running on pyboard 34 | import pyb 35 | except ImportError: 36 | # Otherwise default to time module for non-embedded implementations 37 | # Note that this forces the resolution of the fix time 1 second instead 38 | # of milliseconds as on the pyboard 39 | import time 40 | 41 | 42 | class MicropyGPS(object): 43 | """GPS NMEA Sentence Parser. Creates object that stores all relevant GPS data and statistics. 44 | Parses sentences one character at a time using update(). """ 45 | 46 | # Max Number of Characters a valid sentence can be (based on GGA sentence) 47 | SENTENCE_LIMIT = 76 48 | __HEMISPHERES = ('N', 'S', 'E', 'W') 49 | __NO_FIX = 1 50 | __FIX_2D = 2 51 | __FIX_3D = 3 52 | __DIRECTIONS = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'] 53 | __MONTHS = ('January', 'February', 'March', 'April', 'May', 54 | 'June', 'July', 'August', 'September', 'October', 55 | 'November', 'December') 56 | 57 | def __init__(self, local_offset=0): 58 | """Setup GPS Object Status Flags, Internal Data Registers, etc""" 59 | 60 | ##################### 61 | # Object Status Flags 62 | self.sentence_active = False 63 | self.active_segment = 0 64 | self.process_crc = False 65 | self.gps_segments = [] 66 | self.crc_xor = 0 67 | self.char_count = 0 68 | self.fix_time = 0 69 | 70 | ##################### 71 | # Sentence Statistics 72 | self.crc_fails = 0 73 | self.clean_sentences = 0 74 | self.parsed_sentences = 0 75 | 76 | ##################### 77 | # Logging Related 78 | self.log_handle = None 79 | self.log_en = False 80 | 81 | ##################### 82 | # Data From Sentences 83 | # Time 84 | self.timestamp = (0, 0, 0) 85 | self.date = (0, 0, 0) 86 | self.local_offset = local_offset 87 | 88 | # Position/Motion 89 | self.latitude = (0, 0.0, 'N') 90 | self.longitude = (0, 0.0, 'W') 91 | self.speed = (0.0, 0.0, 0.0) 92 | self.course = 0.0 93 | self.altitude = 0.0 94 | self.geoid_height = 0.0 95 | 96 | # GPS Info 97 | self.satellites_in_view = 0 98 | self.satellites_in_use = 0 99 | self.satellites_used = [] 100 | self.last_sv_sentence = 0 101 | self.total_sv_sentences = 0 102 | self.satellite_data = dict() 103 | self.hdop = 0.0 104 | self.pdop = 0.0 105 | self.vdop = 0.0 106 | self.valid = False 107 | self.fix_stat = 0 108 | self.fix_type = 1 109 | 110 | ######################################## 111 | # Logging Related Functions 112 | ######################################## 113 | def start_logging(self, target_file, mode="append"): 114 | """ 115 | Create GPS data log object 116 | """ 117 | if mode == 'new': 118 | mode_code = 'w' 119 | else: 120 | mode_code = 'a' 121 | try: 122 | self.log_handle = open(target_file, mode_code) 123 | except AttributeError: 124 | print("Invalid FileName") 125 | return False 126 | 127 | self.log_en = True 128 | return True 129 | 130 | def stop_logging(self): 131 | """ 132 | Closes the log file handler and disables further logging 133 | """ 134 | try: 135 | self.log_handle.close() 136 | except AttributeError: 137 | print("Invalid Handle") 138 | return False 139 | 140 | self.log_en = False 141 | return True 142 | 143 | def write_log(self, log_string): 144 | """Attempts to write the last valid NMEA sentence character to the active file handler 145 | """ 146 | try: 147 | self.log_handle.write(log_string) 148 | except TypeError: 149 | return False 150 | return True 151 | 152 | ######################################## 153 | # Sentence Parsers 154 | ######################################## 155 | def gprmc(self): 156 | """Parse Recommended Minimum Specific GPS/Transit data (RMC)Sentence. Updates UTC timestamp, latitude, 157 | longitude, Course, Speed, Date, and fix status""" 158 | 159 | # UTC Timestamp 160 | try: 161 | utc_string = self.gps_segments[1] 162 | 163 | if utc_string: # Possible timestamp found 164 | hours = int(utc_string[0:2]) + self.local_offset 165 | minutes = int(utc_string[2:4]) 166 | seconds = float(utc_string[4:]) 167 | self.timestamp = (hours, minutes, seconds) 168 | else: # No Time stamp yet 169 | self.timestamp = (0, 0, 0) 170 | 171 | except ValueError: # Bad Timestamp value present 172 | return False 173 | 174 | # Date stamp 175 | try: 176 | date_string = self.gps_segments[9] 177 | 178 | # Date string printer function assumes to be year >=2000, 179 | # date_string() must be supplied with the correct century argument to display correctly 180 | if date_string: # Possible date stamp found 181 | day = int(date_string[0:2]) 182 | month = int(date_string[2:4]) 183 | year = int(date_string[4:6]) 184 | self.date = (day, month, year) 185 | else: # No Date stamp yet 186 | self.date = (0, 0, 0) 187 | 188 | except ValueError: # Bad Date stamp value present 189 | return False 190 | 191 | # Check Receiver Data Valid Flag 192 | if self.gps_segments[2] == 'A': # Data from Receiver is Valid/Has Fix 193 | 194 | # Longitude / Latitude 195 | try: 196 | # Latitude 197 | l_string = self.gps_segments[3] 198 | lat_degs = int(l_string[0:2]) 199 | lat_mins = float(l_string[2:]) 200 | lat_hemi = self.gps_segments[4] 201 | 202 | # Longitude 203 | l_string = self.gps_segments[5] 204 | lon_degs = int(l_string[0:3]) 205 | lon_mins = float(l_string[3:]) 206 | lon_hemi = self.gps_segments[6] 207 | except ValueError: 208 | return False 209 | 210 | if lat_hemi not in self.__HEMISPHERES: 211 | return False 212 | 213 | if lon_hemi not in self.__HEMISPHERES: 214 | return False 215 | 216 | # Speed 217 | try: 218 | spd_knt = float(self.gps_segments[7]) 219 | except ValueError: 220 | return False 221 | 222 | # Course 223 | try: 224 | course = float(self.gps_segments[8]) 225 | except ValueError: 226 | return False 227 | 228 | # TODO - Add Magnetic Variation 229 | 230 | # Update Object Data 231 | self.latitude = (lat_degs, lat_mins, lat_hemi) 232 | self.longitude = (lon_degs, lon_mins, lon_hemi) 233 | # Include mph and hm/h 234 | self.speed = (spd_knt, spd_knt * 1.151, spd_knt * 1.852) 235 | self.course = course 236 | self.valid = True 237 | 238 | # Update Last Fix Time 239 | self.new_fix_time() 240 | 241 | else: # Clear Position Data if Sentence is 'Invalid' 242 | self.latitude = (0, 0.0, 'N') 243 | self.longitude = (0, 0.0, 'W') 244 | self.speed = (0.0, 0.0, 0.0) 245 | self.course = 0.0 246 | self.date = (0, 0, 0) 247 | self.valid = False 248 | 249 | return True 250 | 251 | def gpgll(self): 252 | """Parse Geographic Latitude and Longitude (GLL)Sentence. Updates UTC timestamp, latitude, 253 | longitude, and fix status""" 254 | 255 | # UTC Timestamp 256 | try: 257 | utc_string = self.gps_segments[5] 258 | 259 | if utc_string: # Possible timestamp found 260 | hours = int(utc_string[0:2]) + self.local_offset 261 | minutes = int(utc_string[2:4]) 262 | seconds = float(utc_string[4:]) 263 | self.timestamp = (hours, minutes, seconds) 264 | else: # No Time stamp yet 265 | self.timestamp = (0, 0, 0) 266 | 267 | except ValueError: # Bad Timestamp value present 268 | return False 269 | 270 | # Check Receiver Data Valid Flag 271 | if self.gps_segments[6] == 'A': # Data from Receiver is Valid/Has Fix 272 | 273 | # Longitude / Latitude 274 | try: 275 | # Latitude 276 | l_string = self.gps_segments[1] 277 | lat_degs = int(l_string[0:2]) 278 | lat_mins = float(l_string[2:]) 279 | lat_hemi = self.gps_segments[2] 280 | 281 | # Longitude 282 | l_string = self.gps_segments[3] 283 | lon_degs = int(l_string[0:3]) 284 | lon_mins = float(l_string[3:]) 285 | lon_hemi = self.gps_segments[4] 286 | except ValueError: 287 | return False 288 | 289 | if lat_hemi not in self.__HEMISPHERES: 290 | return False 291 | 292 | if lon_hemi not in self.__HEMISPHERES: 293 | return False 294 | 295 | # Update Object Data 296 | self.latitude = (lat_degs, lat_mins, lat_hemi) 297 | self.longitude = (lon_degs, lon_mins, lon_hemi) 298 | self.valid = True 299 | 300 | # Update Last Fix Time 301 | self.new_fix_time() 302 | 303 | else: # Clear Position Data if Sentence is 'Invalid' 304 | self.latitude = (0, 0.0, 'N') 305 | self.longitude = (0, 0.0, 'W') 306 | self.valid = False 307 | 308 | return True 309 | 310 | def gpvtg(self): 311 | """Parse Track Made Good and Ground Speed (VTG) Sentence. Updates speed and course""" 312 | try: 313 | course = float(self.gps_segments[1]) 314 | spd_knt = float(self.gps_segments[5]) 315 | except ValueError: 316 | return False 317 | 318 | # Include mph and km/h 319 | self.speed = (spd_knt, spd_knt * 1.151, spd_knt * 1.852) 320 | self.course = course 321 | return True 322 | 323 | def gpgga(self): 324 | """Parse Global Positioning System Fix Data (GGA) Sentence. Updates UTC timestamp, latitude, longitude, 325 | fix status, satellites in use, Horizontal Dilution of Precision (HDOP), altitude, geoid height and fix status""" 326 | 327 | try: 328 | # UTC Timestamp 329 | utc_string = self.gps_segments[1] 330 | 331 | # Skip timestamp if receiver doesn't have on yet 332 | if utc_string: 333 | hours = int(utc_string[0:2]) + self.local_offset 334 | minutes = int(utc_string[2:4]) 335 | seconds = float(utc_string[4:]) 336 | else: 337 | hours = 0 338 | minutes = 0 339 | seconds = 0.0 340 | 341 | # Number of Satellites in Use 342 | satellites_in_use = int(self.gps_segments[7]) 343 | 344 | # Horizontal Dilution of Precision 345 | hdop = float(self.gps_segments[8]) 346 | 347 | # Get Fix Status 348 | fix_stat = int(self.gps_segments[6]) 349 | 350 | except ValueError: 351 | return False 352 | 353 | # Process Location and Speed Data if Fix is GOOD 354 | if fix_stat: 355 | 356 | # Longitude / Latitude 357 | try: 358 | # Latitude 359 | l_string = self.gps_segments[2] 360 | lat_degs = int(l_string[0:2]) 361 | lat_mins = float(l_string[2:]) 362 | lat_hemi = self.gps_segments[3] 363 | 364 | # Longitude 365 | l_string = self.gps_segments[4] 366 | lon_degs = int(l_string[0:3]) 367 | lon_mins = float(l_string[3:]) 368 | lon_hemi = self.gps_segments[5] 369 | except ValueError: 370 | return False 371 | 372 | if lat_hemi not in self.__HEMISPHERES: 373 | return False 374 | 375 | if lon_hemi not in self.__HEMISPHERES: 376 | return False 377 | 378 | # Altitude / Height Above Geoid 379 | try: 380 | altitude = float(self.gps_segments[9]) 381 | geoid_height = float(self.gps_segments[11]) 382 | except ValueError: 383 | return False 384 | 385 | # Update Object Data 386 | self.latitude = (lat_degs, lat_mins, lat_hemi) 387 | self.longitude = (lon_degs, lon_mins, lon_hemi) 388 | self.altitude = altitude 389 | self.geoid_height = geoid_height 390 | 391 | # Update Object Data 392 | self.timestamp = (hours, minutes, seconds) 393 | self.satellites_in_use = satellites_in_use 394 | self.hdop = hdop 395 | self.fix_stat = fix_stat 396 | 397 | # If Fix is GOOD, update fix timestamp 398 | if fix_stat: 399 | self.new_fix_time() 400 | 401 | return True 402 | 403 | def gpgsa(self): 404 | """Parse GNSS DOP and Active Satellites (GSA) sentence. Updates GPS fix type, list of satellites used in 405 | fix calculation, Position Dilution of Precision (PDOP), Horizontal Dilution of Precision (HDOP), Vertical 406 | Dilution of Precision, and fix status""" 407 | 408 | # Fix Type (None,2D or 3D) 409 | try: 410 | fix_type = int(self.gps_segments[2]) 411 | except ValueError: 412 | return False 413 | 414 | # Read All (up to 12) Available PRN Satellite Numbers 415 | sats_used = [] 416 | for sats in range(12): 417 | sat_number_str = self.gps_segments[3 + sats] 418 | if sat_number_str: 419 | try: 420 | sat_number = int(sat_number_str) 421 | sats_used.append(sat_number) 422 | except ValueError: 423 | return False 424 | else: 425 | break 426 | 427 | # PDOP,HDOP,VDOP 428 | try: 429 | pdop = float(self.gps_segments[15]) 430 | hdop = float(self.gps_segments[16]) 431 | vdop = float(self.gps_segments[17]) 432 | except ValueError: 433 | return False 434 | 435 | # Update Object Data 436 | self.fix_type = fix_type 437 | 438 | # If Fix is GOOD, update fix timestamp 439 | if fix_type > self.__NO_FIX: 440 | self.new_fix_time() 441 | 442 | self.satellites_used = sats_used 443 | self.hdop = hdop 444 | self.vdop = vdop 445 | self.pdop = pdop 446 | 447 | return True 448 | 449 | def gpgsv(self): 450 | """Parse Satellites in View (GSV) sentence. Updates number of SV Sentences,the number of the last SV sentence 451 | parsed, and data on each satellite present in the sentence""" 452 | try: 453 | num_sv_sentences = int(self.gps_segments[1]) 454 | current_sv_sentence = int(self.gps_segments[2]) 455 | sats_in_view = int(self.gps_segments[3]) 456 | except ValueError: 457 | return False 458 | 459 | # Create a blank dict to store all the satellite data from this sentence in: 460 | # satellite PRN is key, tuple containing telemetry is value 461 | satellite_dict = dict() 462 | 463 | # Calculate Number of Satelites to pull data for and thus how many segment positions to read 464 | if num_sv_sentences == current_sv_sentence: 465 | sat_segment_limit = ((sats_in_view % 4) * 4) + 4 # Last sentence may have 1-4 satellites 466 | else: 467 | sat_segment_limit = 20 # Non-last sentences have 4 satellites and thus read up to position 20 468 | 469 | # Try to recover data for up to 4 satellites in sentence 470 | for sats in range(4, sat_segment_limit, 4): 471 | 472 | # If a PRN is present, grab satellite data 473 | if self.gps_segments[sats]: 474 | try: 475 | sat_id = int(self.gps_segments[sats]) 476 | except ValueError: 477 | return False 478 | 479 | try: # elevation can be null (no value) when not tracking 480 | elevation = int(self.gps_segments[sats+1]) 481 | except ValueError: 482 | elevation = None 483 | 484 | try: # azimuth can be null (no value) when not tracking 485 | azimuth = int(self.gps_segments[sats+2]) 486 | except ValueError: 487 | azimuth = None 488 | 489 | try: # SNR can be null (no value) when not tracking 490 | snr = int(self.gps_segments[sats+3]) 491 | except ValueError: 492 | snr = None 493 | 494 | # If no PRN is found, then the sentence has no more satellites to read 495 | else: 496 | break 497 | 498 | # Add Satellite Data to Sentence Dict 499 | satellite_dict[sat_id] = (elevation, azimuth, snr) 500 | 501 | # Update Object Data 502 | self.total_sv_sentences = num_sv_sentences 503 | self.last_sv_sentence = current_sv_sentence 504 | self.satellites_in_view = sats_in_view 505 | 506 | # For a new set of sentences, we either clear out the existing sat data or 507 | # update it as additional SV sentences are parsed 508 | if current_sv_sentence == 1: 509 | self.satellite_data = satellite_dict 510 | else: 511 | self.satellite_data.update(satellite_dict) 512 | 513 | return True 514 | 515 | ########################################## 516 | # Data Stream Handler Functions 517 | ########################################## 518 | 519 | def new_sentence(self): 520 | """Adjust Object Flags in Preparation for a New Sentence""" 521 | self.gps_segments = [''] 522 | self.active_segment = 0 523 | self.crc_xor = 0 524 | self.sentence_active = True 525 | self.process_crc = True 526 | self.char_count = 0 527 | 528 | def update(self, new_char): 529 | """Process a new input char and updates GPS object if necessary based on special characters ('$', ',', '*') 530 | Function builds a list of received string that are validate by CRC prior to parsing by the appropriate 531 | sentence function. Returns sentence type on successful parse, None otherwise""" 532 | 533 | valid_sentence = False 534 | 535 | # Validate new_char is a printable char 536 | ascii_char = ord(new_char) 537 | 538 | if 10 <= ascii_char <= 126: 539 | self.char_count += 1 540 | 541 | # Write Character to log file if enabled 542 | if self.log_en: 543 | self.write_log(new_char) 544 | 545 | # Check if a new string is starting ($) 546 | if new_char == '$': 547 | self.new_sentence() 548 | return None 549 | 550 | elif self.sentence_active: 551 | 552 | # Check if sentence is ending (*) 553 | if new_char == '*': 554 | self.process_crc = False 555 | self.active_segment += 1 556 | self.gps_segments.append('') 557 | return None 558 | 559 | # Check if a section is ended (,), Create a new substring to feed 560 | # characters to 561 | elif new_char == ',': 562 | self.active_segment += 1 563 | self.gps_segments.append('') 564 | 565 | # Store All Other printable character and check CRC when ready 566 | else: 567 | self.gps_segments[self.active_segment] += new_char 568 | 569 | # When CRC input is disabled, sentence is nearly complete 570 | if not self.process_crc: 571 | 572 | if len(self.gps_segments[self.active_segment]) == 2: 573 | try: 574 | final_crc = int(self.gps_segments[self.active_segment], 16) 575 | if self.crc_xor == final_crc: 576 | valid_sentence = True 577 | else: 578 | self.crc_fails += 1 579 | except ValueError: 580 | pass # CRC Value was deformed and could not have been correct 581 | 582 | # Update CRC 583 | if self.process_crc: 584 | self.crc_xor ^= ascii_char 585 | 586 | # If a Valid Sentence Was received and it's a supported sentence, then parse it!! 587 | if valid_sentence: 588 | self.clean_sentences += 1 # Increment clean sentences received 589 | self.sentence_active = False # Clear Active Processing Flag 590 | 591 | if self.gps_segments[0] in self.supported_sentences: 592 | 593 | # parse the Sentence Based on the message type, return True if parse is clean 594 | if self.supported_sentences[self.gps_segments[0]](self): 595 | 596 | # Let host know that the GPS object was updated by returning parsed sentence type 597 | self.parsed_sentences += 1 598 | return self.gps_segments[0] 599 | 600 | # Check that the sentence buffer isn't filling up with Garage waiting for the sentence to complete 601 | if self.char_count > self.SENTENCE_LIMIT: 602 | self.sentence_active = False 603 | 604 | # Tell Host no new sentence was parsed 605 | return None 606 | 607 | def new_fix_time(self): 608 | """Updates a high resolution counter with current time when fix is updated. Currently only triggered from 609 | GGA, GSA and RMC sentences""" 610 | try: 611 | self.fix_time = pyb.millis() 612 | except NameError: 613 | self.fix_time = time.time() 614 | 615 | ######################################### 616 | # User Helper Functions 617 | # These functions make working with the GPS object data easier 618 | ######################################### 619 | 620 | def satellite_data_updated(self): 621 | """ 622 | Checks if the all the GSV sentences in a group have been read, making satellite data complete 623 | :return: boolean 624 | """ 625 | if self.total_sv_sentences > 0 and self.total_sv_sentences == self.last_sv_sentence: 626 | return True 627 | else: 628 | return False 629 | 630 | def satellites_visible(self): 631 | """ 632 | Returns a list of of the satellite PRNs currently visible to the receiver 633 | :return: list 634 | """ 635 | return list(self.satellite_data.keys()) 636 | 637 | def time_since_fix(self): 638 | """Returns number of millisecond since the last sentence with a valid fix was parsed. Returns 0 if 639 | no fix has been found""" 640 | 641 | # Test if a Fix has been found 642 | if self.fix_time == 0: 643 | return -1 644 | 645 | # Try calculating fix time assuming using millis on a pyboard; default to seconds if not 646 | try: 647 | current = pyb.elapsed_millis(self.fix_time) 648 | except NameError: 649 | current = time.time() - self.fix_time 650 | 651 | return current 652 | 653 | def compass_direction(self): 654 | """ 655 | Determine a cardinal or inter-cardinal direction based on current course. 656 | :return: string 657 | """ 658 | # Calculate the offset for a rotated compass 659 | if self.course >= 348.75: 660 | offset_course = 360 - self.course 661 | else: 662 | offset_course = self.course + 11.25 663 | 664 | # Each compass point is separated by 22.5 degrees, divide to find lookup value 665 | dir_index = floor(offset_course / 22.5) 666 | 667 | final_dir = self.__DIRECTIONS[dir_index] 668 | 669 | return final_dir 670 | 671 | def latitude_string(self): 672 | """ 673 | Create a readable string of the current latitude data 674 | :return: string 675 | """ 676 | lat_string = str(self.latitude[0]) + '° ' + str(self.latitude[1]) + "' " + str(self.latitude[2]) 677 | return lat_string 678 | 679 | def longitude_string(self): 680 | """ 681 | Create a readable string of the current longitude data 682 | :return: string 683 | """ 684 | lat_string = str(self.longitude[0]) + '° ' + str(self.longitude[1]) + "' " + str(self.longitude[2]) 685 | return lat_string 686 | 687 | def speed_string(self, unit='kph'): 688 | """ 689 | Creates a readable string of the current speed data in one of three units 690 | :param unit: string of 'kph','mph, or 'knot' 691 | :return: 692 | """ 693 | if unit == 'mph': 694 | speed_string = str(self.speed[1]) + ' mph' 695 | 696 | elif unit == 'knot': 697 | if self.speed[0] == 1: 698 | unit_str = ' knot' 699 | else: 700 | unit_str = ' knots' 701 | speed_string = str(self.speed[0]) + unit_str 702 | 703 | else: 704 | speed_string = str(self.speed[2]) + ' km/h' 705 | 706 | return speed_string 707 | 708 | def date_string(self, formatting='s_mdy', century='20'): 709 | """ 710 | Creates a readable string of the current date. 711 | Can select between long format: Januray 1st, 2014 712 | or two short formats: 713 | 11/01/2014 (MM/DD/YYYY) 714 | 01/11/2014 (DD/MM/YYYY) 715 | :param formatting: string 's_mdy', 's_dmy', or 'long' 716 | :param century: int delineating the century the GPS data is from (19 for 19XX, 20 for 20XX) 717 | :return: date_string string with long or short format date 718 | """ 719 | 720 | # Long Format Januray 1st, 2014 721 | if formatting == 'long': 722 | # Retrieve Month string from private set 723 | month = self.__MONTHS[self.date[1] - 1] 724 | 725 | # Determine Date Suffix 726 | if self.date[0] in (1, 21, 31): 727 | suffix = 'st' 728 | elif self.date[0] in (2, 22): 729 | suffix = 'nd' 730 | elif self.date[0] == 3: 731 | suffix = 'rd' 732 | else: 733 | suffix = 'th' 734 | 735 | day = str(self.date[0]) + suffix # Create Day String 736 | 737 | year = century + str(self.date[2]) # Create Year String 738 | 739 | date_string = month + ' ' + day + ', ' + year # Put it all together 740 | 741 | else: 742 | # Add leading zeros to day string if necessary 743 | if self.date[0] < 10: 744 | day = '0' + str(self.date[0]) 745 | else: 746 | day = str(self.date[0]) 747 | 748 | # Add leading zeros to month string if necessary 749 | if self.date[1] < 10: 750 | month = '0' + str(self.date[1]) 751 | else: 752 | month = str(self.date[1]) 753 | 754 | # Add leading zeros to year string if necessary 755 | if self.date[2] < 10: 756 | year = '0' + str(self.date[2]) 757 | else: 758 | year = str(self.date[2]) 759 | 760 | # Build final string based on desired formatting 761 | if formatting == 's_dmy': 762 | date_string = day + '/' + month + '/' + year 763 | 764 | else: # Default date format 765 | date_string = month + '/' + day + '/' + year 766 | 767 | return date_string 768 | 769 | # All the currently supported NMEA sentences 770 | supported_sentences = {'GPRMC': gprmc, 'GPGGA': gpgga, 'GPVTG': gpvtg, 'GPGSA': gpgsa, 'GPGSV': gpgsv, 771 | 'GPGLL': gpgll} 772 | 773 | if __name__ == "__main__": 774 | 775 | sentence_count = 0 776 | 777 | test_RMC = ['$GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62\n', 778 | '$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A\n', 779 | '$GPRMC,225446,A,4916.45,N,12311.12,W,000.5,054.7,191194,020.3,E*68\n', 780 | '$GPRMC,180041.896,A,3749.1851,N,08338.7891,W,001.9,154.9,240911,,,A*7A\n', 781 | '$GPRMC,180049.896,A,3749.1808,N,08338.7869,W,001.8,156.3,240911,,,A*70\n', 782 | '$GPRMC,092751.000,A,5321.6802,N,00630.3371,W,0.06,31.66,280511,,,A*45\n'] 783 | 784 | test_VTG = ['$GPVTG,232.9,T,,M,002.3,N,004.3,K,A*01\n'] 785 | test_GGA = ['$GPGGA,180050.896,3749.1802,N,08338.7865,W,1,07,1.1,397.4,M,-32.5,M,,0000*6C\n'] 786 | test_GSA = ['$GPGSA,A,3,07,11,28,24,26,08,17,,,,,,2.0,1.1,1.7*37\n', 787 | '$GPGSA,A,3,07,02,26,27,09,04,15,,,,,,1.8,1.0,1.5*33\n'] 788 | test_GSV = ['$GPGSV,3,1,12,28,72,355,39,01,52,063,33,17,51,272,44,08,46,184,38*74\n', 789 | '$GPGSV,3,2,12,24,42,058,33,11,34,053,33,07,20,171,40,20,15,116,*71\n', 790 | '$GPGSV,3,3,12,04,12,204,34,27,11,324,35,32,11,089,,26,10,264,40*7B\n', 791 | '$GPGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,00,13,06,292,00*74\n', 792 | '$GPGSV,3,2,11,14,25,170,00,16,57,208,39,18,67,296,40,19,40,246,00*74\n', 793 | '$GPGSV,3,3,11,22,42,067,42,24,14,311,43,27,05,244,00,,,,*4D\n', 794 | '$GPGSV,4,1,14,22,81,349,25,14,64,296,22,18,54,114,21,51,40,212,*7D\n', 795 | '$GPGSV,4,2,14,24,30,047,22,04,22,312,26,31,22,204,,12,19,088,23*72\n', 796 | '$GPGSV,4,3,14,25,17,127,18,21,16,175,,11,09,315,16,19,05,273,*72\n', 797 | '$GPGSV,4,4,14,32,05,303,,15,02,073,*7A\n'] 798 | test_GLL = ['$GPGLL,3711.0942,N,08671.4472,W,000812.000,A,A*46\n', 799 | '$GPGLL,4916.45,N,12311.12,W,225444,A,*1D\n', 800 | '$GPGLL,4250.5589,S,14718.5084,E,092204.999,A*2D\n', 801 | '$GPGLL,0000.0000,N,00000.0000,E,235947.000,V*2D\n'] 802 | 803 | my_gps = MicropyGPS() 804 | my_gps.start_logging('test.txt', mode="new") 805 | my_gps.write_log('micropyGPS test log\n') 806 | sentence = '' 807 | for RMC_sentence in test_RMC: 808 | sentence_count += 1 809 | for y in RMC_sentence: 810 | sentence = my_gps.update(y) 811 | print('Parsed a', sentence, 'Sentence') 812 | print('Parsed Strings:', my_gps.gps_segments) 813 | print('Sentence CRC Value:', hex(my_gps.crc_xor)) 814 | print('Longitude:', my_gps.longitude) 815 | print('Latitude', my_gps.latitude) 816 | print('UTC Timestamp:', my_gps.timestamp) 817 | print('Speed:', my_gps.speed) 818 | print('Date Stamp:', my_gps.date) 819 | print('Course', my_gps.course) 820 | print('Data is Valid:', my_gps.valid) 821 | print('Compass Direction:', my_gps.compass_direction()) 822 | print('') 823 | 824 | for GLL_sentence in test_GLL: 825 | sentence_count += 1 826 | for y in GLL_sentence: 827 | sentence = my_gps.update(y) 828 | print('Parsed a', sentence, 'Sentence') 829 | print('Parsed Strings', my_gps.gps_segments) 830 | print('Sentence CRC Value:', hex(my_gps.crc_xor)) 831 | print('Longitude:', my_gps.longitude) 832 | print('Latitude', my_gps.latitude) 833 | print('UTC Timestamp:', my_gps.timestamp) 834 | print('Data is Valid:', my_gps.valid) 835 | print('') 836 | 837 | for VTG_sentence in test_VTG: 838 | sentence_count += 1 839 | for y in VTG_sentence: 840 | sentence = my_gps.update(y) 841 | print('Parsed a', sentence, 'Sentence') 842 | print('Parsed Strings', my_gps.gps_segments) 843 | print('Sentence CRC Value:', hex(my_gps.crc_xor)) 844 | print('Speed:', my_gps.speed) 845 | print('Course', my_gps.course) 846 | print('Compass Direction:', my_gps.compass_direction()) 847 | print('') 848 | 849 | for GGA_sentence in test_GGA: 850 | sentence_count += 1 851 | for y in GGA_sentence: 852 | sentence = my_gps.update(y) 853 | print('Parsed a', sentence, 'Sentence') 854 | print('Parsed Strings', my_gps.gps_segments) 855 | print('Sentence CRC Value:', hex(my_gps.crc_xor)) 856 | print('Longitude', my_gps.longitude) 857 | print('Latitude', my_gps.latitude) 858 | print('UTC Timestamp:', my_gps.timestamp) 859 | print('Fix Status:', my_gps.fix_stat) 860 | print('Altitude:', my_gps.altitude) 861 | print('Height Above Geoid:', my_gps.geoid_height) 862 | print('Horizontal Dilution of Precision:', my_gps.hdop) 863 | print('Satellites in Use by Receiver:', my_gps.satellites_in_use) 864 | print('') 865 | 866 | for GSA_sentence in test_GSA: 867 | sentence_count += 1 868 | for y in GSA_sentence: 869 | sentence = my_gps.update(y) 870 | print('Parsed a', sentence, 'Sentence') 871 | print('Parsed Strings', my_gps.gps_segments) 872 | print('Sentence CRC Value:', hex(my_gps.crc_xor)) 873 | print('Satellites Used', my_gps.satellites_used) 874 | print('Fix Type Code:', my_gps.fix_type) 875 | print('Horizontal Dilution of Precision:', my_gps.hdop) 876 | print('Vertical Dilution of Precision:', my_gps.vdop) 877 | print('Position Dilution of Precision:', my_gps.pdop) 878 | print('') 879 | 880 | for GSV_sentence in test_GSV: 881 | sentence_count += 1 882 | for y in GSV_sentence: 883 | sentence = my_gps.update(y) 884 | print('Parsed a', sentence, 'Sentence') 885 | print('Parsed Strings', my_gps.gps_segments) 886 | print('Sentence CRC Value:', hex(my_gps.crc_xor)) 887 | print('SV Sentences Parsed', my_gps.last_sv_sentence) 888 | print('SV Sentences in Total', my_gps.total_sv_sentences) 889 | print('# of Satellites in View:', my_gps.satellites_in_view) 890 | data_valid = my_gps.satellite_data_updated() 891 | print('Is Satellite Data Valid?:', data_valid) 892 | if data_valid: 893 | print('Satellite Data:', my_gps.satellite_data) 894 | print('Satellites Visible:', my_gps.satellites_visible()) 895 | print('') 896 | 897 | print("Pretty Print Examples:") 898 | print('Latitude:', my_gps.latitude_string()) 899 | print('Longitude:', my_gps.longitude_string()) 900 | print('Speed:', my_gps.speed_string('kph'), 'or', my_gps.speed_string('mph'), 'or', my_gps.speed_string('knot')) 901 | print('Date (Long Format):', my_gps.date_string('long')) 902 | print('Date (Short D/M/Y Format):', my_gps.date_string('s_dmy')) 903 | print('Date (Short M/D/Y Format):', my_gps.date_string('s_mdy')) 904 | print() 905 | 906 | print('### Final Results ###') 907 | print('Sentences Attempted:', sentence_count) 908 | print('Sentences Found:', my_gps.clean_sentences) 909 | print('Sentences Parsed:', my_gps.parsed_sentences) 910 | print('CRC_Fails:', my_gps.crc_fails) 911 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-abp_node_GPS/lib/readme.md: -------------------------------------------------------------------------------- 1 | Source of the micropyGPS library: https://github.com/inmcm/micropyGPS 2 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-abp_node_GPS/lib/tools.py: -------------------------------------------------------------------------------- 1 | from micropyGPS import MicropyGPS 2 | from machine import UART 3 | from machine import SD 4 | from machine import RTC 5 | import os 6 | import time 7 | import config 8 | import array 9 | from network import WLAN 10 | from network import LoRa 11 | import socket 12 | import binascii 13 | import struct 14 | 15 | def connect_wifi(): 16 | wlan = WLAN() 17 | wlan.init(mode=WLAN.STA) 18 | wlan.ifconfig(config=(config.WIFI_IP, config.WIFI_SUBNET, config.WIFI_GATEWAY, config.WIFI_DNS1)) 19 | wlan.connect(config.WIFI_SSID, auth=(WLAN.WPA2, config.WIFI_PASS), timeout=5000) 20 | return wlan.ifconfig() 21 | 22 | def mount_sd(): 23 | sd = SD() 24 | os.mount(sd, '/sd/') 25 | return os.listdir('/sd') 26 | 27 | def start_GPS(): 28 | com = UART(1,pins=("P11","P12"), baudrate=9600) 29 | my_gps = MicropyGPS() 30 | if config.LOG == 1: 31 | mount_sd() 32 | my_gps.start_logging('/sd/log.txt') 33 | my_gps.write_log('Restarted logging') 34 | 35 | while True: 36 | if com.any(): 37 | my_sentence = com.readline() 38 | for x in my_sentence: 39 | my_gps.update(chr(x)) 40 | print('Latitude: ' + my_gps.latitude_string() + ' Longitude: ' + my_gps.longitude_string() + ' Altitude: ' + str(my_gps.altitude)) 41 | print('Lat / Lon: ' + str(my_gps.latitude[0] + (my_gps.latitude[1] / 60)) + ', ' + str(my_gps.longitude[0] + (my_gps.longitude[1] / 60))) 42 | 43 | 44 | def convert_latlon(latitude, longitude, altitude, hdop): 45 | # latitude = -7.005941 46 | # longitude = -68.1192 47 | 48 | lat = int((latitude + 90)*10000) 49 | lon = int((longitude + 180)*10000) 50 | alt = int((altitude) * 10) 51 | lhdop = int((hdop) * 10) 52 | 53 | coords = array.array('B', [0,0,0,0,0,0,0,0,0]) 54 | coords[0] = lat 55 | coords[1] = (lat >> 8) 56 | coords[2] = (lat >> 16) 57 | 58 | coords[3] = lon 59 | coords[4] = (lon >> 8) 60 | coords[5] = (lon >> 16) 61 | 62 | coords[6] = alt 63 | coords[7] = (alt >> 8) 64 | coords[8] = lhdop 65 | 66 | return coords 67 | 68 | 69 | def get_GPS_values(): 70 | com = UART(1,pins=(config.TX, config.RX), baudrate=9600) 71 | my_gps = MicropyGPS() 72 | time.sleep(2) 73 | if com.any(): 74 | my_sentence = com.readline() 75 | for x in my_sentence: 76 | my_gps.update(chr(x)) 77 | gps_values = str(my_gps.latitude[0] + (my_gps.latitude[1] / 60)) + ',' + str(my_gps.longitude[0] + (my_gps.longitude[1] / 60)) 78 | return gps_values 79 | 80 | def get_GPS_array(): 81 | com = UART(1,pins=(config.TX, config.RX), baudrate=9600) 82 | my_gps = MicropyGPS() 83 | time.sleep(2) 84 | if com.any(): 85 | my_sentence = com.readline() 86 | for x in my_sentence: 87 | my_gps.update(chr(x)) 88 | gps_array = convert_latlon(my_gps.latitude[0] + (my_gps.latitude[1] / 60), my_gps.longitude[0] + (my_gps.longitude[1] / 60)) 89 | return gps_array 90 | 91 | 92 | def set_date_time(): 93 | com = UART(1,pins=(config.TX, config.RX), baudrate=9600) 94 | my_gps = MicropyGPS() 95 | time.sleep(2) 96 | if com.any(): 97 | my_sentence = com.readline() 98 | for x in my_sentence: 99 | my_gps.update(chr(x)) 100 | date = my_gps.date 101 | timestamp = my_gps.timestamp 102 | hour = timestamp[0] 103 | hour = hour + config.TIMEZONE 104 | if config.DAYLIGHT == 1: 105 | hour = hour + 1 106 | rtc = RTC(datetime=(int(date[2])+2000, int(date[1]), int(date[0]), int(timestamp[0]), int(timestamp[1]), int(timestamp[2]), 0, None)) 107 | return rtc 108 | 109 | def send_LORA(value): 110 | # Initialize LoRa in LORAWAN mode. 111 | lora = LoRa(mode=LoRa.LORAWAN) 112 | 113 | # create an ABP authentication params 114 | dev_addr = struct.unpack(">l", binascii.unhexlify(config.DEV_ADDR.replace(' ','')))[0] 115 | nwk_swkey = binascii.unhexlify(config.NWK_SWKEY.replace(' ','')) 116 | app_swkey = binascii.unhexlify(config.APP_SWKEY.replace(' ','')) 117 | 118 | # join a network using ABP (Activation By Personalization) 119 | lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey)) 120 | 121 | # remove all the non-default channels 122 | for i in range(3, 16): 123 | lora.remove_channel(i) 124 | 125 | # set the 3 default channels to the same frequency 126 | lora.add_channel(0, frequency=868100000, dr_min=0, dr_max=5) 127 | lora.add_channel(1, frequency=868100000, dr_min=0, dr_max=5) 128 | lora.add_channel(2, frequency=868100000, dr_min=0, dr_max=5) 129 | 130 | # create a LoRa socket 131 | s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 132 | 133 | # set the LoRaWAN data rate 134 | s.setsockopt(socket.SOL_LORA, socket.SO_DR, 5) 135 | 136 | # make the socket blocking 137 | s.setblocking(False) 138 | 139 | s.send(bytes(value)) 140 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-abp_node_GPS/main.py: -------------------------------------------------------------------------------- 1 | from micropyGPS import MicropyGPS 2 | from machine import UART 3 | from network import LoRa 4 | import socket 5 | import binascii 6 | import struct 7 | import time 8 | import config 9 | import tools 10 | 11 | DEBUG = config.DEBUG 12 | 13 | speed = config.AUTO 14 | update = config.UPDATE[speed] # seconds between update 15 | 16 | # Initialize GPS 17 | com = UART(1,pins=(config.TX, config.RX), baudrate=9600) 18 | my_gps = MicropyGPS() 19 | 20 | # Initialize LoRa in LORAWAN mode. 21 | lora = LoRa(mode=LoRa.LORAWAN) 22 | 23 | # create an ABP authentication params 24 | dev_addr = struct.unpack(">l", binascii.unhexlify(config.DEV_ADDR.replace(' ','')))[0] 25 | nwk_swkey = binascii.unhexlify(config.NWK_SWKEY.replace(' ','')) 26 | app_swkey = binascii.unhexlify(config.APP_SWKEY.replace(' ','')) 27 | 28 | # join a network using ABP (Activation By Personalization) 29 | lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey)) 30 | 31 | # remove all the non-default channels 32 | for i in range(3, 16): 33 | lora.remove_channel(i) 34 | 35 | # set the 3 default channels to the same frequency 36 | lora.add_channel(0, frequency=868100000, dr_min=0, dr_max=5) 37 | lora.add_channel(1, frequency=868100000, dr_min=0, dr_max=5) 38 | lora.add_channel(2, frequency=868100000, dr_min=0, dr_max=5) 39 | 40 | # create a LoRa socket 41 | s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 42 | 43 | # set the LoRaWAN data rate 44 | s.setsockopt(socket.SOL_LORA, socket.SO_DR, 5) 45 | 46 | # make the socket blocking 47 | s.setblocking(False) 48 | 49 | # set the socket port 50 | if DEBUG==1: 51 | s.bind(config.TTN_FPort_debug) 52 | else: 53 | s.bing(config.TTN_FPort) 54 | 55 | while True: 56 | if my_gps.fix_stat > 0 and my_gps.latitude[0] > 0: 57 | pycom.rgbled(0x007f700) # green 58 | if com.any(): 59 | my_sentence = com.readline() 60 | for x in my_sentence: 61 | my_gps.update(chr(x)) 62 | if DEBUG == 1: 63 | print('Longitude', my_gps.longitude); 64 | print('Latitude', my_gps.latitude); 65 | print('UTC Timestamp:', my_gps.timestamp); 66 | print('Fix Status:', my_gps.fix_stat); 67 | print('Altitude:', my_gps.altitude); 68 | print('Horizontal Dilution of Precision:', my_gps.hdop) 69 | print('Satellites in Use by Receiver:', my_gps.satellites_in_use) 70 | print('Speed in km/hour:', int(my_gps.speed[2])) 71 | gps_speed = int(my_gps.speed[2]) 72 | if (my_gps.fix_stat > 0 and my_gps.latitude[0] > 0) or DEBUG == 1: 73 | gps_array = tools.convert_latlon(my_gps.latitude[0] + (my_gps.latitude[1] / 60), my_gps.longitude[0] + (my_gps.longitude[1] / 60), my_gps.altitude, my_gps.hdop) 74 | print(gps_array) 75 | s.send(gps_array) 76 | s.settimeout(3.0) # configure a timeout value of 3 seconds 77 | # get any data received (if any...) 78 | set_speed = -1 79 | try: 80 | data = s.recv(1) 81 | print(data) 82 | set_speed = int(data[0]) 83 | print("set_speed = " + str(set_speed)) 84 | if (set_speed > -1 and set_speed < 5): 85 | speed = set_speed 86 | update = config.UPDATE[speed] 87 | print("Update interval set to: " + str(update) + " seconds") 88 | print("Speed type set to: " + str(speed)) 89 | except socket.timeout: 90 | # nothing received 91 | if (DEBUG == 1): 92 | print("No RX downlink data received") 93 | time.sleep(.5) 94 | pycom.rgbled(0) 95 | if (speed == config.AUTO): 96 | if (gps_speed < config.MAXSPEED[1]): 97 | speed_type = config.STAT 98 | elif (gps_speed < config.MAXSPEED[2]): 99 | speed_type = config.WALK 100 | elif (gps_speed < config.MAXSPEED[3]): 101 | speed_type = config.CYCLE 102 | else: 103 | speed_type = config.CAR 104 | update = config.UPDATE[speed_type] 105 | print("Update interval set to: " + str(update) + " seconds") 106 | print("Speed type = " + str(speed)) 107 | time.sleep(update - 8.5) # account for all the other sleep commands 108 | time.sleep(5) 109 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-abp_node_GPS/payload_decoder_ttn.txt: -------------------------------------------------------------------------------- 1 | function Decoder(bytes, port) { 2 | 3 | var decoded = {}; 4 | var str = ''; 5 | var length = 0; 6 | for (var i = 0; i < bytes.length-1; i += 1) 7 | str += (bytes[i] + ',') ; 8 | length = i; 9 | str += bytes[length]; 10 | 11 | decoded.raw = bytes; 12 | decoded.hexstring = str; 13 | decoded.latitude = (parseInt(bytes[0] + (bytes[1] << 8) + (bytes[2] << 16 )) / 10000) - 90; 14 | decoded.latitude = Math.round(decoded.latitude * 1000000) / 1000000; 15 | decoded.longitude = (parseInt(bytes[3] + (bytes[4] << 8) + (bytes[5] << 16 )) / 10000) - 180; 16 | decoded.longitude = Math.round(decoded.longitude * 1000000) / 1000000; 17 | decoded.altitude = (bytes[6] + (bytes[7] << 8) )/ 10; 18 | decoded.hdop = bytes[8] / 10; 19 | 20 | 21 | return decoded; 22 | } 23 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-lorawan-nano-gateway/inc/config.py: -------------------------------------------------------------------------------- 1 | """ LoPy LoRaWAN Nano Gateway configuration options """ 2 | 3 | GATEWAY_ID = '11aa334455bb7788' 4 | 5 | SERVER = 'router.eu.thethings.network' 6 | PORT = 1700 7 | 8 | NTP = "pool.ntp.org" 9 | NTP_PERIOD_S = 3600 10 | 11 | WIFI_SSID = 'my-wifi' 12 | WIFI_PASS = 'my-wifi-password' 13 | 14 | LORA_FREQUENCY = 868100000 15 | LORA_DR = "SF7BW125" # DR_5 16 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-lorawan-nano-gateway/inc/nanogateway.py: -------------------------------------------------------------------------------- 1 | """ LoPy Nano Gateway class """ 2 | 3 | from network import WLAN 4 | from network import LoRa 5 | from machine import Timer 6 | import os 7 | import binascii 8 | import machine 9 | import json 10 | import time 11 | import errno 12 | import _thread 13 | import socket 14 | 15 | 16 | PROTOCOL_VERSION = const(2) 17 | 18 | PUSH_DATA = const(0) 19 | PUSH_ACK = const(1) 20 | PULL_DATA = const(2) 21 | PULL_ACK = const(4) 22 | PULL_RESP = const(3) 23 | 24 | TX_ERR_NONE = "NONE" 25 | TX_ERR_TOO_LATE = "TOO_LATE" 26 | TX_ERR_TOO_EARLY = "TOO_EARLY" 27 | TX_ERR_COLLISION_PACKET = "COLLISION_PACKET" 28 | TX_ERR_COLLISION_BEACON = "COLLISION_BEACON" 29 | TX_ERR_TX_FREQ = "TX_FREQ" 30 | TX_ERR_TX_POWER = "TX_POWER" 31 | TX_ERR_GPS_UNLOCKED = "GPS_UNLOCKED" 32 | 33 | STAT_PK = {"stat": {"time": "", "lati": 0, 34 | "long": 0, "alti": 0, 35 | "rxnb": 0, "rxok": 0, 36 | "rxfw": 0, "ackr": 100.0, 37 | "dwnb": 0, "txnb": 0}} 38 | 39 | RX_PK = {"rxpk": [{"time": "", "tmst": 0, 40 | "chan": 0, "rfch": 0, 41 | "freq": 868.1, "stat": 1, 42 | "modu": "LORA", "datr": "SF7BW125", 43 | "codr": "4/5", "rssi": 0, 44 | "lsnr": 0, "size": 0, 45 | "data": ""}]} 46 | 47 | TX_ACK_PK = {"txpk_ack":{"error":""}} 48 | 49 | 50 | class NanoGateway: 51 | 52 | def __init__(self, id, frequency, datarate, ssid, password, server, port, ntp='pool.ntp.org', ntp_period=3600): 53 | self.id = id 54 | self.frequency = frequency 55 | self.sf = self._dr_to_sf(datarate) 56 | self.ssid = ssid 57 | self.password = password 58 | self.server = server 59 | self.port = port 60 | self.ntp = ntp 61 | self.ntp_period = ntp_period 62 | 63 | self.rxnb = 0 64 | self.rxok = 0 65 | self.rxfw = 0 66 | self.dwnb = 0 67 | self.txnb = 0 68 | 69 | self.stat_alarm = None 70 | self.pull_alarm = None 71 | self.uplink_alarm = None 72 | 73 | self.udp_lock = _thread.allocate_lock() 74 | 75 | self.lora = None 76 | self.lora_sock = None 77 | 78 | def start(self): 79 | # Change WiFi to STA mode and connect 80 | self.wlan = WLAN(mode=WLAN.STA) 81 | self._connect_to_wifi() 82 | 83 | # Get a time Sync 84 | self.rtc = machine.RTC() 85 | self.rtc.ntp_sync(self.ntp, update_period=self.ntp_period) 86 | 87 | # Get the server IP and create an UDP socket 88 | self.server_ip = socket.getaddrinfo(self.server, self.port)[0][-1] 89 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) 90 | self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 91 | self.sock.setblocking(False) 92 | 93 | # Push the first time immediatelly 94 | self._push_data(self._make_stat_packet()) 95 | 96 | # Create the alarms 97 | self.stat_alarm = Timer.Alarm(handler=lambda t: self._push_data(self._make_stat_packet()), s=60, periodic=True) 98 | self.pull_alarm = Timer.Alarm(handler=lambda u: self._pull_data(), s=25, periodic=True) 99 | 100 | # Start the UDP receive thread 101 | _thread.start_new_thread(self._udp_thread, ()) 102 | 103 | # Initialize LoRa in LORA mode 104 | self.lora = LoRa(mode=LoRa.LORA, frequency=self.frequency, bandwidth=LoRa.BW_125KHZ, sf=self.sf, 105 | preamble=8, coding_rate=LoRa.CODING_4_5, tx_iq=True) 106 | # Create a raw LoRa socket 107 | self.lora_sock = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 108 | self.lora_sock.setblocking(False) 109 | self.lora_tx_done = False 110 | 111 | self.lora.callback(trigger=(LoRa.RX_PACKET_EVENT | LoRa.TX_PACKET_EVENT), handler=self._lora_cb) 112 | 113 | def stop(self): 114 | # TODO: Check how to stop the NTP sync 115 | # TODO: Create a cancel method for the alarm 116 | # TODO: kill the UDP thread 117 | self.sock.close() 118 | 119 | def _connect_to_wifi(self): 120 | self.wlan.connect(self.ssid, auth=(None, self.password)) 121 | while not self.wlan.isconnected(): 122 | time.sleep(0.5) 123 | print("WiFi connected!") 124 | 125 | def _dr_to_sf(self, dr): 126 | sf = dr[2:4] 127 | if sf[1] not in '0123456789': 128 | sf = sf[:1] 129 | return int(sf) 130 | 131 | def _sf_to_dr(self, sf): 132 | return "SF7BW125" 133 | 134 | def _make_stat_packet(self): 135 | now = self.rtc.now() 136 | STAT_PK["stat"]["time"] = "%d-%02d-%02d %02d:%02d:%02d GMT" % (now[0], now[1], now[2], now[3], now[4], now[5]) 137 | STAT_PK["stat"]["rxnb"] = self.rxnb 138 | STAT_PK["stat"]["rxok"] = self.rxok 139 | STAT_PK["stat"]["rxfw"] = self.rxfw 140 | STAT_PK["stat"]["dwnb"] = self.dwnb 141 | STAT_PK["stat"]["txnb"] = self.txnb 142 | return json.dumps(STAT_PK) 143 | 144 | def _make_node_packet(self, rx_data, rx_time, tmst, sf, rssi, snr): 145 | RX_PK["rxpk"][0]["time"] = "%d-%02d-%02dT%02d:%02d:%02d.%dZ" % (rx_time[0], rx_time[1], rx_time[2], rx_time[3], rx_time[4], rx_time[5], rx_time[6]) 146 | RX_PK["rxpk"][0]["tmst"] = tmst 147 | RX_PK["rxpk"][0]["datr"] = self._sf_to_dr(sf) 148 | RX_PK["rxpk"][0]["rssi"] = rssi 149 | RX_PK["rxpk"][0]["lsnr"] = float(snr) 150 | RX_PK["rxpk"][0]["data"] = binascii.b2a_base64(rx_data)[:-1] 151 | RX_PK["rxpk"][0]["size"] = len(rx_data) 152 | return json.dumps(RX_PK) 153 | 154 | def _push_data(self, data): 155 | token = os.urandom(2) 156 | packet = bytes([PROTOCOL_VERSION]) + token + bytes([PUSH_DATA]) + binascii.unhexlify(self.id) + data 157 | with self.udp_lock: 158 | try: 159 | self.sock.sendto(packet, self.server_ip) 160 | except Exception: 161 | print("PUSH exception") 162 | 163 | def _pull_data(self): 164 | token = os.urandom(2) 165 | packet = bytes([PROTOCOL_VERSION]) + token + bytes([PULL_DATA]) + binascii.unhexlify(self.id) 166 | with self.udp_lock: 167 | try: 168 | self.sock.sendto(packet, self.server_ip) 169 | except Exception: 170 | print("PULL exception") 171 | 172 | def _ack_pull_rsp(self, token, error): 173 | TX_ACK_PK["txpk_ack"]["error"] = error 174 | resp = json.dumps(TX_ACK_PK) 175 | packet = bytes([PROTOCOL_VERSION]) + token + bytes([PULL_ACK]) + binascii.unhexlify(self.id) + resp 176 | with self.udp_lock: 177 | try: 178 | self.sock.sendto(packet, self.server_ip) 179 | except Exception: 180 | print("PULL RSP ACK exception") 181 | 182 | def _lora_cb(self, lora): 183 | events = lora.events() 184 | if events & LoRa.RX_PACKET_EVENT: 185 | self.rxnb += 1 186 | self.rxok += 1 187 | rx_data = self.lora_sock.recv(256) 188 | stats = lora.stats() 189 | self._push_data(self._make_node_packet(rx_data, self.rtc.now(), stats.timestamp, stats.sf, stats.rssi, stats.snr)) 190 | self.rxfw += 1 191 | if events & LoRa.TX_PACKET_EVENT: 192 | self.txnb += 1 193 | lora.init(mode=LoRa.LORA, frequency=self.frequency, bandwidth=LoRa.BW_125KHZ, 194 | sf=self.sf, preamble=8, coding_rate=LoRa.CODING_4_5, tx_iq=True) 195 | 196 | def _send_down_link(self, data, tmst, datarate, frequency): 197 | self.lora.init(mode=LoRa.LORA, frequency=frequency, bandwidth=LoRa.BW_125KHZ, 198 | sf=self._dr_to_sf(datarate), preamble=8, coding_rate=LoRa.CODING_4_5, 199 | tx_iq=True) 200 | while time.ticks_us() < tmst: 201 | pass 202 | self.lora_sock.send(data) 203 | 204 | def _udp_thread(self): 205 | while True: 206 | try: 207 | data, src = self.sock.recvfrom(1024) 208 | _token = data[1:3] 209 | _type = data[3] 210 | if _type == PUSH_ACK: 211 | print("Push ack") 212 | elif _type == PULL_ACK: 213 | print("Pull ack") 214 | elif _type == PULL_RESP: 215 | self.dwnb += 1 216 | ack_error = TX_ERR_NONE 217 | tx_pk = json.loads(data[4:]) 218 | tmst = tx_pk["txpk"]["tmst"] 219 | t_us = tmst - time.ticks_us() - 5000 220 | if t_us < 0: 221 | t_us += 0xFFFFFFFF 222 | if t_us < 20000000: 223 | self.uplink_alarm = Timer.Alarm(handler=lambda x: self._send_down_link(binascii.a2b_base64(tx_pk["txpk"]["data"]), 224 | tx_pk["txpk"]["tmst"] - 10, tx_pk["txpk"]["datr"], 225 | int(tx_pk["txpk"]["freq"] * 1000000)), us=t_us) 226 | else: 227 | ack_error = TX_ERR_TOO_LATE 228 | print("Downlink timestamp error!, t_us:", t_us) 229 | self._ack_pull_rsp(_token, ack_error) 230 | print("Pull rsp") 231 | except socket.timeout: 232 | pass 233 | except OSError as e: 234 | if e.errno == errno.EAGAIN: 235 | pass 236 | else: 237 | print("UDP recv OSError Exception") 238 | except Exception: 239 | print("UDP recv Exception") 240 | # Wait before trying to receive again 241 | time.sleep(0.025) 242 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-lorawan-nano-gateway/main.py: -------------------------------------------------------------------------------- 1 | """ LoPy LoRaWAN Nano Gateway example usage """ 2 | 3 | import config 4 | from nanogateway import NanoGateway 5 | 6 | 7 | nanogw = NanoGateway(id=config.GATEWAY_ID, frequency=config.LORA_FREQUENCY, 8 | datarate=config.LORA_DR, ssid=config.WIFI_SSID, 9 | password=config.WIFI_PASS, server=config.SERVER, 10 | port=config.PORT, ntp=config.NTP, ntp_period=config.NTP_PERIOD_S) 11 | 12 | nanogw.start() 13 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-otaa_node-cpg-sensor/Circuit Playground/CPG_Serial_hardware_sensors/CPG_Serial_hardware_sensors.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | #define TEMP_INPUT A0 // - A0 = temperature sensor / thermistor 7 | #define SOUND_INPUT A4 // - A4 = sound sensor / microphone 8 | #define LIGHT_INPUT A5 // - A5 = light sensor 9 | 10 | String Data = ""; 11 | 12 | void setup() { 13 | CircuitPlayground.begin(); 14 | // initialize serial ports 15 | Serial.begin(9600); // USB serial port 0 16 | Serial1.begin(9600); // serial port 1, TX/RX 17 | while (!Serial) { 18 | ; // wait for serial port to connect. Needed for native USB port only 19 | } 20 | 21 | Serial.println("Setup complete"); 22 | } 23 | 24 | byte rx_byte = 0; // stores received byte 25 | int x = 0; 26 | String returnvalue = ""; 27 | 28 | void loop() { 29 | while (Serial1.available()) 30 | { 31 | 32 | char character = Serial1.read(); // Receive a single character from the software serial port 33 | if (isDigit(character)) { 34 | Data.concat(character); // Add the received character to the receive buffer, wait for end of line 35 | } 36 | if (character == '\n') 37 | { 38 | int x = Data.toInt(); 39 | Serial.print("Received: "); 40 | Serial.println(x); 41 | 42 | if(x > 0 && x <= 10) { 43 | uint16_t tempvalue = analogRead(TEMP_INPUT); 44 | Serial.print("TEMP_INPUT: "); 45 | Serial.println(tempvalue, DEC); 46 | String tempstring = String(tempvalue, HEX); 47 | if (tempvalue < 256) tempstring = "0" + tempstring; 48 | if (tempvalue < 16) tempstring = "0" + tempstring; 49 | 50 | uint16_t soundvalue = analogRead(SOUND_INPUT); 51 | Serial.print("SOUND_INPUT: "); 52 | Serial.println(soundvalue, DEC); 53 | String soundstring = String(soundvalue, HEX); 54 | if (soundvalue < 256) soundstring = "0" + soundstring; 55 | if (soundvalue < 16) soundstring = "0" + soundstring; 56 | 57 | uint16_t lightvalue = analogRead(LIGHT_INPUT); 58 | Serial.print("LIGHT_INPUT: "); 59 | Serial.println(lightvalue, DEC); 60 | String lightstring = String(lightvalue, HEX); 61 | if (lightvalue < 256) lightstring = "0" + lightstring; 62 | if (lightvalue < 16) lightstring = "0" + lightstring; 63 | 64 | returnvalue = tempstring + soundstring + lightstring; 65 | 66 | CircuitPlayground.clearPixels(); 67 | for (int i=0; i < x; i++){ 68 | CircuitPlayground.setPixelColor(i, 0x0000FF); 69 | } 70 | } else { 71 | Serial.println("Received unknown input"); 72 | CircuitPlayground.setPixelColor(0, 0xFF0000); 73 | returnvalue = String('000000000'); 74 | } 75 | 76 | Serial1.println(returnvalue); 77 | Serial.print(" Returned: "); 78 | Serial.println(returnvalue); 79 | 80 | // Clear receive buffer so we're ready to receive the next line 81 | 82 | Data = ""; 83 | } 84 | } 85 | } 86 | 87 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-otaa_node-cpg-sensor/LoPy-flash/boot.py: -------------------------------------------------------------------------------- 1 | # boot.py -- run on boot-up 2 | import os 3 | import time 4 | import pycom 5 | import machine 6 | import config 7 | from network import WLAN 8 | from machine import UART 9 | 10 | uart = UART(0, 115200) 11 | os.dupterm(uart) 12 | 13 | uart1 = UART(1, 9600) 14 | 15 | pycom.heartbeat(False) 16 | pycom.rgbled(0x7f7f00) # yellow 17 | 18 | wlan = WLAN() # get current object, without changing the mode 19 | 20 | if machine.reset_cause() != machine.SOFT_RESET: 21 | wlan.init(mode=WLAN.STA) 22 | # configuration below MUST match your home router settings!! 23 | wlan.ifconfig(config=(config.WIFI_IP, config.WIFI_SUBNET, config.WIFI_GATEWAY, config.WIFI_DNS1)) 24 | 25 | nets = wlan.scan() 26 | 27 | for net in nets: 28 | if net.ssid == config.WIFI_SSID: 29 | if not wlan.isconnected(): 30 | wlan.connect(config.WIFI_SSID, auth=(WLAN.WPA2, config.WIFI_PASS), timeout=5000) 31 | while not wlan.isconnected(): 32 | machine.idle() # save power while waiting 33 | pycom.rgbled(0x007f00) # green 34 | time.sleep(3) 35 | pycom.rgbled(0) 36 | break 37 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-otaa_node-cpg-sensor/LoPy-flash/lib/config.py: -------------------------------------------------------------------------------- 1 | """ LoPy LoRaWAN Nodeconfiguration options """ 2 | # These you need to replace! 3 | DEV_EUI = '' 4 | APP_EUI = '' 5 | APP_KEY = '' 6 | 7 | 8 | # Leave them empty or unchanged if you don't want the 9 | # node to connect to WiFi 10 | WIFI_SSID = '' 11 | WIFI_PASS = '' 12 | WIFI_IP = '' # fixed IP 13 | WIFI_SUBNET = '' 14 | WIFI_GATEWAY = '' 15 | WIFI_DNS1 = '' 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-otaa_node-cpg-sensor/LoPy-flash/main.py: -------------------------------------------------------------------------------- 1 | from network import LoRa 2 | import socket 3 | import binascii 4 | import struct 5 | import time 6 | import config 7 | 8 | # Initialize LoRa in LORAWAN mode. 9 | lora = LoRa(mode=LoRa.LORAWAN) 10 | 11 | # create an ABP authentication params 12 | dev_eui = binascii.unhexlify(config.DEV_EUI.replace(' ','')) 13 | app_eui = binascii.unhexlify(config.APP_EUI.replace(' ','')) 14 | app_key = binascii.unhexlify(config.APP_KEY.replace(' ','')) 15 | 16 | # set the 3 default channels to the same frequency 17 | lora.add_channel(0, frequency=868100000, dr_min=0, dr_max=5) 18 | lora.add_channel(1, frequency=868100000, dr_min=0, dr_max=5) 19 | lora.add_channel(2, frequency=868100000, dr_min=0, dr_max=5) 20 | 21 | # join a network using OTAA 22 | lora.join(activation=LoRa.OTAA, auth=(dev_eui, app_eui, app_key), timeout=0) 23 | 24 | # wait until the module has joined the network 25 | while not lora.has_joined(): 26 | time.sleep(2.5) 27 | print('Not joined yet...') 28 | 29 | # remove all the non-default channels 30 | for i in range(3, 16): 31 | lora.remove_channel(i) 32 | 33 | # create a LoRa socket 34 | s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 35 | 36 | # set the LoRaWAN data rate 37 | s.setsockopt(socket.SOL_LORA, socket.SO_DR, 5) 38 | 39 | # make the socket blocking 40 | s.setblocking(False) 41 | 42 | i = 0 43 | 44 | while True: # runs forever 45 | if (i >= 10): 46 | i = 0 47 | i = i + 1 48 | sensor = '' + str(i) 49 | uart1.write(str(i)+'\n') 50 | time.sleep(3) 51 | if uart1.any() > 0: 52 | sensor = uart1.readall() # read the response from the Circuit Playground 53 | pycom.rgbled(0x007f700) # green 54 | s.send(bytes(sensor)) 55 | print('count: ' + str(i)) 56 | print('sending: ') 57 | print(sensor) 58 | time.sleep(.5) 59 | pycom.rgbled(0) 60 | time.sleep(3.5) 61 | rx = s.recv(256) 62 | if rx: 63 | pycom.rgbled(0x00007F) # blue 64 | print(rx) 65 | time.sleep(1) 66 | pycom.rgbled(0) 67 | time.sleep(30) -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-otaa_node/boot.py: -------------------------------------------------------------------------------- 1 | # boot.py -- run on boot-up 2 | import os 3 | import time 4 | import pycom 5 | import machine 6 | import config 7 | from network import WLAN 8 | from machine import UART 9 | 10 | uart = UART(0, 115200) 11 | os.dupterm(uart) 12 | 13 | pycom.heartbeat(False) 14 | pycom.rgbled(0x7f7f00) # yellow 15 | 16 | wlan = WLAN() # get current object, without changing the mode 17 | 18 | if machine.reset_cause() != machine.SOFT_RESET: 19 | wlan.init(mode=WLAN.STA) 20 | # configuration below MUST match your home router settings!! 21 | wlan.ifconfig(config=(config.WIFI_IP, config.WIFI_SUBNET, config.WIFI_GATEWAY, config.WIFI_DNS1)) 22 | 23 | nets = wlan.scan() 24 | 25 | for net in nets: 26 | if net.ssid == config.WIFI_SSID: 27 | if not wlan.isconnected(): 28 | wlan.connect(config.WIFI_SSID, auth=(WLAN.WPA2, config.WIFI_PASS), timeout=5000) 29 | while not wlan.isconnected(): 30 | machine.idle() # save power while waiting 31 | pycom.rgbled(0x007f00) # green 32 | time.sleep(3) 33 | pycom.rgbled(0) 34 | break 35 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-otaa_node/lib/config.py: -------------------------------------------------------------------------------- 1 | """ LoPy LoRaWAN Nodeconfiguration options """ 2 | # These you need to replace! 3 | DEV_EUI = '' 4 | APP_EUI = '' 5 | APP_KEY = '' 6 | 7 | 8 | # Leave them empty or unchanged if you don't want the 9 | # node to connect to WiFi 10 | WIFI_SSID = '' 11 | WIFI_PASS = '' 12 | WIFI_IP = '' # fixed IP 13 | WIFI_SUBNET = '' 14 | WIFI_GATEWAY = '' 15 | WIFI_DNS1 = '' 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /examples/lorawan-nano-gateway/TTN-otaa_node/main.py: -------------------------------------------------------------------------------- 1 | from network import LoRa 2 | import socket 3 | import binascii 4 | import struct 5 | import time 6 | import config 7 | 8 | # Initialize LoRa in LORAWAN mode. 9 | lora = LoRa(mode=LoRa.LORAWAN) 10 | 11 | # create an ABP authentication params 12 | dev_eui = binascii.unhexlify(config.DEV_EUI.replace(' ','')) 13 | app_eui = binascii.unhexlify(config.APP_EUI.replace(' ','')) 14 | app_key = binascii.unhexlify(config.APP_KEY.replace(' ','')) 15 | 16 | # set the 3 default channels to the same frequency 17 | lora.add_channel(0, frequency=868100000, dr_min=0, dr_max=5) 18 | lora.add_channel(1, frequency=868100000, dr_min=0, dr_max=5) 19 | lora.add_channel(2, frequency=868100000, dr_min=0, dr_max=5) 20 | 21 | # join a network using OTAA 22 | lora.join(activation=LoRa.OTAA, auth=(dev_eui, app_eui, app_key), timeout=0) 23 | 24 | # wait until the module has joined the network 25 | while not lora.has_joined(): 26 | time.sleep(2.5) 27 | print('Not joined yet...') 28 | 29 | # remove all the non-default channels 30 | for i in range(3, 16): 31 | lora.remove_channel(i) 32 | 33 | # create a LoRa socket 34 | s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) 35 | 36 | # set the LoRaWAN data rate 37 | s.setsockopt(socket.SOL_LORA, socket.SO_DR, 5) 38 | 39 | # make the socket blocking 40 | s.setblocking(False) 41 | 42 | for i in range (2000): 43 | pycom.rgbled(0x007f700) # green 44 | s.send(b'PKT #' + bytes([i])) 45 | print('sending ' + str(i)) 46 | time.sleep(.5) 47 | pycom.rgbled(0) 48 | time.sleep(3.5) 49 | rx = s.recv(256) 50 | if rx: 51 | pycom.rgbled(0x00007F) # blue 52 | print(rx) 53 | time.sleep(1) 54 | pycom.rgbled(0) 55 | time.sleep(30) -------------------------------------------------------------------------------- /examples/mqtt/boot.py: -------------------------------------------------------------------------------- 1 | from machine import UART 2 | import machine 3 | import os 4 | 5 | uart = UART(0, baudrate=115200) 6 | os.dupterm(uart) 7 | 8 | machine.main('main.py') 9 | -------------------------------------------------------------------------------- /examples/mqtt/main.py: -------------------------------------------------------------------------------- 1 | from network import WLAN 2 | from mqtt import MQTTClient 3 | import machine 4 | import time 5 | 6 | def settimeout(duration): 7 | pass 8 | 9 | wlan = WLAN(mode=WLAN.STA) 10 | wlan.antenna(WLAN.EXT_ANT) 11 | wlan.connect("yourwifinetwork", auth=(WLAN.WPA2, "wifipassword"), timeout=5000) 12 | 13 | while not wlan.isconnected(): 14 | machine.idle() 15 | 16 | print("Connected to Wifi\n") 17 | client = MQTTClient("demo", "broker.hivemq.com", port=1883) 18 | client.settimeout = settimeout 19 | client.connect() 20 | 21 | while True: 22 | print("Sending ON") 23 | client.publish("/lights", "ON") 24 | time.sleep(1) 25 | print("Sending OFF") 26 | client.publish("/lights", "OFF") 27 | time.sleep(1) 28 | -------------------------------------------------------------------------------- /examples/mqtt/mqtt.py: -------------------------------------------------------------------------------- 1 | import usocket as socket 2 | import ustruct as struct 3 | from ubinascii import hexlify 4 | 5 | class MQTTException(Exception): 6 | pass 7 | 8 | class MQTTClient: 9 | 10 | def __init__(self, client_id, server, port=0, user=None, password=None, keepalive=0, 11 | ssl=False, ssl_params={}): 12 | if port == 0: 13 | port = 8883 if ssl else 1883 14 | self.client_id = client_id 15 | self.sock = None 16 | self.addr = socket.getaddrinfo(server, port)[0][-1] 17 | self.ssl = ssl 18 | self.ssl_params = ssl_params 19 | self.pid = 0 20 | self.cb = None 21 | self.user = user 22 | self.pswd = password 23 | self.keepalive = keepalive 24 | self.lw_topic = None 25 | self.lw_msg = None 26 | self.lw_qos = 0 27 | self.lw_retain = False 28 | 29 | def _send_str(self, s): 30 | self.sock.write(struct.pack("!H", len(s))) 31 | self.sock.write(s) 32 | 33 | def _recv_len(self): 34 | n = 0 35 | sh = 0 36 | while 1: 37 | b = self.sock.read(1)[0] 38 | n |= (b & 0x7f) << sh 39 | if not b & 0x80: 40 | return n 41 | sh += 7 42 | 43 | def set_callback(self, f): 44 | self.cb = f 45 | 46 | def set_last_will(self, topic, msg, retain=False, qos=0): 47 | assert 0 <= qos <= 2 48 | assert topic 49 | self.lw_topic = topic 50 | self.lw_msg = msg 51 | self.lw_qos = qos 52 | self.lw_retain = retain 53 | 54 | def connect(self, clean_session=True): 55 | self.sock = socket.socket() 56 | self.sock.connect(self.addr) 57 | if self.ssl: 58 | import ussl 59 | self.sock = ussl.wrap_socket(self.sock, **self.ssl_params) 60 | msg = bytearray(b"\x10\0\0\x04MQTT\x04\x02\0\0") 61 | msg[1] = 10 + 2 + len(self.client_id) 62 | msg[9] = clean_session << 1 63 | if self.user is not None: 64 | msg[1] += 2 + len(self.user) + 2 + len(self.pswd) 65 | msg[9] |= 0xC0 66 | if self.keepalive: 67 | assert self.keepalive < 65536 68 | msg[10] |= self.keepalive >> 8 69 | msg[11] |= self.keepalive & 0x00FF 70 | if self.lw_topic: 71 | msg[1] += 2 + len(self.lw_topic) + 2 + len(self.lw_msg) 72 | msg[9] |= 0x4 | (self.lw_qos & 0x1) << 3 | (self.lw_qos & 0x2) << 3 73 | msg[9] |= self.lw_retain << 5 74 | self.sock.write(msg) 75 | #print(hex(len(msg)), hexlify(msg, ":")) 76 | self._send_str(self.client_id) 77 | if self.lw_topic: 78 | self._send_str(self.lw_topic) 79 | self._send_str(self.lw_msg) 80 | if self.user is not None: 81 | self._send_str(self.user) 82 | self._send_str(self.pswd) 83 | resp = self.sock.read(4) 84 | assert resp[0] == 0x20 and resp[1] == 0x02 85 | if resp[3] != 0: 86 | raise MQTTException(resp[3]) 87 | return resp[2] & 1 88 | 89 | def disconnect(self): 90 | self.sock.write(b"\xe0\0") 91 | self.sock.close() 92 | 93 | def ping(self): 94 | self.sock.write(b"\xc0\0") 95 | 96 | def publish(self, topic, msg, retain=False, qos=0): 97 | pkt = bytearray(b"\x30\0\0\0") 98 | pkt[0] |= qos << 1 | retain 99 | sz = 2 + len(topic) + len(msg) 100 | if qos > 0: 101 | sz += 2 102 | assert sz < 2097152 103 | i = 1 104 | while sz > 0x7f: 105 | pkt[i] = (sz & 0x7f) | 0x80 106 | sz >>= 7 107 | i += 1 108 | pkt[i] = sz 109 | #print(hex(len(pkt)), hexlify(pkt, ":")) 110 | self.sock.write(pkt, i + 1) 111 | self._send_str(topic) 112 | if qos > 0: 113 | self.pid += 1 114 | pid = self.pid 115 | struct.pack_into("!H", pkt, 0, pid) 116 | self.sock.write(pkt, 2) 117 | self.sock.write(msg) 118 | if qos == 1: 119 | while 1: 120 | op = self.wait_msg() 121 | if op == 0x40: 122 | sz = self.sock.read(1) 123 | assert sz == b"\x02" 124 | rcv_pid = self.sock.read(2) 125 | rcv_pid = rcv_pid[0] << 8 | rcv_pid[1] 126 | if pid == rcv_pid: 127 | return 128 | elif qos == 2: 129 | assert 0 130 | 131 | def subscribe(self, topic, qos=0): 132 | assert self.cb is not None, "Subscribe callback is not set" 133 | pkt = bytearray(b"\x82\0\0\0") 134 | self.pid += 1 135 | struct.pack_into("!BH", pkt, 1, 2 + 2 + len(topic) + 1, self.pid) 136 | #print(hex(len(pkt)), hexlify(pkt, ":")) 137 | self.sock.write(pkt) 138 | self._send_str(topic) 139 | self.sock.write(qos.to_bytes(1)) 140 | while 1: 141 | op = self.wait_msg() 142 | if op == 0x90: 143 | resp = self.sock.read(4) 144 | #print(resp) 145 | assert resp[1] == pkt[2] and resp[2] == pkt[3] 146 | if resp[3] == 0x80: 147 | raise MQTTException(resp[3]) 148 | return 149 | 150 | # Wait for a single incoming MQTT message and process it. 151 | # Subscribed messages are delivered to a callback previously 152 | # set by .set_callback() method. Other (internal) MQTT 153 | # messages processed internally. 154 | def wait_msg(self): 155 | res = self.sock.read(1) 156 | self.sock.setblocking(True) 157 | if res is None: 158 | return None 159 | if res == b"": 160 | raise OSError(-1) 161 | if res == b"\xd0": # PINGRESP 162 | sz = self.sock.read(1)[0] 163 | assert sz == 0 164 | return None 165 | op = res[0] 166 | if op & 0xf0 != 0x30: 167 | return op 168 | sz = self._recv_len() 169 | topic_len = self.sock.read(2) 170 | topic_len = (topic_len[0] << 8) | topic_len[1] 171 | topic = self.sock.read(topic_len) 172 | sz -= topic_len + 2 173 | if op & 6: 174 | pid = self.sock.read(2) 175 | pid = pid[0] << 8 | pid[1] 176 | sz -= 2 177 | msg = self.sock.read(sz) 178 | self.cb(topic, msg) 179 | if op & 6 == 2: 180 | pkt = bytearray(b"\x40\x02\0\0") 181 | struct.pack_into("!H", pkt, 2, pid) 182 | self.sock.write(pkt) 183 | elif op & 6 == 4: 184 | assert 0 185 | 186 | # Checks whether a pending message from server is available. 187 | # If not, returns immediately with None. Otherwise, does 188 | # the same processing as wait_msg. 189 | def check_msg(self): 190 | self.sock.setblocking(False) 191 | return self.wait_msg() 192 | 193 | -------------------------------------------------------------------------------- /examples/onewire/boot.py: -------------------------------------------------------------------------------- 1 | from machine import UART 2 | import machine 3 | import os 4 | 5 | uart = UART(0, baudrate=115200) 6 | os.dupterm(uart) 7 | 8 | machine.main('main.py') 9 | -------------------------------------------------------------------------------- /examples/onewire/main.py: -------------------------------------------------------------------------------- 1 | import time 2 | from machine import Pin 3 | from onewire import DS18X20 4 | from onewire import OneWire 5 | 6 | #DS18B20 data line connected to pin P10 7 | ow = OneWire(Pin('P10')) 8 | temp = DS18X20(ow) 9 | 10 | while True: 11 | print(temp.read_temp_async()) 12 | time.sleep(1) 13 | temp.start_convertion() 14 | time.sleep(1) 15 | -------------------------------------------------------------------------------- /examples/onewire/onewire.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | OneWire library for MicroPython 5 | """ 6 | 7 | import time 8 | import machine 9 | 10 | class OneWire: 11 | CMD_SEARCHROM = const(0xf0) 12 | CMD_READROM = const(0x33) 13 | CMD_MATCHROM = const(0x55) 14 | CMD_SKIPROM = const(0xcc) 15 | 16 | def __init__(self, pin): 17 | self.pin = pin 18 | self.pin.init(pin.OPEN_DRAIN, pin.PULL_UP) 19 | 20 | def reset(self): 21 | """ 22 | Perform the onewire reset function. 23 | Returns True if a device asserted a presence pulse, False otherwise. 24 | """ 25 | sleep_us = time.sleep_us 26 | disable_irq = machine.disable_irq 27 | enable_irq = machine.enable_irq 28 | pin = self.pin 29 | 30 | pin(0) 31 | sleep_us(480) 32 | i = disable_irq() 33 | pin(1) 34 | sleep_us(60) 35 | status = not pin() 36 | enable_irq(i) 37 | sleep_us(420) 38 | return status 39 | 40 | def read_bit(self): 41 | sleep_us = time.sleep_us 42 | enable_irq = machine.enable_irq 43 | pin = self.pin 44 | 45 | pin(1) # half of the devices don't match CRC without this line 46 | i = machine.disable_irq() 47 | pin(0) 48 | sleep_us(1) 49 | pin(1) 50 | sleep_us(1) 51 | value = pin() 52 | enable_irq(i) 53 | sleep_us(40) 54 | return value 55 | 56 | def read_byte(self): 57 | value = 0 58 | for i in range(8): 59 | value |= self.read_bit() << i 60 | return value 61 | 62 | def read_bytes(self, count): 63 | buf = bytearray(count) 64 | for i in range(count): 65 | buf[i] = self.read_byte() 66 | return buf 67 | 68 | def write_bit(self, value): 69 | sleep_us = time.sleep_us 70 | pin = self.pin 71 | 72 | i = machine.disable_irq() 73 | pin(0) 74 | sleep_us(1) 75 | pin(value) 76 | sleep_us(60) 77 | pin(1) 78 | sleep_us(1) 79 | machine.enable_irq(i) 80 | 81 | def write_byte(self, value): 82 | for i in range(8): 83 | self.write_bit(value & 1) 84 | value >>= 1 85 | 86 | def write_bytes(self, buf): 87 | for b in buf: 88 | self.write_byte(b) 89 | 90 | def select_rom(self, rom): 91 | """ 92 | Select a specific device to talk to. Pass in rom as a bytearray (8 bytes). 93 | """ 94 | self.reset() 95 | self.write_byte(CMD_MATCHROM) 96 | self.write_bytes(rom) 97 | 98 | def crc8(self, data): 99 | """ 100 | Compute CRC 101 | """ 102 | crc = 0 103 | for i in range(len(data)): 104 | byte = data[i] 105 | for b in range(8): 106 | fb_bit = (crc ^ byte) & 0x01 107 | if fb_bit == 0x01: 108 | crc = crc ^ 0x18 109 | crc = (crc >> 1) & 0x7f 110 | if fb_bit == 0x01: 111 | crc = crc | 0x80 112 | byte = byte >> 1 113 | return crc 114 | 115 | def scan(self): 116 | """ 117 | Return a list of ROMs for all attached devices. 118 | Each ROM is returned as a bytes object of 8 bytes. 119 | """ 120 | devices = [] 121 | diff = 65 122 | rom = False 123 | for i in range(0xff): 124 | rom, diff = self._search_rom(rom, diff) 125 | if rom: 126 | devices += [rom] 127 | if diff == 0: 128 | break 129 | return devices 130 | 131 | def _search_rom(self, l_rom, diff): 132 | if not self.reset(): 133 | return None, 0 134 | self.write_byte(CMD_SEARCHROM) 135 | if not l_rom: 136 | l_rom = bytearray(8) 137 | rom = bytearray(8) 138 | next_diff = 0 139 | i = 64 140 | for byte in range(8): 141 | r_b = 0 142 | for bit in range(8): 143 | b = self.read_bit() 144 | if self.read_bit(): 145 | if b: # there are no devices or there is an error on the bus 146 | return None, 0 147 | else: 148 | if not b: # collision, two devices with different bit meaning 149 | if diff > i or ((l_rom[byte] & (1 << bit)) and diff != i): 150 | b = 1 151 | next_diff = i 152 | self.write_bit(b) 153 | if b: 154 | r_b |= 1 << bit 155 | i -= 1 156 | rom[byte] = r_b 157 | return rom, next_diff 158 | 159 | class DS18X20(object): 160 | def __init__(self, onewire): 161 | self.ow = onewire 162 | self.roms = [rom for rom in self.ow.scan() if rom[0] == 0x10 or rom[0] == 0x28] 163 | self.fp = True 164 | try: 165 | 1/1 166 | except TypeError: 167 | self.fp = False # floatingpoint not supported 168 | 169 | def isbusy(self): 170 | """ 171 | Checks wether one of the DS18x20 devices on the bus is busy 172 | performing a temperature convertion 173 | """ 174 | return not self.ow.read_bit() 175 | 176 | def start_convertion(self, rom=None): 177 | """ 178 | Start the temp conversion on one DS18x20 device. 179 | Pass the 8-byte bytes object with the ROM of the specific device you want to read. 180 | If only one DS18x20 device is attached to the bus you may omit the rom parameter. 181 | """ 182 | if (rom==None) and (len(self.roms)>0): 183 | rom=self.roms[0] 184 | if rom!=None: 185 | rom = rom or self.roms[0] 186 | ow = self.ow 187 | ow.reset() 188 | ow.select_rom(rom) 189 | ow.write_byte(0x44) # Convert Temp 190 | 191 | def read_temp_async(self, rom=None): 192 | """ 193 | Read the temperature of one DS18x20 device if the convertion is complete, 194 | otherwise return None. 195 | """ 196 | if self.isbusy(): 197 | return None 198 | if (rom==None) and (len(self.roms)>0): 199 | rom=self.roms[0] 200 | if rom==None: 201 | return None 202 | else: 203 | ow = self.ow 204 | ow.reset() 205 | ow.select_rom(rom) 206 | ow.write_byte(0xbe) # Read scratch 207 | data = ow.read_bytes(9) 208 | return self.convert_temp(rom[0], data) 209 | 210 | def convert_temp(self, rom0, data): 211 | """ 212 | Convert the raw temperature data into degrees celsius and return as a fixed point with 2 decimal places. 213 | """ 214 | temp_lsb = data[0] 215 | temp_msb = data[1] 216 | if rom0 == 0x10: 217 | if temp_msb != 0: 218 | # convert negative number 219 | temp_read = temp_lsb >> 1 | 0x80 # truncate bit 0 by shifting, fill high bit with 1. 220 | temp_read = -((~temp_read + 1) & 0xff) # now convert from two's complement 221 | else: 222 | temp_read = temp_lsb >> 1 # truncate bit 0 by shifting 223 | count_remain = data[6] 224 | count_per_c = data[7] 225 | if self.fp: 226 | return temp_read - 25 + (count_per_c - count_remain) / count_per_c 227 | else: 228 | return 100 * temp_read - 25 + (count_per_c - count_remain) // count_per_c 229 | elif rom0 == 0x28: 230 | temp = None 231 | if self.fp: 232 | temp = (temp_msb << 8 | temp_lsb) / 16 233 | else: 234 | temp = (temp_msb << 8 | temp_lsb) * 100 // 16 235 | if (temp_msb & 0xf8) == 0xf8: # for negative temperature 236 | temp -= 0x1000 237 | return temp 238 | else: 239 | assert False 240 | -------------------------------------------------------------------------------- /examples/onlineLog/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PiAir/pycom-libraries/266e8a1fadb1a6ed1cf68e0b77c24cf9ae70cf29/examples/onlineLog/__init__.py -------------------------------------------------------------------------------- /examples/onlineLog/boot.py: -------------------------------------------------------------------------------- 1 | from machine import UART 2 | import machine 3 | import os 4 | from network import WLAN 5 | 6 | uart = UART(0, baudrate=115200) 7 | os.dupterm(uart) 8 | 9 | wifi_ssid = 'YOURWIFISSID' 10 | wifi_pass = 'YOURWIFIPASSWORD' 11 | 12 | if machine.reset_cause() != machine.SOFT_RESET: 13 | 14 | wlan = WLAN(mode=WLAN.STA) 15 | 16 | wlan.connect(wifi_ssid, auth=(WLAN.WPA2, wifi_pass), timeout=5000) 17 | 18 | while not wlan.isconnected(): 19 | machine.idle() 20 | 21 | 22 | machine.main('main.py') 23 | -------------------------------------------------------------------------------- /examples/onlineLog/main.py: -------------------------------------------------------------------------------- 1 | import time 2 | import machine 3 | from onewire import DS18X20 4 | from onewire import OneWire 5 | 6 | import usocket as socket 7 | 8 | publicKey = 'SPARKFUNCHANNELPUBLICKEY' 9 | privateKey = 'SPARKFUNCAHNNELPRIVATEKEY' 10 | 11 | 12 | #DS18B20 data line connected to pin P10 13 | ow = OneWire(machine.Pin('P10')) 14 | temp = DS18X20(ow) 15 | 16 | while True: 17 | temp.start_convertion() 18 | time.sleep(1) 19 | tempValue = temp.read_temp_async()/100.0 20 | u = 'POST /input/%s?private_key=%s&temp=%f HTTP/1.0\n\n'%( publicKey, privateKey, tempValue) 21 | 22 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 23 | ai = socket.getaddrinfo("data.sparkfun.com", 80) 24 | addr = ai[0][4] 25 | s.connect(addr) 26 | s.sendall(u) 27 | status = s.recv(4096) 28 | s.close() 29 | print('POST temp=%f'%tempValue) 30 | time.sleep(10) 31 | -------------------------------------------------------------------------------- /examples/onlineLog/onewire.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | OneWire library for MicroPython 5 | """ 6 | 7 | import time 8 | import machine 9 | 10 | class OneWire: 11 | CMD_SEARCHROM = const(0xf0) 12 | CMD_READROM = const(0x33) 13 | CMD_MATCHROM = const(0x55) 14 | CMD_SKIPROM = const(0xcc) 15 | 16 | def __init__(self, pin): 17 | self.pin = pin 18 | self.pin.init(pin.OPEN_DRAIN, pin.PULL_UP) 19 | 20 | def reset(self): 21 | """ 22 | Perform the onewire reset function. 23 | Returns True if a device asserted a presence pulse, False otherwise. 24 | """ 25 | sleep_us = time.sleep_us 26 | disable_irq = machine.disable_irq 27 | enable_irq = machine.enable_irq 28 | pin = self.pin 29 | 30 | pin(0) 31 | sleep_us(480) 32 | i = disable_irq() 33 | pin(1) 34 | sleep_us(60) 35 | status = not pin() 36 | enable_irq(i) 37 | sleep_us(420) 38 | return status 39 | 40 | def read_bit(self): 41 | sleep_us = time.sleep_us 42 | enable_irq = machine.enable_irq 43 | pin = self.pin 44 | 45 | pin(1) # half of the devices don't match CRC without this line 46 | i = machine.disable_irq() 47 | pin(0) 48 | sleep_us(1) 49 | pin(1) 50 | sleep_us(1) 51 | value = pin() 52 | enable_irq(i) 53 | sleep_us(40) 54 | return value 55 | 56 | def read_byte(self): 57 | value = 0 58 | for i in range(8): 59 | value |= self.read_bit() << i 60 | return value 61 | 62 | def read_bytes(self, count): 63 | buf = bytearray(count) 64 | for i in range(count): 65 | buf[i] = self.read_byte() 66 | return buf 67 | 68 | def write_bit(self, value): 69 | sleep_us = time.sleep_us 70 | pin = self.pin 71 | 72 | i = machine.disable_irq() 73 | pin(0) 74 | sleep_us(1) 75 | pin(value) 76 | sleep_us(60) 77 | pin(1) 78 | sleep_us(1) 79 | machine.enable_irq(i) 80 | 81 | def write_byte(self, value): 82 | for i in range(8): 83 | self.write_bit(value & 1) 84 | value >>= 1 85 | 86 | def write_bytes(self, buf): 87 | for b in buf: 88 | self.write_byte(b) 89 | 90 | def select_rom(self, rom): 91 | """ 92 | Select a specific device to talk to. Pass in rom as a bytearray (8 bytes). 93 | """ 94 | self.reset() 95 | self.write_byte(CMD_MATCHROM) 96 | self.write_bytes(rom) 97 | 98 | def crc8(self, data): 99 | """ 100 | Compute CRC 101 | """ 102 | crc = 0 103 | for i in range(len(data)): 104 | byte = data[i] 105 | for b in range(8): 106 | fb_bit = (crc ^ byte) & 0x01 107 | if fb_bit == 0x01: 108 | crc = crc ^ 0x18 109 | crc = (crc >> 1) & 0x7f 110 | if fb_bit == 0x01: 111 | crc = crc | 0x80 112 | byte = byte >> 1 113 | return crc 114 | 115 | def scan(self): 116 | """ 117 | Return a list of ROMs for all attached devices. 118 | Each ROM is returned as a bytes object of 8 bytes. 119 | """ 120 | devices = [] 121 | diff = 65 122 | rom = False 123 | for i in range(0xff): 124 | rom, diff = self._search_rom(rom, diff) 125 | if rom: 126 | devices += [rom] 127 | if diff == 0: 128 | break 129 | return devices 130 | 131 | def _search_rom(self, l_rom, diff): 132 | if not self.reset(): 133 | return None, 0 134 | self.write_byte(CMD_SEARCHROM) 135 | if not l_rom: 136 | l_rom = bytearray(8) 137 | rom = bytearray(8) 138 | next_diff = 0 139 | i = 64 140 | for byte in range(8): 141 | r_b = 0 142 | for bit in range(8): 143 | b = self.read_bit() 144 | if self.read_bit(): 145 | if b: # there are no devices or there is an error on the bus 146 | return None, 0 147 | else: 148 | if not b: # collision, two devices with different bit meaning 149 | if diff > i or ((l_rom[byte] & (1 << bit)) and diff != i): 150 | b = 1 151 | next_diff = i 152 | self.write_bit(b) 153 | if b: 154 | r_b |= 1 << bit 155 | i -= 1 156 | rom[byte] = r_b 157 | return rom, next_diff 158 | 159 | class DS18X20(object): 160 | def __init__(self, onewire): 161 | self.ow = onewire 162 | self.roms = [rom for rom in self.ow.scan() if rom[0] == 0x10 or rom[0] == 0x28] 163 | 164 | def isbusy(self): 165 | """ 166 | Checks wether one of the DS18x20 devices on the bus is busy 167 | performing a temperature convertion 168 | """ 169 | return not self.ow.read_bit() 170 | 171 | def start_convertion(self, rom=None): 172 | """ 173 | Start the temp conversion on one DS18x20 device. 174 | Pass the 8-byte bytes object with the ROM of the specific device you want to read. 175 | If only one DS18x20 device is attached to the bus you may omit the rom parameter. 176 | """ 177 | rom = rom or self.roms[0] 178 | ow = self.ow 179 | ow.reset() 180 | ow.select_rom(rom) 181 | ow.write_byte(0x44) # Convert Temp 182 | 183 | def read_temp_async(self, rom=None): 184 | """ 185 | Read the temperature of one DS18x20 device if the convertion is complete, 186 | otherwise return None. 187 | """ 188 | if self.isbusy(): 189 | return None 190 | rom = rom or self.roms[0] 191 | ow = self.ow 192 | ow.reset() 193 | ow.select_rom(rom) 194 | ow.write_byte(0xbe) # Read scratch 195 | data = ow.read_bytes(9) 196 | return self.convert_temp(rom[0], data) 197 | 198 | def convert_temp(self, rom0, data): 199 | """ 200 | Convert the raw temperature data into degrees celsius and return as a fixed point with 2 decimal places. 201 | """ 202 | temp_lsb = data[0] 203 | temp_msb = data[1] 204 | if rom0 == 0x10: 205 | if temp_msb != 0: 206 | # convert negative number 207 | temp_read = temp_lsb >> 1 | 0x80 # truncate bit 0 by shifting, fill high bit with 1. 208 | temp_read = -((~temp_read + 1) & 0xff) # now convert from two's complement 209 | else: 210 | temp_read = temp_lsb >> 1 # truncate bit 0 by shifting 211 | count_remain = data[6] 212 | count_per_c = data[7] 213 | temp = 100 * temp_read - 25 + (count_per_c - count_remain) // count_per_c 214 | return temp 215 | elif rom0 == 0x28: 216 | return (temp_msb << 8 | temp_lsb) * 100 // 16 217 | else: 218 | assert False 219 | -------------------------------------------------------------------------------- /examples/sigfoxUplink/boot.py: -------------------------------------------------------------------------------- 1 | from machine import UART 2 | import machine 3 | import os 4 | 5 | uart = UART(0, baudrate=115200) 6 | os.dupterm(uart) 7 | 8 | machine.main('main.py') 9 | -------------------------------------------------------------------------------- /examples/sigfoxUplink/main.py: -------------------------------------------------------------------------------- 1 | from network import Sigfox 2 | import socket 3 | 4 | # init Sigfox for RCZ1 (Europe) 5 | sigfox = Sigfox(mode=Sigfox.SIGFOX, rcz=Sigfox.RCZ1) 6 | 7 | # create a Sigfox socket 8 | s = socket.socket(socket.AF_SIGFOX, socket.SOCK_RAW) 9 | 10 | # make the socket blocking 11 | s.setblocking(True) 12 | 13 | # configure it as uplink only 14 | s.setsockopt(socket.SOL_SIGFOX, socket.SO_RX, False) 15 | 16 | # send some bytes 17 | s.send(bytes([0x01, 0x02, 0x03]) 18 | -------------------------------------------------------------------------------- /examples/threading/boot.py: -------------------------------------------------------------------------------- 1 | from machine import UART 2 | import machine 3 | import os 4 | 5 | uart = UART(0, baudrate=115200) 6 | os.dupterm(uart) 7 | 8 | machine.main('main.py') 9 | -------------------------------------------------------------------------------- /examples/threading/main.py: -------------------------------------------------------------------------------- 1 | import _thread 2 | import time 3 | 4 | def th_func(delay, id): 5 | while True: 6 | time.sleep(delay) 7 | print('Running thread %d' % id) 8 | 9 | for i in range(2): 10 | _thread.start_new_thread(th_func, (i + 1, i)) -------------------------------------------------------------------------------- /lib/onewire/onewire.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | OneWire library for MicroPython 5 | """ 6 | 7 | import time 8 | import machine 9 | 10 | class OneWire: 11 | CMD_SEARCHROM = const(0xf0) 12 | CMD_READROM = const(0x33) 13 | CMD_MATCHROM = const(0x55) 14 | CMD_SKIPROM = const(0xcc) 15 | 16 | def __init__(self, pin): 17 | self.pin = pin 18 | self.pin.init(pin.OPEN_DRAIN, pin.PULL_UP) 19 | 20 | def reset(self): 21 | """ 22 | Perform the onewire reset function. 23 | Returns True if a device asserted a presence pulse, False otherwise. 24 | """ 25 | sleep_us = time.sleep_us 26 | disable_irq = machine.disable_irq 27 | enable_irq = machine.enable_irq 28 | pin = self.pin 29 | 30 | pin(0) 31 | sleep_us(480) 32 | i = disable_irq() 33 | pin(1) 34 | sleep_us(60) 35 | status = not pin() 36 | enable_irq(i) 37 | sleep_us(420) 38 | return status 39 | 40 | def read_bit(self): 41 | sleep_us = time.sleep_us 42 | enable_irq = machine.enable_irq 43 | pin = self.pin 44 | 45 | pin(1) # half of the devices don't match CRC without this line 46 | i = machine.disable_irq() 47 | pin(0) 48 | sleep_us(1) 49 | pin(1) 50 | sleep_us(1) 51 | value = pin() 52 | enable_irq(i) 53 | sleep_us(40) 54 | return value 55 | 56 | def read_byte(self): 57 | value = 0 58 | for i in range(8): 59 | value |= self.read_bit() << i 60 | return value 61 | 62 | def read_bytes(self, count): 63 | buf = bytearray(count) 64 | for i in range(count): 65 | buf[i] = self.read_byte() 66 | return buf 67 | 68 | def write_bit(self, value): 69 | sleep_us = time.sleep_us 70 | pin = self.pin 71 | 72 | i = machine.disable_irq() 73 | pin(0) 74 | sleep_us(1) 75 | pin(value) 76 | sleep_us(60) 77 | pin(1) 78 | sleep_us(1) 79 | machine.enable_irq(i) 80 | 81 | def write_byte(self, value): 82 | for i in range(8): 83 | self.write_bit(value & 1) 84 | value >>= 1 85 | 86 | def write_bytes(self, buf): 87 | for b in buf: 88 | self.write_byte(b) 89 | 90 | def select_rom(self, rom): 91 | """ 92 | Select a specific device to talk to. Pass in rom as a bytearray (8 bytes). 93 | """ 94 | self.reset() 95 | self.write_byte(CMD_MATCHROM) 96 | self.write_bytes(rom) 97 | 98 | def crc8(self, data): 99 | """ 100 | Compute CRC 101 | """ 102 | crc = 0 103 | for i in range(len(data)): 104 | byte = data[i] 105 | for b in range(8): 106 | fb_bit = (crc ^ byte) & 0x01 107 | if fb_bit == 0x01: 108 | crc = crc ^ 0x18 109 | crc = (crc >> 1) & 0x7f 110 | if fb_bit == 0x01: 111 | crc = crc | 0x80 112 | byte = byte >> 1 113 | return crc 114 | 115 | def scan(self): 116 | """ 117 | Return a list of ROMs for all attached devices. 118 | Each ROM is returned as a bytes object of 8 bytes. 119 | """ 120 | devices = [] 121 | diff = 65 122 | rom = False 123 | for i in range(0xff): 124 | rom, diff = self._search_rom(rom, diff) 125 | if rom: 126 | devices += [rom] 127 | if diff == 0: 128 | break 129 | return devices 130 | 131 | def _search_rom(self, l_rom, diff): 132 | if not self.reset(): 133 | return None, 0 134 | self.write_byte(CMD_SEARCHROM) 135 | if not l_rom: 136 | l_rom = bytearray(8) 137 | rom = bytearray(8) 138 | next_diff = 0 139 | i = 64 140 | for byte in range(8): 141 | r_b = 0 142 | for bit in range(8): 143 | b = self.read_bit() 144 | if self.read_bit(): 145 | if b: # there are no devices or there is an error on the bus 146 | return None, 0 147 | else: 148 | if not b: # collision, two devices with different bit meaning 149 | if diff > i or ((l_rom[byte] & (1 << bit)) and diff != i): 150 | b = 1 151 | next_diff = i 152 | self.write_bit(b) 153 | if b: 154 | r_b |= 1 << bit 155 | i -= 1 156 | rom[byte] = r_b 157 | return rom, next_diff 158 | 159 | class DS18X20(object): 160 | def __init__(self, onewire): 161 | self.ow = onewire 162 | self.roms = [rom for rom in self.ow.scan() if rom[0] == 0x10 or rom[0] == 0x28] 163 | self.fp = True 164 | try: 165 | 1/1 166 | except TypeError: 167 | self.fp = False # floatingpoint not supported 168 | 169 | def isbusy(self): 170 | """ 171 | Checks wether one of the DS18x20 devices on the bus is busy 172 | performing a temperature convertion 173 | """ 174 | return not self.ow.read_bit() 175 | 176 | def start_convertion(self, rom=None): 177 | """ 178 | Start the temp conversion on one DS18x20 device. 179 | Pass the 8-byte bytes object with the ROM of the specific device you want to read. 180 | If only one DS18x20 device is attached to the bus you may omit the rom parameter. 181 | """ 182 | if (rom==None) and (len(self.roms)>0): 183 | rom=self.roms[0] 184 | if rom!=None: 185 | rom = rom or self.roms[0] 186 | ow = self.ow 187 | ow.reset() 188 | ow.select_rom(rom) 189 | ow.write_byte(0x44) # Convert Temp 190 | 191 | def read_temp_async(self, rom=None): 192 | """ 193 | Read the temperature of one DS18x20 device if the convertion is complete, 194 | otherwise return None. 195 | """ 196 | if self.isbusy(): 197 | return None 198 | if (rom==None) and (len(self.roms)>0): 199 | rom=self.roms[0] 200 | if rom==None: 201 | return None 202 | else: 203 | ow = self.ow 204 | ow.reset() 205 | ow.select_rom(rom) 206 | ow.write_byte(0xbe) # Read scratch 207 | data = ow.read_bytes(9) 208 | return self.convert_temp(rom[0], data) 209 | 210 | def convert_temp(self, rom0, data): 211 | """ 212 | Convert the raw temperature data into degrees celsius and return as a fixed point with 2 decimal places. 213 | """ 214 | temp_lsb = data[0] 215 | temp_msb = data[1] 216 | if rom0 == 0x10: 217 | if temp_msb != 0: 218 | # convert negative number 219 | temp_read = temp_lsb >> 1 | 0x80 # truncate bit 0 by shifting, fill high bit with 1. 220 | temp_read = -((~temp_read + 1) & 0xff) # now convert from two's complement 221 | else: 222 | temp_read = temp_lsb >> 1 # truncate bit 0 by shifting 223 | count_remain = data[6] 224 | count_per_c = data[7] 225 | if self.fp: 226 | return temp_read - 25 + (count_per_c - count_remain) / count_per_c 227 | else: 228 | return 100 * temp_read - 25 + (count_per_c - count_remain) // count_per_c 229 | elif rom0 == 0x28: 230 | temp = None 231 | if self.fp: 232 | temp = (temp_msb << 8 | temp_lsb) / 16 233 | else: 234 | temp = (temp_msb << 8 | temp_lsb) * 100 // 16 235 | if (temp_msb & 0xf8) == 0xf8: # for negative temperature 236 | temp -= 0x1000 237 | return temp 238 | else: 239 | assert False 240 | --------------------------------------------------------------------------------