├── .gitignore ├── .vscode └── launch.json ├── README.md ├── boot.py ├── config.eventhub.json ├── config.json └── lib ├── base64.py ├── bme280.py ├── config.py ├── eventhub.py ├── hashlib ├── __init__.py ├── sha224.py ├── sha256.py ├── sha384.py └── sha512.py ├── hmac.py ├── iothub.py ├── sensor_bme280.py ├── sensor_fake.py ├── ssd1306.py ├── urlencode.py └── warnings.py /.gitignore: -------------------------------------------------------------------------------- 1 | config.json 2 | config.esp32-02.json 3 | config.esp32-lolin32.json 4 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | 8 | { 9 | "name": "Python", 10 | "type": "python", 11 | "request": "launch", 12 | "stopOnEntry": true, 13 | "pythonPath": "${config:python.pythonPath}", 14 | "program": "${file}", 15 | "cwd": "${workspaceRoot}", 16 | "env": {}, 17 | "envFile": "${workspaceRoot}/.env", 18 | "debugOptions": [ 19 | "WaitOnAbnormalExit", 20 | "WaitOnNormalExit", 21 | "RedirectOutput" 22 | ] 23 | }, 24 | { 25 | "name": "Python: Attach", 26 | "type": "python", 27 | "request": "attach", 28 | "localRoot": "${workspaceRoot}", 29 | "remoteRoot": "${workspaceRoot}", 30 | "port": 3000, 31 | "secret": "my_secret", 32 | "host": "localhost" 33 | }, 34 | { 35 | "name": "Python: Terminal (integrated)", 36 | "type": "python", 37 | "request": "launch", 38 | "stopOnEntry": true, 39 | "pythonPath": "${config:python.pythonPath}", 40 | "program": "${file}", 41 | "cwd": "", 42 | "console": "integratedTerminal", 43 | "env": {}, 44 | "envFile": "${workspaceRoot}/.env", 45 | "debugOptions": [ 46 | "WaitOnAbnormalExit", 47 | "WaitOnNormalExit" 48 | ] 49 | }, 50 | { 51 | "name": "Python: Terminal (external)", 52 | "type": "python", 53 | "request": "launch", 54 | "stopOnEntry": true, 55 | "pythonPath": "${config:python.pythonPath}", 56 | "program": "${file}", 57 | "cwd": "", 58 | "console": "externalTerminal", 59 | "env": {}, 60 | "envFile": "${workspaceRoot}/.env", 61 | "debugOptions": [ 62 | "WaitOnAbnormalExit", 63 | "WaitOnNormalExit" 64 | ] 65 | }, 66 | { 67 | "name": "Python: Django", 68 | "type": "python", 69 | "request": "launch", 70 | "stopOnEntry": true, 71 | "pythonPath": "${config:python.pythonPath}", 72 | "program": "${workspaceRoot}/manage.py", 73 | "cwd": "${workspaceRoot}", 74 | "args": [ 75 | "runserver", 76 | "--noreload", 77 | "--nothreading" 78 | ], 79 | "env": {}, 80 | "envFile": "${workspaceRoot}/.env", 81 | "debugOptions": [ 82 | "WaitOnAbnormalExit", 83 | "WaitOnNormalExit", 84 | "RedirectOutput", 85 | "DjangoDebugging" 86 | ] 87 | }, 88 | { 89 | "name": "Python: Flask (0.11.x or later)", 90 | "type": "python", 91 | "request": "launch", 92 | "stopOnEntry": false, 93 | "pythonPath": "${config:python.pythonPath}", 94 | "program": "fully qualified path fo 'flask' executable. Generally located along with python interpreter", 95 | "cwd": "${workspaceRoot}", 96 | "env": { 97 | "FLASK_APP": "${workspaceRoot}/quickstart/app.py" 98 | }, 99 | "args": [ 100 | "run", 101 | "--no-debugger", 102 | "--no-reload" 103 | ], 104 | "envFile": "${workspaceRoot}/.env", 105 | "debugOptions": [ 106 | "WaitOnAbnormalExit", 107 | "WaitOnNormalExit", 108 | "RedirectOutput" 109 | ] 110 | }, 111 | { 112 | "name": "Python: Flask (0.10.x or earlier)", 113 | "type": "python", 114 | "request": "launch", 115 | "stopOnEntry": false, 116 | "pythonPath": "${config:python.pythonPath}", 117 | "program": "${workspaceRoot}/run.py", 118 | "cwd": "${workspaceRoot}", 119 | "args": [], 120 | "env": {}, 121 | "envFile": "${workspaceRoot}/.env", 122 | "debugOptions": [ 123 | "WaitOnAbnormalExit", 124 | "WaitOnNormalExit", 125 | "RedirectOutput" 126 | ] 127 | }, 128 | { 129 | "name": "Python: PySpark", 130 | "type": "python", 131 | "request": "launch", 132 | "stopOnEntry": true, 133 | "osx": { 134 | "pythonPath": "${env:SPARK_HOME}/bin/spark-submit" 135 | }, 136 | "windows": { 137 | "pythonPath": "${env:SPARK_HOME}/bin/spark-submit.cmd" 138 | }, 139 | "linux": { 140 | "pythonPath": "${env:SPARK_HOME}/bin/spark-submit" 141 | }, 142 | "program": "${file}", 143 | "cwd": "${workspaceRoot}", 144 | "env": {}, 145 | "envFile": "${workspaceRoot}/.env", 146 | "debugOptions": [ 147 | "WaitOnAbnormalExit", 148 | "WaitOnNormalExit", 149 | "RedirectOutput" 150 | ] 151 | }, 152 | { 153 | "name": "Python: Module", 154 | "type": "python", 155 | "request": "launch", 156 | "stopOnEntry": true, 157 | "pythonPath": "${config:python.pythonPath}", 158 | "module": "module.name", 159 | "cwd": "${workspaceRoot}", 160 | "env": {}, 161 | "envFile": "${workspaceRoot}/.env", 162 | "debugOptions": [ 163 | "WaitOnAbnormalExit", 164 | "WaitOnNormalExit", 165 | "RedirectOutput" 166 | ] 167 | }, 168 | { 169 | "name": "Python: Pyramid", 170 | "type": "python", 171 | "request": "launch", 172 | "stopOnEntry": true, 173 | "pythonPath": "${config:python.pythonPath}", 174 | "cwd": "${workspaceRoot}", 175 | "env": {}, 176 | "envFile": "${workspaceRoot}/.env", 177 | "args": [ 178 | "${workspaceRoot}/development.ini" 179 | ], 180 | "debugOptions": [ 181 | "WaitOnAbnormalExit", 182 | "WaitOnNormalExit", 183 | "RedirectOutput", 184 | "Pyramid" 185 | ] 186 | }, 187 | { 188 | "name": "Python: Watson", 189 | "type": "python", 190 | "request": "launch", 191 | "stopOnEntry": true, 192 | "pythonPath": "${config:python.pythonPath}", 193 | "program": "${workspaceRoot}/console.py", 194 | "cwd": "${workspaceRoot}", 195 | "args": [ 196 | "dev", 197 | "runserver", 198 | "--noreload=True" 199 | ], 200 | "env": {}, 201 | "envFile": "${workspaceRoot}/.env", 202 | "debugOptions": [ 203 | "WaitOnAbnormalExit", 204 | "WaitOnNormalExit", 205 | "RedirectOutput" 206 | ] 207 | } 208 | ] 209 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32-Micropython-Azure-IoT-and-Event-Hub-Client 2 | -------------------------------------------------------------------------------- /boot.py: -------------------------------------------------------------------------------- 1 | import iothub 2 | import eventhub 3 | import socket 4 | import ussl 5 | import utime as time 6 | import network 7 | from machine import I2C, Pin, ADC 8 | import ssd1306 as oled 9 | import gc 10 | import config 11 | import sensor_fake 12 | 13 | # i2c = I2C(scl=Pin(4), sda=Pin(5)) 14 | i2c = I2C(scl=Pin(22), sda=Pin(21)) 15 | builtinLedPin = 5 16 | 17 | display = None 18 | builtinLed = None 19 | 20 | cfg = config.Config('config.eventhub.json') 21 | mySensor = cfg.sensor.Sensor(i2c) 22 | 23 | 24 | if cfg.hubType == 'eventhub': 25 | hub = eventhub.EventHub(cfg.host, cfg.eventHubName, cfg.eventHubPolicyName, cfg.key) 26 | elif cfg.hubType == 'iothub': 27 | hub = iothub.IotHub(cfg.host, cfg.deviceId, cfg.key) 28 | 29 | wlan = network.WLAN(network.STA_IF) 30 | 31 | lastUpdated = 0 32 | updateSas = True 33 | 34 | 35 | def newSasToken(): 36 | global lastUpdated, updateSas, SAS 37 | if time.ticks_diff(time.time(), lastUpdated) > 60 * 15: 38 | lastUpdated = time.time() 39 | updateSas = True 40 | 41 | if updateSas: 42 | SAS = hub.generate_sas_token() 43 | print('Updating Sas') 44 | updateSas = False 45 | 46 | 47 | def initDisplay(i2c): 48 | global display, builtinLed 49 | i2cDevices = I2C.scan(i2c) 50 | if 60 in i2cDevices: 51 | display = oled.SSD1306_I2C(128, 64, i2c) 52 | return True 53 | else: 54 | builtinLed = Pin(builtinLedPin, Pin.OUT) 55 | return False 56 | 57 | 58 | def wlan_connect(ssid='MYSSID', password='MYPASS'): 59 | if not wlan.active() or not wlan.isconnected(): 60 | wlan.active(True) 61 | print('connecting to:', ssid) 62 | wlan.connect(ssid, password) 63 | while not wlan.isconnected(): 64 | pass 65 | print('network config:', wlan.ifconfig()) 66 | 67 | 68 | def checkwifi(): 69 | blinkcnt = 0 70 | while not wlan.isconnected(): 71 | time.sleep_ms(500) 72 | 73 | 74 | def main(use_stream=True): 75 | 76 | s = socket.socket() 77 | ai = socket.getaddrinfo(cfg.host, 443) 78 | addr = ai[0][-1] 79 | s.close() 80 | 81 | count = 0 82 | errorCount = 0 83 | 84 | while True: 85 | if not oledDisplay: 86 | builtinLed.value(0) # turn on led 87 | 88 | checkwifi() 89 | newSasToken() 90 | count = count + 1 91 | 92 | temperature, pressure, humidity = mySensor.measure() 93 | light = 0 94 | freeMemory = gc.mem_free() 95 | 96 | if oledDisplay: 97 | display.fill(0) 98 | display.text('t:' + str(temperature), 0, 0) 99 | display.text('p:' + str(pressure), 0, 9) 100 | display.text('h:' + str(humidity), 0, 18) 101 | display.text('m:' + str(freeMemory), 0, 27) 102 | display.text('c:' + str(count), 0, 36) 103 | display.text('d:' + cfg.deviceId, 0, 45) 104 | display.text('e:' + str(errorCount), 0, 53) 105 | display.show() 106 | 107 | data = b'{"DeviceId":"%s","Id":%u,"Mem":%u,"Celsius":%s,"hPa":%s,"Humidity":%s, "Geo":"%s", "Light":%d, "Errors":%d}' % ( 108 | cfg.deviceId, count, freeMemory, temperature, pressure, humidity, cfg.location, light, errorCount) 109 | 110 | try: 111 | s = socket.socket() 112 | s.connect(addr) 113 | s = ussl.wrap_socket(s) # SSL wrap 114 | 115 | # Send POST request to Azure IoT Hub 116 | # s.write("POST /devices/" + cfg.deviceId + 117 | # "/messages/events?api-version=2016-02-03 HTTP/1.0\r\n") 118 | 119 | s.write("POST " + hub.endpoint + " HTTP/1.1\r\n") 120 | # HTTP Headers 121 | s.write("Host: " + cfg.host + "\r\n") 122 | s.write("Authorization: " + SAS + "\r\n") 123 | s.write("Content-Type: application/json\r\n") 124 | s.write("Connection: close\r\n") 125 | s.write("Content-Length: " + str(len(data)) + "\r\n\r\n") 126 | # Data 127 | s.write(data) 128 | 129 | # Print 128 bytes of response 130 | print(s.read(128)[:12]) 131 | 132 | s.close() 133 | except: 134 | print('Problem posting data') 135 | errorCount = errorCount + 1 136 | finally: 137 | if not oledDisplay: 138 | builtinLed.value(1) # turn off led 139 | 140 | print('messages sent: %d, errors: %d' % (count, errorCount)) 141 | time.sleep(cfg.sampleRate) 142 | 143 | 144 | oledDisplay = initDisplay(i2c) 145 | if oledDisplay: 146 | display.text("welcome", 0, 0) 147 | display.show() 148 | 149 | wlan_connect(cfg.wifiSsid, cfg.wifiPwd) 150 | 151 | time.sleep(2) # allow for a little settle time 152 | main() 153 | -------------------------------------------------------------------------------- /config.eventhub.json: -------------------------------------------------------------------------------- 1 | { 2 | "HubType":"eventhub", 3 | "WifiSsid": "NCW", 4 | "WifiPwd": "malolos5459", 5 | "Host": "glovebox01.servicebus.windows.net", 6 | "DeviceId": "wemos10", 7 | "Key": "CFu6jEIEwTiPspK587p/jQ8+z9bsQqWiglHob6jryhs=", 8 | "SensorModule": "sensor_bme280", 9 | "SampleRate": 15, 10 | "Location": "syd-lolin32", 11 | "EventHubName":"environment", 12 | "EventHubPolicyName":"NSW" 13 | } -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "WifiSsid":"YourWiFiSSID", 3 | "WifiPwd":"Your Wifi Password", 4 | "Host":"Your IoT Hub.azure-devices.net", 5 | "DeviceId":"esp32-02", 6 | "Key":"BD5iazmYNDyuiasyawYYO0ueDzZ47Z8/RN881Lxew=", 7 | "SensorModule":"sensor_bme280", 8 | "SampleRate":2, 9 | "Location":"syd-esp32-02" 10 | } 11 | -------------------------------------------------------------------------------- /lib/base64.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | """RFC 3548: Base16, Base32, Base64 Data Encodings""" 4 | 5 | # Modified 04-Oct-1995 by Jack Jansen to use binascii module 6 | # Modified 30-Dec-2003 by Barry Warsaw to add full RFC 3548 support 7 | # Modified 22-May-2007 by Guido van Rossum to use bytes everywhere 8 | 9 | import re 10 | import struct 11 | import binascii 12 | 13 | 14 | __all__ = [ 15 | # Legacy interface exports traditional RFC 1521 Base64 encodings 16 | 'encode', 'decode', 'encodebytes', 'decodebytes', 17 | # Generalized interface for other encodings 18 | 'b64encode', 'b64decode', 'b32encode', 'b32decode', 19 | 'b16encode', 'b16decode', 20 | # Standard Base64 encoding 21 | 'standard_b64encode', 'standard_b64decode', 22 | # Some common Base64 alternatives. As referenced by RFC 3458, see thread 23 | # starting at: 24 | # 25 | # http://zgp.org/pipermail/p2p-hackers/2001-September/000316.html 26 | 'urlsafe_b64encode', 'urlsafe_b64decode', 27 | ] 28 | 29 | 30 | bytes_types = (bytes, bytearray) # Types acceptable as binary data 31 | 32 | def _bytes_from_decode_data(s): 33 | if isinstance(s, str): 34 | try: 35 | return s.encode('ascii') 36 | # except UnicodeEncodeError: 37 | except: 38 | raise ValueError('string argument should contain only ASCII characters') 39 | elif isinstance(s, bytes_types): 40 | return s 41 | else: 42 | raise TypeError("argument should be bytes or ASCII string, not %s" % s.__class__.__name__) 43 | 44 | 45 | 46 | # Base64 encoding/decoding uses binascii 47 | 48 | def b64encode(s, altchars=None): 49 | """Encode a byte string using Base64. 50 | 51 | s is the byte string to encode. Optional altchars must be a byte 52 | string of length 2 which specifies an alternative alphabet for the 53 | '+' and '/' characters. This allows an application to 54 | e.g. generate url or filesystem safe Base64 strings. 55 | 56 | The encoded byte string is returned. 57 | """ 58 | if not isinstance(s, bytes_types): 59 | raise TypeError("expected bytes, not %s" % s.__class__.__name__) 60 | # Strip off the trailing newline 61 | encoded = binascii.b2a_base64(s)[:-1] 62 | if altchars is not None: 63 | if not isinstance(altchars, bytes_types): 64 | raise TypeError("expected bytes, not %s" 65 | % altchars.__class__.__name__) 66 | assert len(altchars) == 2, repr(altchars) 67 | return encoded.translate(bytes.maketrans(b'+/', altchars)) 68 | return encoded 69 | 70 | 71 | def b64decode(s, altchars=None, validate=False): 72 | """Decode a Base64 encoded byte string. 73 | 74 | s is the byte string to decode. Optional altchars must be a 75 | string of length 2 which specifies the alternative alphabet used 76 | instead of the '+' and '/' characters. 77 | 78 | The decoded string is returned. A binascii.Error is raised if s is 79 | incorrectly padded. 80 | 81 | If validate is False (the default), non-base64-alphabet characters are 82 | discarded prior to the padding check. If validate is True, 83 | non-base64-alphabet characters in the input result in a binascii.Error. 84 | """ 85 | s = _bytes_from_decode_data(s) 86 | if altchars is not None: 87 | altchars = _bytes_from_decode_data(altchars) 88 | assert len(altchars) == 2, repr(altchars) 89 | s = s.translate(bytes.maketrans(altchars, b'+/')) 90 | if validate and not re.match(b'^[A-Za-z0-9+/]*={0,2}$', s): 91 | raise binascii.Error('Non-base64 digit found') 92 | return binascii.a2b_base64(s) 93 | 94 | 95 | def standard_b64encode(s): 96 | """Encode a byte string using the standard Base64 alphabet. 97 | 98 | s is the byte string to encode. The encoded byte string is returned. 99 | """ 100 | return b64encode(s) 101 | 102 | def standard_b64decode(s): 103 | """Decode a byte string encoded with the standard Base64 alphabet. 104 | 105 | s is the byte string to decode. The decoded byte string is 106 | returned. binascii.Error is raised if the input is incorrectly 107 | padded or if there are non-alphabet characters present in the 108 | input. 109 | """ 110 | return b64decode(s) 111 | 112 | 113 | #_urlsafe_encode_translation = bytes.maketrans(b'+/', b'-_') 114 | #_urlsafe_decode_translation = bytes.maketrans(b'-_', b'+/') 115 | 116 | def urlsafe_b64encode(s): 117 | """Encode a byte string using a url-safe Base64 alphabet. 118 | 119 | s is the byte string to encode. The encoded byte string is 120 | returned. The alphabet uses '-' instead of '+' and '_' instead of 121 | '/'. 122 | """ 123 | # return b64encode(s).translate(_urlsafe_encode_translation) 124 | raise NotImplementedError() 125 | 126 | def urlsafe_b64decode(s): 127 | """Decode a byte string encoded with the standard Base64 alphabet. 128 | 129 | s is the byte string to decode. The decoded byte string is 130 | returned. binascii.Error is raised if the input is incorrectly 131 | padded or if there are non-alphabet characters present in the 132 | input. 133 | 134 | The alphabet uses '-' instead of '+' and '_' instead of '/'. 135 | """ 136 | # s = _bytes_from_decode_data(s) 137 | # s = s.translate(_urlsafe_decode_translation) 138 | # return b64decode(s) 139 | raise NotImplementedError() 140 | 141 | 142 | 143 | # Base32 encoding/decoding must be done in Python 144 | _b32alphabet = { 145 | 0: b'A', 9: b'J', 18: b'S', 27: b'3', 146 | 1: b'B', 10: b'K', 19: b'T', 28: b'4', 147 | 2: b'C', 11: b'L', 20: b'U', 29: b'5', 148 | 3: b'D', 12: b'M', 21: b'V', 30: b'6', 149 | 4: b'E', 13: b'N', 22: b'W', 31: b'7', 150 | 5: b'F', 14: b'O', 23: b'X', 151 | 6: b'G', 15: b'P', 24: b'Y', 152 | 7: b'H', 16: b'Q', 25: b'Z', 153 | 8: b'I', 17: b'R', 26: b'2', 154 | } 155 | 156 | _b32tab = [v[0] for k, v in sorted(_b32alphabet.items())] 157 | _b32rev = dict([(v[0], k) for k, v in _b32alphabet.items()]) 158 | 159 | 160 | def b32encode(s): 161 | """Encode a byte string using Base32. 162 | 163 | s is the byte string to encode. The encoded byte string is returned. 164 | """ 165 | if not isinstance(s, bytes_types): 166 | raise TypeError("expected bytes, not %s" % s.__class__.__name__) 167 | quanta, leftover = divmod(len(s), 5) 168 | # Pad the last quantum with zero bits if necessary 169 | if leftover: 170 | s = s + bytes(5 - leftover) # Don't use += ! 171 | quanta += 1 172 | encoded = bytearray() 173 | for i in range(quanta): 174 | # c1 and c2 are 16 bits wide, c3 is 8 bits wide. The intent of this 175 | # code is to process the 40 bits in units of 5 bits. So we take the 1 176 | # leftover bit of c1 and tack it onto c2. Then we take the 2 leftover 177 | # bits of c2 and tack them onto c3. The shifts and masks are intended 178 | # to give us values of exactly 5 bits in width. 179 | c1, c2, c3 = struct.unpack('!HHB', s[i*5:(i+1)*5]) 180 | c2 += (c1 & 1) << 16 # 17 bits wide 181 | c3 += (c2 & 3) << 8 # 10 bits wide 182 | encoded += bytes([_b32tab[c1 >> 11], # bits 1 - 5 183 | _b32tab[(c1 >> 6) & 0x1f], # bits 6 - 10 184 | _b32tab[(c1 >> 1) & 0x1f], # bits 11 - 15 185 | _b32tab[c2 >> 12], # bits 16 - 20 (1 - 5) 186 | _b32tab[(c2 >> 7) & 0x1f], # bits 21 - 25 (6 - 10) 187 | _b32tab[(c2 >> 2) & 0x1f], # bits 26 - 30 (11 - 15) 188 | _b32tab[c3 >> 5], # bits 31 - 35 (1 - 5) 189 | _b32tab[c3 & 0x1f], # bits 36 - 40 (1 - 5) 190 | ]) 191 | # Adjust for any leftover partial quanta 192 | if leftover == 1: 193 | encoded = encoded[:-6] + b'======' 194 | elif leftover == 2: 195 | encoded = encoded[:-4] + b'====' 196 | elif leftover == 3: 197 | encoded = encoded[:-3] + b'===' 198 | elif leftover == 4: 199 | encoded = encoded[:-1] + b'=' 200 | return bytes(encoded) 201 | 202 | 203 | def b32decode(s, casefold=False, map01=None): 204 | """Decode a Base32 encoded byte string. 205 | 206 | s is the byte string to decode. Optional casefold is a flag 207 | specifying whether a lowercase alphabet is acceptable as input. 208 | For security purposes, the default is False. 209 | 210 | RFC 3548 allows for optional mapping of the digit 0 (zero) to the 211 | letter O (oh), and for optional mapping of the digit 1 (one) to 212 | either the letter I (eye) or letter L (el). The optional argument 213 | map01 when not None, specifies which letter the digit 1 should be 214 | mapped to (when map01 is not None, the digit 0 is always mapped to 215 | the letter O). For security purposes the default is None, so that 216 | 0 and 1 are not allowed in the input. 217 | 218 | The decoded byte string is returned. binascii.Error is raised if 219 | the input is incorrectly padded or if there are non-alphabet 220 | characters present in the input. 221 | """ 222 | s = _bytes_from_decode_data(s) 223 | quanta, leftover = divmod(len(s), 8) 224 | if leftover: 225 | raise binascii.Error('Incorrect padding') 226 | # Handle section 2.4 zero and one mapping. The flag map01 will be either 227 | # False, or the character to map the digit 1 (one) to. It should be 228 | # either L (el) or I (eye). 229 | if map01 is not None: 230 | map01 = _bytes_from_decode_data(map01) 231 | assert len(map01) == 1, repr(map01) 232 | s = s.translate(bytes.maketrans(b'01', b'O' + map01)) 233 | if casefold: 234 | s = s.upper() 235 | # Strip off pad characters from the right. We need to count the pad 236 | # characters because this will tell us how many null bytes to remove from 237 | # the end of the decoded string. 238 | padchars = s.find(b'=') 239 | if padchars > 0: 240 | padchars = len(s) - padchars 241 | s = s[:-padchars] 242 | else: 243 | padchars = 0 244 | 245 | # Now decode the full quanta 246 | parts = [] 247 | acc = 0 248 | shift = 35 249 | for c in s: 250 | val = _b32rev.get(c) 251 | if val is None: 252 | raise binascii.Error('Non-base32 digit found') 253 | acc += _b32rev[c] << shift 254 | shift -= 5 255 | if shift < 0: 256 | parts.append(binascii.unhexlify(bytes('%010x' % acc, "ascii"))) 257 | acc = 0 258 | shift = 35 259 | # Process the last, partial quanta 260 | last = binascii.unhexlify(bytes('%010x' % acc, "ascii")) 261 | if padchars == 0: 262 | last = b'' # No characters 263 | elif padchars == 1: 264 | last = last[:-1] 265 | elif padchars == 3: 266 | last = last[:-2] 267 | elif padchars == 4: 268 | last = last[:-3] 269 | elif padchars == 6: 270 | last = last[:-4] 271 | else: 272 | raise binascii.Error('Incorrect padding') 273 | parts.append(last) 274 | return b''.join(parts) 275 | 276 | 277 | 278 | # RFC 3548, Base 16 Alphabet specifies uppercase, but hexlify() returns 279 | # lowercase. The RFC also recommends against accepting input case 280 | # insensitively. 281 | def b16encode(s): 282 | """Encode a byte string using Base16. 283 | 284 | s is the byte string to encode. The encoded byte string is returned. 285 | """ 286 | if not isinstance(s, bytes_types): 287 | raise TypeError("expected bytes, not %s" % s.__class__.__name__) 288 | return binascii.hexlify(s).upper() 289 | 290 | 291 | def b16decode(s, casefold=False): 292 | """Decode a Base16 encoded byte string. 293 | 294 | s is the byte string to decode. Optional casefold is a flag 295 | specifying whether a lowercase alphabet is acceptable as input. 296 | For security purposes, the default is False. 297 | 298 | The decoded byte string is returned. binascii.Error is raised if 299 | s were incorrectly padded or if there are non-alphabet characters 300 | present in the string. 301 | """ 302 | s = _bytes_from_decode_data(s) 303 | if casefold: 304 | s = s.upper() 305 | if re.search(b'[^0-9A-F]', s): 306 | raise binascii.Error('Non-base16 digit found') 307 | return binascii.unhexlify(s) 308 | 309 | 310 | 311 | # Legacy interface. This code could be cleaned up since I don't believe 312 | # binascii has any line length limitations. It just doesn't seem worth it 313 | # though. The files should be opened in binary mode. 314 | 315 | MAXLINESIZE = 76 # Excluding the CRLF 316 | MAXBINSIZE = (MAXLINESIZE//4)*3 317 | 318 | def encode(input, output): 319 | """Encode a file; input and output are binary files.""" 320 | while True: 321 | s = input.read(MAXBINSIZE) 322 | if not s: 323 | break 324 | while len(s) < MAXBINSIZE: 325 | ns = input.read(MAXBINSIZE-len(s)) 326 | if not ns: 327 | break 328 | s += ns 329 | line = binascii.b2a_base64(s) 330 | output.write(line) 331 | 332 | 333 | def decode(input, output): 334 | """Decode a file; input and output are binary files.""" 335 | while True: 336 | line = input.readline() 337 | if not line: 338 | break 339 | s = binascii.a2b_base64(line) 340 | output.write(s) 341 | 342 | 343 | def encodebytes(s): 344 | """Encode a bytestring into a bytestring containing multiple lines 345 | of base-64 data.""" 346 | if not isinstance(s, bytes_types): 347 | raise TypeError("expected bytes, not %s" % s.__class__.__name__) 348 | pieces = [] 349 | for i in range(0, len(s), MAXBINSIZE): 350 | chunk = s[i : i + MAXBINSIZE] 351 | pieces.append(binascii.b2a_base64(chunk)) 352 | return b"".join(pieces) 353 | 354 | def encodestring(s): 355 | """Legacy alias of encodebytes().""" 356 | import warnings 357 | warnings.warn("encodestring() is a deprecated alias, use encodebytes()", 358 | DeprecationWarning, 2) 359 | return encodebytes(s) 360 | 361 | 362 | def decodebytes(s): 363 | """Decode a bytestring of base-64 data into a bytestring.""" 364 | if not isinstance(s, bytes_types): 365 | raise TypeError("expected bytes, not %s" % s.__class__.__name__) 366 | return binascii.a2b_base64(s) 367 | 368 | def decodestring(s): 369 | """Legacy alias of decodebytes().""" 370 | import warnings 371 | warnings.warn("decodestring() is a deprecated alias, use decodebytes()", 372 | DeprecationWarning, 2) 373 | return decodebytes(s) 374 | 375 | 376 | # Usable as a script... 377 | def main(): 378 | """Small main program""" 379 | import sys, getopt 380 | try: 381 | opts, args = getopt.getopt(sys.argv[1:], 'deut') 382 | except getopt.error as msg: 383 | sys.stdout = sys.stderr 384 | print(msg) 385 | print("""usage: %s [-d|-e|-u|-t] [file|-] 386 | -d, -u: decode 387 | -e: encode (default) 388 | -t: encode and decode string 'Aladdin:open sesame'"""%sys.argv[0]) 389 | sys.exit(2) 390 | func = encode 391 | for o, a in opts: 392 | if o == '-e': func = encode 393 | if o == '-d': func = decode 394 | if o == '-u': func = decode 395 | if o == '-t': test(); return 396 | if args and args[0] != '-': 397 | with open(args[0], 'rb') as f: 398 | func(f, sys.stdout.buffer) 399 | else: 400 | func(sys.stdin.buffer, sys.stdout.buffer) 401 | 402 | 403 | def test(): 404 | s0 = b"Aladdin:open sesame" 405 | print(repr(s0)) 406 | s1 = encodebytes(s0) 407 | print(repr(s1)) 408 | s2 = decodebytes(s1) 409 | print(repr(s2)) 410 | assert s0 == s2 411 | 412 | 413 | if __name__ == '__main__': 414 | main() 415 | -------------------------------------------------------------------------------- /lib/bme280.py: -------------------------------------------------------------------------------- 1 | # Authors: Paul Cunnane 2016, Peter Dahlebrg 2016 2 | # 3 | # This module borrows from the Adafruit BME280 Python library. Original 4 | # Copyright notices are reproduced below. 5 | # 6 | # Those libraries were written for the Raspberry Pi. This modification is 7 | # intended for the MicroPython and esp8266 boards. 8 | # 9 | # Copyright (c) 2014 Adafruit Industries 10 | # Author: Tony DiCola 11 | # 12 | # Based on the BMP280 driver with BME280 changes provided by 13 | # David J Taylor, Edinburgh (www.satsignal.eu) 14 | # 15 | # Based on Adafruit_I2C.py created by Kevin Townsend. 16 | # 17 | # Permission is hereby granted, free of charge, to any person obtaining a copy 18 | # of this software and associated documentation files (the "Software"), to deal 19 | # in the Software without restriction, including without limitation the rights 20 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 21 | # copies of the Software, and to permit persons to whom the Software is 22 | # furnished to do so, subject to the following conditions: 23 | # 24 | # The above copyright notice and this permission notice shall be included in 25 | # all copies or substantial portions of the Software. 26 | # 27 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 32 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 33 | # THE SOFTWARE. 34 | 35 | import time 36 | from ustruct import unpack, unpack_from 37 | from array import array 38 | 39 | # BME280 default address. 40 | BME280_I2CADDR = 0x76 41 | 42 | # Operating Modes 43 | BME280_OSAMPLE_1 = 1 44 | BME280_OSAMPLE_2 = 2 45 | BME280_OSAMPLE_4 = 3 46 | BME280_OSAMPLE_8 = 4 47 | BME280_OSAMPLE_16 = 5 48 | 49 | BME280_REGISTER_CONTROL_HUM = 0xF2 50 | BME280_REGISTER_CONTROL = 0xF4 51 | 52 | 53 | class BME280: 54 | 55 | def __init__(self, 56 | mode=BME280_OSAMPLE_1, 57 | address=BME280_I2CADDR, 58 | i2c=None, 59 | **kwargs): 60 | # Check that mode is valid. 61 | if mode not in [BME280_OSAMPLE_1, BME280_OSAMPLE_2, BME280_OSAMPLE_4, 62 | BME280_OSAMPLE_8, BME280_OSAMPLE_16]: 63 | raise ValueError( 64 | 'Unexpected mode value {0}. Set mode to one of ' 65 | 'BME280_ULTRALOWPOWER, BME280_STANDARD, BME280_HIGHRES, or ' 66 | 'BME280_ULTRAHIGHRES'.format(mode)) 67 | self._mode = mode 68 | self.address = address 69 | if i2c is None: 70 | raise ValueError('An I2C object is required.') 71 | self.i2c = i2c 72 | 73 | # load calibration data 74 | dig_88_a1 = self.i2c.readfrom_mem(self.address, 0x88, 26) 75 | dig_e1_e7 = self.i2c.readfrom_mem(self.address, 0xE1, 7) 76 | self.dig_T1, self.dig_T2, self.dig_T3, self.dig_P1, \ 77 | self.dig_P2, self.dig_P3, self.dig_P4, self.dig_P5, \ 78 | self.dig_P6, self.dig_P7, self.dig_P8, self.dig_P9, \ 79 | _, self.dig_H1 = unpack("> 4) 87 | 88 | self.dig_H6 = unpack_from("> 4 125 | raw_press = ((readout[0] << 16) | (readout[1] << 8) | readout[2]) >> 4 126 | # temperature(0xFA): ((msb << 16) | (lsb << 8) | xlsb) >> 4 127 | raw_temp = ((readout[3] << 16) | (readout[4] << 8) | readout[5]) >> 4 128 | # humidity(0xFD): (msb << 8) | lsb 129 | raw_hum = (readout[6] << 8) | readout[7] 130 | 131 | result[0] = raw_temp 132 | result[1] = raw_press 133 | result[2] = raw_hum 134 | 135 | def read_compensated_data(self, result=None): 136 | """ Reads the data from the sensor and returns the compensated data. 137 | 138 | Args: 139 | result: array of length 3 or alike where the result will be 140 | stored, in temperature, pressure, humidity order. You may use 141 | this to read out the sensor without allocating heap memory 142 | 143 | Returns: 144 | array with temperature, pressure, humidity. Will be the one from 145 | the result parameter if not None 146 | """ 147 | self.read_raw_data(self._l3_resultarray) 148 | raw_temp, raw_press, raw_hum = self._l3_resultarray 149 | # temperature 150 | var1 = ((raw_temp >> 3) - (self.dig_T1 << 1)) * (self.dig_T2 >> 11) 151 | var2 = (((((raw_temp >> 4) - self.dig_T1) * 152 | ((raw_temp >> 4) - self.dig_T1)) >> 12) * self.dig_T3) >> 14 153 | self.t_fine = var1 + var2 154 | temp = (self.t_fine * 5 + 128) >> 8 155 | 156 | # pressure 157 | var1 = self.t_fine - 128000 158 | var2 = var1 * var1 * self.dig_P6 159 | var2 = var2 + ((var1 * self.dig_P5) << 17) 160 | var2 = var2 + (self.dig_P4 << 35) 161 | var1 = (((var1 * var1 * self.dig_P3) >> 8) + 162 | ((var1 * self.dig_P2) << 12)) 163 | var1 = (((1 << 47) + var1) * self.dig_P1) >> 33 164 | if var1 == 0: 165 | pressure = 0 166 | else: 167 | p = 1048576 - raw_press 168 | p = (((p << 31) - var2) * 3125) // var1 169 | var1 = (self.dig_P9 * (p >> 13) * (p >> 13)) >> 25 170 | var2 = (self.dig_P8 * p) >> 19 171 | pressure = ((p + var1 + var2) >> 8) + (self.dig_P7 << 4) 172 | 173 | # humidity 174 | h = self.t_fine - 76800 175 | h = (((((raw_hum << 14) - (self.dig_H4 << 20) - 176 | (self.dig_H5 * h)) + 16384) 177 | >> 15) * (((((((h * self.dig_H6) >> 10) * 178 | (((h * self.dig_H3) >> 11) + 32768)) >> 10) + 179 | 2097152) * self.dig_H2 + 8192) >> 14)) 180 | h = h - (((((h >> 15) * (h >> 15)) >> 7) * self.dig_H1) >> 4) 181 | h = 0 if h < 0 else h 182 | h = 419430400 if h > 419430400 else h 183 | humidity = h >> 12 184 | 185 | if result: 186 | result[0] = temp 187 | result[1] = pressure 188 | result[2] = humidity 189 | return result 190 | 191 | return array("i", (temp, pressure, humidity)) 192 | 193 | @property 194 | def values(self): 195 | """ human readable values """ 196 | 197 | t, p, h = self.read_compensated_data() 198 | 199 | p = p // 256 200 | pi = p // 100 201 | pd = p - pi * 100 202 | 203 | hi = h // 1024 204 | hd = h * 100 // 1024 - hi * 100 205 | return ("{}C".format(t / 100), "{}.{:02d}hPa".format(pi, pd), 206 | "{}.{:02d}%".format(hi, hd)) -------------------------------------------------------------------------------- /lib/config.py: -------------------------------------------------------------------------------- 1 | import ujson as json 2 | 3 | 4 | class Config(): 5 | 6 | def config_load(self, configFile): 7 | # global sensor, hubAddress, deviceId, sharedAccessKey, owmApiKey, owmLocation 8 | try: 9 | print('Loading {0} settings'.format(configFile)) 10 | 11 | config_data = open(configFile) 12 | config = json.load(config_data) 13 | self.hubType = config['HubType'] 14 | 15 | # self.hub = __import__(hubType) 16 | self.sensor = __import__(config['SensorModule']) 17 | 18 | self.host = config['Host'] 19 | self.key = config['Key'] 20 | self.deviceId = config['DeviceId'] 21 | self.sampleRate = config['SampleRate'] 22 | self.wifiSsid = config['WifiSsid'] 23 | self.wifiPwd = config['WifiPwd'] 24 | self.location = config['Location'] 25 | self.eventHubName = None 26 | self.eventHubPolicyName = None 27 | 28 | if config.get('EventHubName'): 29 | self.eventHubName = config['EventHubName'] 30 | 31 | if config.get('EventHubPolicyName'): 32 | self.eventHubPolicyName = config['EventHubPolicyName'] 33 | 34 | except: 35 | print('Error loading config data') 36 | 37 | def __init__(self, configFile): 38 | self.config_load(configFile) 39 | -------------------------------------------------------------------------------- /lib/eventhub.py: -------------------------------------------------------------------------------- 1 | from hashlib.sha256 import sha256 2 | import base64 3 | import hmac 4 | import urlencode 5 | import ntptime 6 | import time 7 | 8 | 9 | class EventHub(): 10 | 11 | def __init__(self, hubAddress, eventHubName, eventHubPolicyName, sharedAccessKey): 12 | self.sharedAccessKey = sharedAccessKey 13 | self.EVENT_HUB_END_POINT = "/%s/publishers/%s/messages" 14 | self.eventHubPolicyName = eventHubPolicyName 15 | self.eventHubName = eventHubName 16 | self.hubAddress = hubAddress 17 | 18 | self.endpoint = self.EVENT_HUB_END_POINT % ( 19 | self.eventHubName, self.eventHubPolicyName) 20 | self.sasUrl = "https://" + self.hubAddress + self.endpoint 21 | 22 | # sas generator from https://github.com/bechynsky/AzureIoTDeviceClientPY/blob/master/DeviceClient.py 23 | def generate_sas_token(self, expiry=3600): # default to one hour expiry period 24 | urlencoder = urlencode.Urlencode() 25 | 26 | # Time Epoch: Unix port uses standard for POSIX systems epoch of 1970-01-01 00:00:00 UTC. 27 | # However, embedded ports use epoch of 2000-01-01 00:00:00 UTC. 28 | # 946684800 is the offset from embedded epoch and unix epoch bases 29 | 30 | now = 0 31 | 32 | while now == 0: 33 | try: 34 | now = ntptime.time() + 946684800 35 | except: 36 | time.sleep(1) 37 | 38 | ttl = now + expiry 39 | urlToSign = urlencoder.quote(self.sasUrl) 40 | 41 | msg = "{0}\n{1}".format(urlToSign, ttl).encode('utf-8') 42 | 43 | h = hmac.HMAC(bytearray(self.sharedAccessKey), 44 | msg=msg, digestmod=sha256) 45 | decodedDigest = base64.b64encode(h.digest()).decode() 46 | signature = urlencoder.quote(decodedDigest) 47 | sas = "SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}".format( 48 | urlToSign, signature, ttl, self.eventHubPolicyName) 49 | # print(sas) 50 | return sas 51 | -------------------------------------------------------------------------------- /lib/hashlib/__init__.py: -------------------------------------------------------------------------------- 1 | from .sha256 import sha224, sha256 2 | from .sha512 import sha384, sha512 3 | -------------------------------------------------------------------------------- /lib/hashlib/sha224.py: -------------------------------------------------------------------------------- 1 | from .sha256 import sha224 2 | -------------------------------------------------------------------------------- /lib/hashlib/sha256.py: -------------------------------------------------------------------------------- 1 | SHA_BLOCKSIZE = 64 2 | SHA_DIGESTSIZE = 32 3 | 4 | 5 | def new_shaobject(): 6 | return { 7 | 'digest': [0]*8, 8 | 'count_lo': 0, 9 | 'count_hi': 0, 10 | 'data': [0]* SHA_BLOCKSIZE, 11 | 'local': 0, 12 | 'digestsize': 0 13 | } 14 | 15 | ROR = lambda x, y: (((x & 0xffffffff) >> (y & 31)) | (x << (32 - (y & 31)))) & 0xffffffff 16 | Ch = lambda x, y, z: (z ^ (x & (y ^ z))) 17 | Maj = lambda x, y, z: (((x | y) & z) | (x & y)) 18 | S = lambda x, n: ROR(x, n) 19 | R = lambda x, n: (x & 0xffffffff) >> n 20 | Sigma0 = lambda x: (S(x, 2) ^ S(x, 13) ^ S(x, 22)) 21 | Sigma1 = lambda x: (S(x, 6) ^ S(x, 11) ^ S(x, 25)) 22 | Gamma0 = lambda x: (S(x, 7) ^ S(x, 18) ^ R(x, 3)) 23 | Gamma1 = lambda x: (S(x, 17) ^ S(x, 19) ^ R(x, 10)) 24 | 25 | def sha_transform(sha_info): 26 | W = [] 27 | 28 | d = sha_info['data'] 29 | for i in range(0,16): 30 | W.append( (d[4*i]<<24) + (d[4*i+1]<<16) + (d[4*i+2]<<8) + d[4*i+3]) 31 | 32 | for i in range(16,64): 33 | W.append( (Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]) & 0xffffffff ) 34 | 35 | ss = sha_info['digest'][:] 36 | 37 | def RND(a,b,c,d,e,f,g,h,i,ki): 38 | t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; 39 | t1 = Sigma0(a) + Maj(a, b, c); 40 | d += t0; 41 | h = t0 + t1; 42 | return d & 0xffffffff, h & 0xffffffff 43 | 44 | ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],0,0x428a2f98); 45 | ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],1,0x71374491); 46 | ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],2,0xb5c0fbcf); 47 | ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],3,0xe9b5dba5); 48 | ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],4,0x3956c25b); 49 | ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],5,0x59f111f1); 50 | ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],6,0x923f82a4); 51 | ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],7,0xab1c5ed5); 52 | ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],8,0xd807aa98); 53 | ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],9,0x12835b01); 54 | ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],10,0x243185be); 55 | ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],11,0x550c7dc3); 56 | ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],12,0x72be5d74); 57 | ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],13,0x80deb1fe); 58 | ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],14,0x9bdc06a7); 59 | ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],15,0xc19bf174); 60 | ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],16,0xe49b69c1); 61 | ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],17,0xefbe4786); 62 | ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],18,0x0fc19dc6); 63 | ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],19,0x240ca1cc); 64 | ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],20,0x2de92c6f); 65 | ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],21,0x4a7484aa); 66 | ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],22,0x5cb0a9dc); 67 | ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],23,0x76f988da); 68 | ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],24,0x983e5152); 69 | ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],25,0xa831c66d); 70 | ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],26,0xb00327c8); 71 | ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],27,0xbf597fc7); 72 | ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],28,0xc6e00bf3); 73 | ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],29,0xd5a79147); 74 | ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],30,0x06ca6351); 75 | ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],31,0x14292967); 76 | ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],32,0x27b70a85); 77 | ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],33,0x2e1b2138); 78 | ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],34,0x4d2c6dfc); 79 | ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],35,0x53380d13); 80 | ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],36,0x650a7354); 81 | ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],37,0x766a0abb); 82 | ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],38,0x81c2c92e); 83 | ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],39,0x92722c85); 84 | ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],40,0xa2bfe8a1); 85 | ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],41,0xa81a664b); 86 | ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],42,0xc24b8b70); 87 | ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],43,0xc76c51a3); 88 | ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],44,0xd192e819); 89 | ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],45,0xd6990624); 90 | ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],46,0xf40e3585); 91 | ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],47,0x106aa070); 92 | ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],48,0x19a4c116); 93 | ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],49,0x1e376c08); 94 | ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],50,0x2748774c); 95 | ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],51,0x34b0bcb5); 96 | ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],52,0x391c0cb3); 97 | ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],53,0x4ed8aa4a); 98 | ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],54,0x5b9cca4f); 99 | ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],55,0x682e6ff3); 100 | ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],56,0x748f82ee); 101 | ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],57,0x78a5636f); 102 | ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],58,0x84c87814); 103 | ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],59,0x8cc70208); 104 | ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],60,0x90befffa); 105 | ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],61,0xa4506ceb); 106 | ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],62,0xbef9a3f7); 107 | ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],63,0xc67178f2); 108 | 109 | dig = [] 110 | for i, x in enumerate(sha_info['digest']): 111 | dig.append( (x + ss[i]) & 0xffffffff ) 112 | sha_info['digest'] = dig 113 | 114 | def sha_init(): 115 | sha_info = new_shaobject() 116 | sha_info['digest'] = [0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19] 117 | sha_info['count_lo'] = 0 118 | sha_info['count_hi'] = 0 119 | sha_info['local'] = 0 120 | sha_info['digestsize'] = 32 121 | return sha_info 122 | 123 | def sha224_init(): 124 | sha_info = new_shaobject() 125 | sha_info['digest'] = [0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4] 126 | sha_info['count_lo'] = 0 127 | sha_info['count_hi'] = 0 128 | sha_info['local'] = 0 129 | sha_info['digestsize'] = 28 130 | return sha_info 131 | 132 | def getbuf(s): 133 | if isinstance(s, str): 134 | return s.encode('ascii') 135 | else: 136 | return bytes(s) 137 | 138 | def sha_update(sha_info, buffer): 139 | if isinstance(buffer, str): 140 | raise TypeError("Unicode strings must be encoded before hashing") 141 | count = len(buffer) 142 | buffer_idx = 0 143 | clo = (sha_info['count_lo'] + (count << 3)) & 0xffffffff 144 | if clo < sha_info['count_lo']: 145 | sha_info['count_hi'] += 1 146 | sha_info['count_lo'] = clo 147 | 148 | sha_info['count_hi'] += (count >> 29) 149 | 150 | if sha_info['local']: 151 | i = SHA_BLOCKSIZE - sha_info['local'] 152 | if i > count: 153 | i = count 154 | 155 | # copy buffer 156 | for x in enumerate(buffer[buffer_idx:buffer_idx+i]): 157 | sha_info['data'][sha_info['local']+x[0]] = x[1] 158 | 159 | count -= i 160 | buffer_idx += i 161 | 162 | sha_info['local'] += i 163 | if sha_info['local'] == SHA_BLOCKSIZE: 164 | sha_transform(sha_info) 165 | sha_info['local'] = 0 166 | else: 167 | return 168 | 169 | while count >= SHA_BLOCKSIZE: 170 | # copy buffer 171 | sha_info['data'] = list(buffer[buffer_idx:buffer_idx + SHA_BLOCKSIZE]) 172 | count -= SHA_BLOCKSIZE 173 | buffer_idx += SHA_BLOCKSIZE 174 | sha_transform(sha_info) 175 | 176 | 177 | # copy buffer 178 | pos = sha_info['local'] 179 | sha_info['data'][pos:pos+count] = list(buffer[buffer_idx:buffer_idx + count]) 180 | sha_info['local'] = count 181 | 182 | def sha_final(sha_info): 183 | lo_bit_count = sha_info['count_lo'] 184 | hi_bit_count = sha_info['count_hi'] 185 | count = (lo_bit_count >> 3) & 0x3f 186 | sha_info['data'][count] = 0x80; 187 | count += 1 188 | if count > SHA_BLOCKSIZE - 8: 189 | # zero the bytes in data after the count 190 | sha_info['data'] = sha_info['data'][:count] + ([0] * (SHA_BLOCKSIZE - count)) 191 | sha_transform(sha_info) 192 | # zero bytes in data 193 | sha_info['data'] = [0] * SHA_BLOCKSIZE 194 | else: 195 | sha_info['data'] = sha_info['data'][:count] + ([0] * (SHA_BLOCKSIZE - count)) 196 | 197 | sha_info['data'][56] = (hi_bit_count >> 24) & 0xff 198 | sha_info['data'][57] = (hi_bit_count >> 16) & 0xff 199 | sha_info['data'][58] = (hi_bit_count >> 8) & 0xff 200 | sha_info['data'][59] = (hi_bit_count >> 0) & 0xff 201 | sha_info['data'][60] = (lo_bit_count >> 24) & 0xff 202 | sha_info['data'][61] = (lo_bit_count >> 16) & 0xff 203 | sha_info['data'][62] = (lo_bit_count >> 8) & 0xff 204 | sha_info['data'][63] = (lo_bit_count >> 0) & 0xff 205 | 206 | sha_transform(sha_info) 207 | 208 | dig = [] 209 | for i in sha_info['digest']: 210 | dig.extend([ ((i>>24) & 0xff), ((i>>16) & 0xff), ((i>>8) & 0xff), (i & 0xff) ]) 211 | return bytes(dig) 212 | 213 | class sha256(object): 214 | digest_size = digestsize = SHA_DIGESTSIZE 215 | block_size = SHA_BLOCKSIZE 216 | 217 | def __init__(self, s=None): 218 | self._sha = sha_init() 219 | if s: 220 | sha_update(self._sha, getbuf(s)) 221 | 222 | def update(self, s): 223 | sha_update(self._sha, getbuf(s)) 224 | 225 | def digest(self): 226 | return sha_final(self._sha.copy())[:self._sha['digestsize']] 227 | 228 | def hexdigest(self): 229 | return ''.join(['%.2x' % i for i in self.digest()]) 230 | 231 | def copy(self): 232 | new = sha256() 233 | new._sha = self._sha.copy() 234 | return new 235 | 236 | class sha224(sha256): 237 | digest_size = digestsize = 28 238 | 239 | def __init__(self, s=None): 240 | self._sha = sha224_init() 241 | if s: 242 | sha_update(self._sha, getbuf(s)) 243 | 244 | def copy(self): 245 | new = sha224() 246 | new._sha = self._sha.copy() 247 | return new 248 | 249 | def test(): 250 | a_str = "just a test string" 251 | 252 | assert b"\xe3\xb0\xc4B\x98\xfc\x1c\x14\x9a\xfb\xf4\xc8\x99o\xb9$'\xaeA\xe4d\x9b\x93L\xa4\x95\x99\x1bxR\xb8U" == sha256().digest() 253 | assert 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' == sha256().hexdigest() 254 | assert 'd7b553c6f09ac85d142415f857c5310f3bbbe7cdd787cce4b985acedd585266f' == sha256(a_str).hexdigest() 255 | assert '8113ebf33c97daa9998762aacafe750c7cefc2b2f173c90c59663a57fe626f21' == sha256(a_str*7).hexdigest() 256 | 257 | s = sha256(a_str) 258 | s.update(a_str) 259 | assert '03d9963e05a094593190b6fc794cb1a3e1ac7d7883f0b5855268afeccc70d461' == s.hexdigest() 260 | 261 | if __name__ == "__main__": 262 | test() 263 | 264 | 265 | -------------------------------------------------------------------------------- /lib/hashlib/sha384.py: -------------------------------------------------------------------------------- 1 | from .sha512 import sha384 2 | -------------------------------------------------------------------------------- /lib/hashlib/sha512.py: -------------------------------------------------------------------------------- 1 | """ 2 | This code was Ported from CPython's sha512module.c 3 | """ 4 | 5 | SHA_BLOCKSIZE = 128 6 | SHA_DIGESTSIZE = 64 7 | 8 | 9 | def new_shaobject(): 10 | return { 11 | 'digest': [0]*8, 12 | 'count_lo': 0, 13 | 'count_hi': 0, 14 | 'data': [0]* SHA_BLOCKSIZE, 15 | 'local': 0, 16 | 'digestsize': 0 17 | } 18 | 19 | ROR64 = lambda x, y: (((x & 0xffffffffffffffff) >> (y & 63)) | (x << (64 - (y & 63)))) & 0xffffffffffffffff 20 | Ch = lambda x, y, z: (z ^ (x & (y ^ z))) 21 | Maj = lambda x, y, z: (((x | y) & z) | (x & y)) 22 | S = lambda x, n: ROR64(x, n) 23 | R = lambda x, n: (x & 0xffffffffffffffff) >> n 24 | Sigma0 = lambda x: (S(x, 28) ^ S(x, 34) ^ S(x, 39)) 25 | Sigma1 = lambda x: (S(x, 14) ^ S(x, 18) ^ S(x, 41)) 26 | Gamma0 = lambda x: (S(x, 1) ^ S(x, 8) ^ R(x, 7)) 27 | Gamma1 = lambda x: (S(x, 19) ^ S(x, 61) ^ R(x, 6)) 28 | 29 | def sha_transform(sha_info): 30 | W = [] 31 | 32 | d = sha_info['data'] 33 | for i in range(0,16): 34 | W.append( (d[8*i]<<56) + (d[8*i+1]<<48) + (d[8*i+2]<<40) + (d[8*i+3]<<32) + (d[8*i+4]<<24) + (d[8*i+5]<<16) + (d[8*i+6]<<8) + d[8*i+7]) 35 | 36 | for i in range(16,80): 37 | W.append( (Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]) & 0xffffffffffffffff ) 38 | 39 | ss = sha_info['digest'][:] 40 | 41 | def RND(a,b,c,d,e,f,g,h,i,ki): 42 | t0 = (h + Sigma1(e) + Ch(e, f, g) + ki + W[i]) & 0xffffffffffffffff 43 | t1 = (Sigma0(a) + Maj(a, b, c)) & 0xffffffffffffffff 44 | d = (d + t0) & 0xffffffffffffffff 45 | h = (t0 + t1) & 0xffffffffffffffff 46 | return d & 0xffffffffffffffff, h & 0xffffffffffffffff 47 | 48 | ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],0,0x428a2f98d728ae22) 49 | ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],1,0x7137449123ef65cd) 50 | ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],2,0xb5c0fbcfec4d3b2f) 51 | ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],3,0xe9b5dba58189dbbc) 52 | ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],4,0x3956c25bf348b538) 53 | ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],5,0x59f111f1b605d019) 54 | ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],6,0x923f82a4af194f9b) 55 | ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],7,0xab1c5ed5da6d8118) 56 | ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],8,0xd807aa98a3030242) 57 | ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],9,0x12835b0145706fbe) 58 | ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],10,0x243185be4ee4b28c) 59 | ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],11,0x550c7dc3d5ffb4e2) 60 | ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],12,0x72be5d74f27b896f) 61 | ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],13,0x80deb1fe3b1696b1) 62 | ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],14,0x9bdc06a725c71235) 63 | ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],15,0xc19bf174cf692694) 64 | ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],16,0xe49b69c19ef14ad2) 65 | ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],17,0xefbe4786384f25e3) 66 | ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],18,0x0fc19dc68b8cd5b5) 67 | ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],19,0x240ca1cc77ac9c65) 68 | ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],20,0x2de92c6f592b0275) 69 | ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],21,0x4a7484aa6ea6e483) 70 | ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],22,0x5cb0a9dcbd41fbd4) 71 | ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],23,0x76f988da831153b5) 72 | ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],24,0x983e5152ee66dfab) 73 | ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],25,0xa831c66d2db43210) 74 | ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],26,0xb00327c898fb213f) 75 | ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],27,0xbf597fc7beef0ee4) 76 | ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],28,0xc6e00bf33da88fc2) 77 | ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],29,0xd5a79147930aa725) 78 | ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],30,0x06ca6351e003826f) 79 | ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],31,0x142929670a0e6e70) 80 | ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],32,0x27b70a8546d22ffc) 81 | ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],33,0x2e1b21385c26c926) 82 | ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],34,0x4d2c6dfc5ac42aed) 83 | ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],35,0x53380d139d95b3df) 84 | ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],36,0x650a73548baf63de) 85 | ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],37,0x766a0abb3c77b2a8) 86 | ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],38,0x81c2c92e47edaee6) 87 | ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],39,0x92722c851482353b) 88 | ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],40,0xa2bfe8a14cf10364) 89 | ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],41,0xa81a664bbc423001) 90 | ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],42,0xc24b8b70d0f89791) 91 | ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],43,0xc76c51a30654be30) 92 | ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],44,0xd192e819d6ef5218) 93 | ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],45,0xd69906245565a910) 94 | ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],46,0xf40e35855771202a) 95 | ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],47,0x106aa07032bbd1b8) 96 | ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],48,0x19a4c116b8d2d0c8) 97 | ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],49,0x1e376c085141ab53) 98 | ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],50,0x2748774cdf8eeb99) 99 | ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],51,0x34b0bcb5e19b48a8) 100 | ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],52,0x391c0cb3c5c95a63) 101 | ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],53,0x4ed8aa4ae3418acb) 102 | ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],54,0x5b9cca4f7763e373) 103 | ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],55,0x682e6ff3d6b2b8a3) 104 | ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],56,0x748f82ee5defb2fc) 105 | ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],57,0x78a5636f43172f60) 106 | ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],58,0x84c87814a1f0ab72) 107 | ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],59,0x8cc702081a6439ec) 108 | ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],60,0x90befffa23631e28) 109 | ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],61,0xa4506cebde82bde9) 110 | ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],62,0xbef9a3f7b2c67915) 111 | ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],63,0xc67178f2e372532b) 112 | ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],64,0xca273eceea26619c) 113 | ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],65,0xd186b8c721c0c207) 114 | ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],66,0xeada7dd6cde0eb1e) 115 | ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],67,0xf57d4f7fee6ed178) 116 | ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],68,0x06f067aa72176fba) 117 | ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],69,0x0a637dc5a2c898a6) 118 | ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],70,0x113f9804bef90dae) 119 | ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],71,0x1b710b35131c471b) 120 | ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],72,0x28db77f523047d84) 121 | ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],73,0x32caab7b40c72493) 122 | ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],74,0x3c9ebe0a15c9bebc) 123 | ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],75,0x431d67c49c100d4c) 124 | ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],76,0x4cc5d4becb3e42b6) 125 | ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],77,0x597f299cfc657e2a) 126 | ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],78,0x5fcb6fab3ad6faec) 127 | ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],79,0x6c44198c4a475817) 128 | 129 | dig = [] 130 | for i, x in enumerate(sha_info['digest']): 131 | dig.append( (x + ss[i]) & 0xffffffffffffffff ) 132 | sha_info['digest'] = dig 133 | 134 | def sha_init(): 135 | sha_info = new_shaobject() 136 | sha_info['digest'] = [ 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179] 137 | sha_info['count_lo'] = 0 138 | sha_info['count_hi'] = 0 139 | sha_info['local'] = 0 140 | sha_info['digestsize'] = 64 141 | return sha_info 142 | 143 | def sha384_init(): 144 | sha_info = new_shaobject() 145 | sha_info['digest'] = [ 0xcbbb9d5dc1059ed8, 0x629a292a367cd507, 0x9159015a3070dd17, 0x152fecd8f70e5939, 0x67332667ffc00b31, 0x8eb44a8768581511, 0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4] 146 | sha_info['count_lo'] = 0 147 | sha_info['count_hi'] = 0 148 | sha_info['local'] = 0 149 | sha_info['digestsize'] = 48 150 | return sha_info 151 | 152 | def getbuf(s): 153 | if isinstance(s, str): 154 | return s.encode('ascii') 155 | else: 156 | return bytes(s) 157 | 158 | def sha_update(sha_info, buffer): 159 | if isinstance(buffer, str): 160 | raise TypeError("Unicode strings must be encoded before hashing") 161 | count = len(buffer) 162 | buffer_idx = 0 163 | clo = (sha_info['count_lo'] + (count << 3)) & 0xffffffff 164 | if clo < sha_info['count_lo']: 165 | sha_info['count_hi'] += 1 166 | sha_info['count_lo'] = clo 167 | 168 | sha_info['count_hi'] += (count >> 29) 169 | 170 | if sha_info['local']: 171 | i = SHA_BLOCKSIZE - sha_info['local'] 172 | if i > count: 173 | i = count 174 | 175 | # copy buffer 176 | for x in enumerate(buffer[buffer_idx:buffer_idx+i]): 177 | sha_info['data'][sha_info['local']+x[0]] = x[1] 178 | 179 | count -= i 180 | buffer_idx += i 181 | 182 | sha_info['local'] += i 183 | if sha_info['local'] == SHA_BLOCKSIZE: 184 | sha_transform(sha_info) 185 | sha_info['local'] = 0 186 | else: 187 | return 188 | 189 | while count >= SHA_BLOCKSIZE: 190 | # copy buffer 191 | sha_info['data'] = list(buffer[buffer_idx:buffer_idx + SHA_BLOCKSIZE]) 192 | count -= SHA_BLOCKSIZE 193 | buffer_idx += SHA_BLOCKSIZE 194 | sha_transform(sha_info) 195 | 196 | # copy buffer 197 | pos = sha_info['local'] 198 | sha_info['data'][pos:pos+count] = list(buffer[buffer_idx:buffer_idx + count]) 199 | sha_info['local'] = count 200 | 201 | def sha_final(sha_info): 202 | lo_bit_count = sha_info['count_lo'] 203 | hi_bit_count = sha_info['count_hi'] 204 | count = (lo_bit_count >> 3) & 0x7f 205 | sha_info['data'][count] = 0x80; 206 | count += 1 207 | if count > SHA_BLOCKSIZE - 16: 208 | # zero the bytes in data after the count 209 | sha_info['data'] = sha_info['data'][:count] + ([0] * (SHA_BLOCKSIZE - count)) 210 | sha_transform(sha_info) 211 | # zero bytes in data 212 | sha_info['data'] = [0] * SHA_BLOCKSIZE 213 | else: 214 | sha_info['data'] = sha_info['data'][:count] + ([0] * (SHA_BLOCKSIZE - count)) 215 | 216 | sha_info['data'][112] = 0; 217 | sha_info['data'][113] = 0; 218 | sha_info['data'][114] = 0; 219 | sha_info['data'][115] = 0; 220 | sha_info['data'][116] = 0; 221 | sha_info['data'][117] = 0; 222 | sha_info['data'][118] = 0; 223 | sha_info['data'][119] = 0; 224 | 225 | sha_info['data'][120] = (hi_bit_count >> 24) & 0xff 226 | sha_info['data'][121] = (hi_bit_count >> 16) & 0xff 227 | sha_info['data'][122] = (hi_bit_count >> 8) & 0xff 228 | sha_info['data'][123] = (hi_bit_count >> 0) & 0xff 229 | sha_info['data'][124] = (lo_bit_count >> 24) & 0xff 230 | sha_info['data'][125] = (lo_bit_count >> 16) & 0xff 231 | sha_info['data'][126] = (lo_bit_count >> 8) & 0xff 232 | sha_info['data'][127] = (lo_bit_count >> 0) & 0xff 233 | 234 | sha_transform(sha_info) 235 | 236 | dig = [] 237 | for i in sha_info['digest']: 238 | dig.extend([ ((i>>56) & 0xff), ((i>>48) & 0xff), ((i>>40) & 0xff), ((i>>32) & 0xff), ((i>>24) & 0xff), ((i>>16) & 0xff), ((i>>8) & 0xff), (i & 0xff) ]) 239 | return bytes(dig) 240 | 241 | class sha512(object): 242 | digest_size = digestsize = SHA_DIGESTSIZE 243 | block_size = SHA_BLOCKSIZE 244 | 245 | def __init__(self, s=None): 246 | self._sha = sha_init() 247 | if s: 248 | sha_update(self._sha, getbuf(s)) 249 | 250 | def update(self, s): 251 | sha_update(self._sha, getbuf(s)) 252 | 253 | def digest(self): 254 | return sha_final(self._sha.copy())[:self._sha['digestsize']] 255 | 256 | def hexdigest(self): 257 | return ''.join(['%.2x' % i for i in self.digest()]) 258 | 259 | def copy(self): 260 | new = sha512() 261 | new._sha = self._sha.copy() 262 | return new 263 | 264 | class sha384(sha512): 265 | digest_size = digestsize = 48 266 | 267 | def __init__(self, s=None): 268 | self._sha = sha384_init() 269 | if s: 270 | sha_update(self._sha, getbuf(s)) 271 | 272 | def copy(self): 273 | new = sha384() 274 | new._sha = self._sha.copy() 275 | return new 276 | 277 | def test(): 278 | a_str = "just a test string" 279 | 280 | assert sha512().digest() == b"\xcf\x83\xe15~\xef\xb8\xbd\xf1T(P\xd6m\x80\x07\xd6 \xe4\x05\x0bW\x15\xdc\x83\xf4\xa9!\xd3l\xe9\xceG\xd0\xd1<]\x85\xf2\xb0\xff\x83\x18\xd2\x87~\xec/c\xb91\xbdGAz\x81\xa582z\xf9'\xda>" 281 | assert sha512().hexdigest() == 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e' 282 | assert sha512(a_str).hexdigest() == '68be4c6664af867dd1d01c8d77e963d87d77b702400c8fabae355a41b8927a5a5533a7f1c28509bbd65c5f3ac716f33be271fbda0ca018b71a84708c9fae8a53' 283 | assert sha512(a_str*7).hexdigest() == '3233acdbfcfff9bff9fc72401d31dbffa62bd24e9ec846f0578d647da73258d9f0879f7fde01fe2cc6516af3f343807fdef79e23d696c923d79931db46bf1819' 284 | 285 | s = sha512(a_str) 286 | s.update(a_str) 287 | assert s.hexdigest() == '341aeb668730bbb48127d5531115f3c39d12cb9586a6ca770898398aff2411087cfe0b570689adf328cddeb1f00803acce6737a19f310b53bbdb0320828f75bb' 288 | 289 | if __name__ == "__main__": 290 | test() 291 | -------------------------------------------------------------------------------- /lib/hmac.py: -------------------------------------------------------------------------------- 1 | """HMAC (Keyed-Hashing for Message Authentication) Python module. 2 | 3 | Implements the HMAC algorithm as described by RFC 2104. 4 | """ 5 | 6 | import warnings as _warnings 7 | #from _operator import _compare_digest as compare_digest 8 | import hashlib as _hashlib 9 | PendingDeprecationWarning = None 10 | RuntimeWarning = None 11 | 12 | trans_5C = bytes((x ^ 0x5C) for x in range(256)) 13 | trans_36 = bytes((x ^ 0x36) for x in range(256)) 14 | 15 | def translate(d, t): 16 | return bytes(t[x] for x in d) 17 | 18 | # The size of the digests returned by HMAC depends on the underlying 19 | # hashing module used. Use digest_size from the instance of HMAC instead. 20 | digest_size = None 21 | 22 | 23 | 24 | class HMAC: 25 | """RFC 2104 HMAC class. Also complies with RFC 4231. 26 | 27 | This supports the API for Cryptographic Hash Functions (PEP 247). 28 | """ 29 | blocksize = 64 # 512-bit HMAC; can be changed in subclasses. 30 | 31 | def __init__(self, key, msg = None, digestmod = None): 32 | """Create a new HMAC object. 33 | 34 | key: key for the keyed hash object. 35 | msg: Initial input for the hash, if provided. 36 | digestmod: A module supporting PEP 247. *OR* 37 | A hashlib constructor returning a new hash object. *OR* 38 | A hash name suitable for hashlib.new(). 39 | Defaults to hashlib.md5. 40 | Implicit default to hashlib.md5 is deprecated and will be 41 | removed in Python 3.6. 42 | 43 | Note: key and msg must be a bytes or bytearray objects. 44 | """ 45 | 46 | if not isinstance(key, (bytes, bytearray)): 47 | raise TypeError("key: expected bytes or bytearray, but got %r" % type(key).__name__) 48 | 49 | if digestmod is None: 50 | _warnings.warn("HMAC() without an explicit digestmod argument " 51 | "is deprecated.", PendingDeprecationWarning, 2) 52 | digestmod = _hashlib.sha256 53 | 54 | if callable(digestmod): 55 | self.digest_cons = digestmod 56 | elif isinstance(digestmod, str): 57 | self.digest_cons = lambda d=b'': _hashlib.new(digestmod, d) 58 | else: 59 | self.digest_cons = lambda d=b'': digestmod.new(d) 60 | 61 | self.outer = self.digest_cons() 62 | self.inner = self.digest_cons() 63 | self.digest_size = self.inner.digest_size 64 | 65 | if hasattr(self.inner, 'block_size'): 66 | blocksize = self.inner.block_size 67 | if blocksize < 16: 68 | _warnings.warn('block_size of %d seems too small; using our ' 69 | 'default of %d.' % (blocksize, self.blocksize), 70 | RuntimeWarning, 2) 71 | blocksize = self.blocksize 72 | else: 73 | _warnings.warn('No block_size attribute on given digest object; ' 74 | 'Assuming %d.' % (self.blocksize), 75 | RuntimeWarning, 2) 76 | blocksize = self.blocksize 77 | 78 | # self.blocksize is the default blocksize. self.block_size is 79 | # effective block size as well as the public API attribute. 80 | self.block_size = blocksize 81 | 82 | if len(key) > blocksize: 83 | key = self.digest_cons(key).digest() 84 | 85 | key = key + bytes(blocksize - len(key)) 86 | self.outer.update(translate(key, trans_5C)) 87 | self.inner.update(translate(key, trans_36)) 88 | if msg is not None: 89 | self.update(msg) 90 | 91 | @property 92 | def name(self): 93 | return "hmac-" + self.inner.name 94 | 95 | def update(self, msg): 96 | """Update this hashing object with the string msg. 97 | """ 98 | self.inner.update(msg) 99 | 100 | def copy(self): 101 | """Return a separate copy of this hashing object. 102 | 103 | An update to this copy won't affect the original object. 104 | """ 105 | # Call __new__ directly to avoid the expensive __init__. 106 | other = self.__class__.__new__(self.__class__) 107 | other.digest_cons = self.digest_cons 108 | other.digest_size = self.digest_size 109 | other.inner = self.inner.copy() 110 | other.outer = self.outer.copy() 111 | return other 112 | 113 | def _current(self): 114 | """Return a hash object for the current state. 115 | 116 | To be used only internally with digest() and hexdigest(). 117 | """ 118 | h = self.outer.copy() 119 | h.update(self.inner.digest()) 120 | return h 121 | 122 | def digest(self): 123 | """Return the hash value of this hashing object. 124 | 125 | This returns a string containing 8-bit data. The object is 126 | not altered in any way by this function; you can continue 127 | updating the object after calling this function. 128 | """ 129 | h = self._current() 130 | return h.digest() 131 | 132 | def hexdigest(self): 133 | """Like digest(), but returns a string of hexadecimal digits instead. 134 | """ 135 | h = self._current() 136 | return h.hexdigest() 137 | 138 | def new(key, msg = None, digestmod = None): 139 | """Create a new hashing object and return it. 140 | 141 | key: The starting key for the hash. 142 | msg: if available, will immediately be hashed into the object's starting 143 | state. 144 | 145 | You can now feed arbitrary strings into the object using its update() 146 | method, and can ask for the hash value at any time by calling its digest() 147 | method. 148 | """ 149 | return HMAC(key, msg, digestmod) 150 | -------------------------------------------------------------------------------- /lib/iothub.py: -------------------------------------------------------------------------------- 1 | from hashlib.sha256 import sha256 2 | import base64 3 | import hmac 4 | import urlencode 5 | import ntptime 6 | import time 7 | 8 | 9 | class IotHub(): 10 | 11 | def __init__(self, hubAddress, deviceId, sharedAccessKey): 12 | self.sharedAccessKey = sharedAccessKey 13 | self.sasUrl = hubAddress + '/devices/' + deviceId 14 | self.hubUser = hubAddress + '/' + deviceId 15 | self.endpoint = "/devices/" + deviceId + "/messages/events?api-version=2016-02-03") 16 | 17 | 18 | # sas generator from https://github.com/bechynsky/AzureIoTDeviceClientPY/blob/master/DeviceClient.py 19 | def generate_sas_token(self, expiry = 3600): # default to one hour expiry period 20 | urlencoder=urlencode.Urlencode() 21 | 22 | # Time Epoch: Unix port uses standard for POSIX systems epoch of 1970-01-01 00:00:00 UTC. 23 | # However, embedded ports use epoch of 2000-01-01 00:00:00 UTC. 24 | # 946684800 is the offset from embedded epoch and unix epoch bases 25 | 26 | now=0 27 | 28 | while now == 0: 29 | try: 30 | now=ntptime.time() + 946684800 31 | except: 32 | time.sleep(1) 33 | 34 | ttl=now + expiry 35 | urlToSign=urlencoder.quote(self.sasUrl) 36 | msg="{0}\n{1}".format(urlToSign, ttl).encode('utf-8') 37 | key=base64.b64decode(self.sharedAccessKey) 38 | h=hmac.HMAC(key, msg = msg, digestmod = sha256) 39 | decodedDigest=base64.b64encode(h.digest()).decode() 40 | signature=urlencoder.quote(decodedDigest) 41 | sas="SharedAccessSignature sr={0}&sig={1}&se={2}".format( 42 | urlToSign, signature, ttl) 43 | # print(sas) 44 | return sas 45 | -------------------------------------------------------------------------------- /lib/sensor_bme280.py: -------------------------------------------------------------------------------- 1 | import bme280 2 | 3 | 4 | class Sensor(): 5 | def __init__(self, i2c_config): 6 | self.bme = bme280.BME280(i2c=i2c_config, mode=5) 7 | 8 | def measure(self): 9 | v = self.bme.values 10 | 11 | temperature = v[0][:-1] 12 | pressure = v[1][:-3] 13 | humidity = v[2][:-1] 14 | 15 | return (temperature, pressure, humidity) 16 | -------------------------------------------------------------------------------- /lib/sensor_fake.py: -------------------------------------------------------------------------------- 1 | 2 | class Sensor(): 3 | def __init__(self, ignore='ignore'): 4 | pass 5 | 6 | def measure(self): 7 | temperature = 21 8 | pressure = 1008 9 | humidity = 55 10 | 11 | return (temperature, pressure, humidity) 12 | -------------------------------------------------------------------------------- /lib/ssd1306.py: -------------------------------------------------------------------------------- 1 | # MicroPython SSD1306 OLED driver, I2C and SPI interfaces 2 | 3 | from micropython import const 4 | import framebuf 5 | 6 | 7 | # register definitions 8 | SET_CONTRAST = const(0x81) 9 | SET_ENTIRE_ON = const(0xa4) 10 | SET_NORM_INV = const(0xa6) 11 | SET_DISP = const(0xae) 12 | SET_MEM_ADDR = const(0x20) 13 | SET_COL_ADDR = const(0x21) 14 | SET_PAGE_ADDR = const(0x22) 15 | SET_DISP_START_LINE = const(0x40) 16 | SET_SEG_REMAP = const(0xa0) 17 | SET_MUX_RATIO = const(0xa8) 18 | SET_COM_OUT_DIR = const(0xc0) 19 | SET_DISP_OFFSET = const(0xd3) 20 | SET_COM_PIN_CFG = const(0xda) 21 | SET_DISP_CLK_DIV = const(0xd5) 22 | SET_PRECHARGE = const(0xd9) 23 | SET_VCOM_DESEL = const(0xdb) 24 | SET_CHARGE_PUMP = const(0x8d) 25 | 26 | 27 | class SSD1306: 28 | def __init__(self, width, height, external_vcc): 29 | self.width = width 30 | self.height = height 31 | self.external_vcc = external_vcc 32 | self.pages = self.height // 8 33 | self.buffer = bytearray(self.pages * self.width) 34 | fb = framebuf.FrameBuffer(self.buffer, self.width, self.height, framebuf.MONO_VLSB) 35 | self.framebuf = fb 36 | # Provide methods for accessing FrameBuffer graphics primitives. This is a 37 | # workround because inheritance from a native class is currently unsupported. 38 | # http://docs.micropython.org/en/latest/pyboard/library/framebuf.html 39 | self.fill = fb.fill 40 | self.pixel = fb.pixel 41 | self.hline = fb.hline 42 | self.vline = fb.vline 43 | self.line = fb.line 44 | self.rect = fb.rect 45 | self.fill_rect = fb.fill_rect 46 | self.text = fb.text 47 | self.scroll = fb.scroll 48 | self.blit = fb.blit 49 | self.init_display() 50 | 51 | def init_display(self): 52 | for cmd in ( 53 | SET_DISP | 0x00, # off 54 | # address setting 55 | SET_MEM_ADDR, 0x00, # horizontal 56 | # resolution and layout 57 | SET_DISP_START_LINE | 0x00, 58 | SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0 59 | SET_MUX_RATIO, self.height - 1, 60 | SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0 61 | SET_DISP_OFFSET, 0x00, 62 | SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12, 63 | # timing and driving scheme 64 | SET_DISP_CLK_DIV, 0x80, 65 | SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1, 66 | SET_VCOM_DESEL, 0x30, # 0.83*Vcc 67 | # display 68 | SET_CONTRAST, 0xff, # maximum 69 | SET_ENTIRE_ON, # output follows RAM contents 70 | SET_NORM_INV, # not inverted 71 | # charge pump 72 | SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14, 73 | SET_DISP | 0x01): # on 74 | self.write_cmd(cmd) 75 | self.fill(0) 76 | self.show() 77 | 78 | def poweroff(self): 79 | self.write_cmd(SET_DISP | 0x00) 80 | 81 | def poweron(self): 82 | self.write_cmd(SET_DISP | 0x01) 83 | 84 | def contrast(self, contrast): 85 | self.write_cmd(SET_CONTRAST) 86 | self.write_cmd(contrast) 87 | 88 | def invert(self, invert): 89 | self.write_cmd(SET_NORM_INV | (invert & 1)) 90 | 91 | def show(self): 92 | x0 = 0 93 | x1 = self.width - 1 94 | if self.width == 64: 95 | # displays with width of 64 pixels are shifted by 32 96 | x0 += 32 97 | x1 += 32 98 | self.write_cmd(SET_COL_ADDR) 99 | self.write_cmd(x0) 100 | self.write_cmd(x1) 101 | self.write_cmd(SET_PAGE_ADDR) 102 | self.write_cmd(0) 103 | self.write_cmd(self.pages - 1) 104 | self.write_data(self.buffer) 105 | 106 | 107 | class SSD1306_I2C(SSD1306): 108 | def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False): 109 | self.i2c = i2c 110 | self.addr = addr 111 | self.temp = bytearray(2) 112 | super().__init__(width, height, external_vcc) 113 | 114 | def write_cmd(self, cmd): 115 | self.temp[0] = 0x80 # Co=1, D/C#=0 116 | self.temp[1] = cmd 117 | self.i2c.writeto(self.addr, self.temp) 118 | 119 | def write_data(self, buf): 120 | self.temp[0] = self.addr << 1 121 | self.temp[1] = 0x40 # Co=0, D/C#=1 122 | self.i2c.start() 123 | self.i2c.write(self.temp) 124 | self.i2c.write(buf) 125 | self.i2c.stop() 126 | 127 | 128 | class SSD1306_SPI(SSD1306): 129 | def __init__(self, width, height, spi, dc, res, cs, external_vcc=False): 130 | self.rate = 10 * 1024 * 1024 131 | dc.init(dc.OUT, value=0) 132 | res.init(res.OUT, value=0) 133 | cs.init(cs.OUT, value=1) 134 | self.spi = spi 135 | self.dc = dc 136 | self.res = res 137 | self.cs = cs 138 | import time 139 | self.res(1) 140 | time.sleep_ms(1) 141 | self.res(0) 142 | time.sleep_ms(10) 143 | self.res(1) 144 | super().__init__(width, height, external_vcc) 145 | 146 | def write_cmd(self, cmd): 147 | self.spi.init(baudrate=self.rate, polarity=0, phase=0) 148 | self.cs(1) 149 | self.dc(0) 150 | self.cs(0) 151 | self.spi.write(bytearray([cmd])) 152 | self.cs(1) 153 | 154 | def write_data(self, buf): 155 | self.spi.init(baudrate=self.rate, polarity=0, phase=0) 156 | self.cs(1) 157 | self.dc(1) 158 | self.cs(0) 159 | self.spi.write(buf) 160 | self.cs(1) -------------------------------------------------------------------------------- /lib/urlencode.py: -------------------------------------------------------------------------------- 1 | class Urlencode(): 2 | 3 | always_safe = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' 4 | 'abcdefghijklmnopqrstuvwxyz' 5 | '0123456789' '_.-') 6 | 7 | def quote(self, s): 8 | res = [] 9 | replacements = {} 10 | for c in s: 11 | if c in self.always_safe: 12 | res.append(c) 13 | continue 14 | res.append('%%%x' % ord(c)) 15 | return ''.join(res) 16 | 17 | def quote_plus(self, s): 18 | if ' ' in s: 19 | s = s.replace(' ', '+') 20 | return self.quote(s) 21 | 22 | def encode(self, query): 23 | if isinstance(query, dict): 24 | query = query.items() 25 | l = [] 26 | for k, v in query: 27 | if not isinstance(v, list): 28 | v = [v] 29 | for value in v: 30 | k = self.quote_plus(str(k)) 31 | v = self.quote_plus(str(value)) 32 | # l.append(k + '=' + v) 33 | l.append(v) 34 | return '&'.join(l) 35 | -------------------------------------------------------------------------------- /lib/warnings.py: -------------------------------------------------------------------------------- 1 | def warn(msg, cat=None, stacklevel=1): 2 | print("%s: %s" % ("Warning" if cat is None else cat.__name__, msg)) 3 | --------------------------------------------------------------------------------