├── README.md ├── firmware └── esp32-cam-micropython.bin ├── src ├── googleDriveTest.py ├── uasyncio │ ├── __init__.py │ └── core.py └── urequests.py └── uploadImageToFolder.gs /README.md: -------------------------------------------------------------------------------- 1 | # micropython-esp32cam-to-googleDrive 2 | Source code and tutorial: How to upload a photo taken in esp32cam-micropython to google Drive Folder 3 | 4 | 5 | I've searched in many repos a solution to upload a image using the [ESP32-cam](https://esp32.com/) running [micropython](https://github.com/micropython/micropython). To implent this solution we need to install an esp32cam firmware, which is **NOT PRESENT** in [official micropython repository](http://micropython.org/download). I recommend the firmwares in [shariltumin esp32cam micropython repo](https://github.com/shariltumin/esp32-cam-micropython). Unfortunately, I could not build my own firmware so far. The working solution is based in [Guillermo Sampallo esp32cam tutorial](https://www.gsampallo.com/blog/2019/10/13/esp32-cam-subir-fotos-a-google-drive/) and [arduino code](https://github.com/gsampallo/esp32cam-gdrive). 6 | 7 | ## How to use? 8 | 9 | 1. Install ESP32-cam [firmware](firmware/esp32-cam-micropython.bin) from [@shariltumin esp32cam micropython repo](https://github.com/shariltumin/esp32-cam-micropython) (Use [esptool](https://github.com/espressif/esptool)); 10 | 1. Use [Adafruit Ampy](https://learn.adafruit.com/micropython-basics-load-files-and-run-code/install-ampy) to send `.py` files to ESP32; 11 | 1. Upload [`uploadImageToFolder.gs`](uploadImageToFolder.gs) to you Google Drive folder; 12 | 1. Change Folder ID and Wi-Fi configuration in [`uploadImageToFolder.gs`](uploadImageToFolder.gs) and [`googleDriveTest.py`](src/googleDriveTest.py) files; 13 | -------------------------------------------------------------------------------- /firmware/esp32-cam-micropython.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcielbp/micropython-esp32cam-to-googleDrive/859507b7354b10405a6afd86084e8bf050771652/firmware/esp32-cam-micropython.bin -------------------------------------------------------------------------------- /src/googleDriveTest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env micropython 2 | # -*- coding: utf-8 -*- 3 | 4 | import network 5 | from machine import Pin, Timer, I2C, ADC 6 | import camera 7 | import machine 8 | import time 9 | import uasyncio as asyncio 10 | import ubinascii 11 | import gc 12 | import usocket 13 | import json 14 | import urequests 15 | 16 | __author__ = "Marciel Barros Pereira" 17 | __credits__ = ["Marciel Barros Pereira"] 18 | __maintainer__ = "Marciel Barros Pereira" 19 | __email__ = "marcielbp@gmail.com" 20 | 21 | 22 | 23 | 24 | sta_if = network.WLAN(network.STA_IF) 25 | sta_if.active(True) 26 | try: 27 | sta_if.disconnect() 28 | except: 29 | pass 30 | localNetworks = sta_if.scan() 31 | 32 | knownSsid = [b'put',b'your',b'known',b'essid-here'] 33 | knownPasswd = [b'put',b'known-essid',b'passwords',b'here'] 34 | 35 | ssidmode = False 36 | for i in range (0,len(knownSsid)): 37 | for j in range (0,len(localNetworks)): 38 | print("Compare {} with {}".format(knownSsid[i],redes[j][0])) 39 | if (knownSsid[i]==redes[j][0]): 40 | print("Connecting to {}".format(knownSsid[i])) 41 | sta_if.connect(knownSsid[i],knownPasswd[i]) 42 | while sta_if.isconnected() == False: 43 | pass 44 | print('Connection successful') 45 | ssid = knownSsid[i] 46 | ssidmode = True 47 | print(sta_if.ifconfig()) 48 | break 49 | # check framesize modes in https://github.com/shariltumin/esp32-cam-micropython 50 | camera.init() 51 | camera.framesize(5) 52 | buf = camera.capture() 53 | camera.deinit() 54 | 55 | # there is no need to save right now, but, if you want to save file, use followigng code 56 | # newFile = open("ESP32-CAM.jpg", "wb") 57 | # newFile.write(buf) 58 | # newFile.close() 59 | 60 | # Create base64 string from image bytearray 61 | bufBase64 = ubinascii.b2a_base64(buf) 62 | 63 | # gather some free memory 64 | gc.collect() 65 | 66 | myScriptID = 'putYourScriptIdHere' 67 | url = 'https://script.google.com/macros/s/'+myScriptID+'/exec' 68 | myScript = '/macros/s/'+myScriptID+'/exec' 69 | 70 | myFilename = "filename=ESP32-CAM.jpg&mimetype=image/jpeg&data=" 71 | #myFilename = "imgBase64=" 72 | myDomain = "script.google.com" 73 | 74 | # below lines were adapted from urequests lib 75 | 76 | a = bufBase64.decode() 77 | data = myFilename + a 78 | method = "POST" 79 | try: 80 | proto, dummy, host, path = url.split("/", 3) 81 | except ValueError: 82 | proto, dummy, host = url.split("/", 2) 83 | path = "" 84 | if proto == "http:": 85 | port = 80 86 | elif proto == "https:": 87 | import ussl 88 | port = 443 89 | else: 90 | raise ValueError("Unsupported protocol: " + proto) 91 | 92 | if ":" in host: 93 | host, port = host.split(":", 1) 94 | port = int(port) 95 | 96 | ai = usocket.getaddrinfo(host, port, 0, usocket.SOCK_STREAM) 97 | ai = ai[0] 98 | 99 | s = usocket.socket(ai[0], ai[1], ai[2]) 100 | 101 | s.connect(ai[-1]) 102 | if proto == "https:": 103 | s = ussl.wrap_socket(s, server_hostname=host) 104 | s.write(b"%s /%s HTTP/1.0\r\n" % (method, path)) 105 | 106 | s.write(b"Host: %s\r\n" % host) 107 | # Iterate over keys to avoid tuple alloc 108 | s.write(b"Content-Length: %d\r\n" % len(data)) 109 | s.write(b"Content-Type: application/x-www-form-urlencoded\r\n") 110 | s.write(b"\r\n") 111 | 112 | s.write(data) 113 | 114 | l = s.readline() 115 | 116 | l = l.split(None, 2) 117 | status = int(l[1]) 118 | reason = "" 119 | if len(l) > 2: 120 | reason = l[2].rstrip() 121 | while True: 122 | l = s.readline() 123 | if not l or l == b"\r\n": 124 | break 125 | if l.startswith(b"Transfer-Encoding:"): 126 | if b"chunked" in l: 127 | raise ValueError("Unsupported " + l) 128 | elif l.startswith(b"Location:") and not 200 <= status <= 299: 129 | raise NotImplementedError("Redirects not yet supported") 130 | -------------------------------------------------------------------------------- /src/uasyncio/__init__.py: -------------------------------------------------------------------------------- 1 | import uerrno 2 | import uselect as select 3 | import usocket as _socket 4 | from uasyncio.core import * 5 | 6 | 7 | DEBUG = 0 8 | log = None 9 | 10 | def set_debug(val): 11 | global DEBUG, log 12 | DEBUG = val 13 | if val: 14 | import logging 15 | log = logging.getLogger("uasyncio") 16 | 17 | 18 | class PollEventLoop(EventLoop): 19 | 20 | def __init__(self, runq_len=16, waitq_len=16): 21 | EventLoop.__init__(self, runq_len, waitq_len) 22 | self.poller = select.poll() 23 | self.objmap = {} 24 | 25 | def add_reader(self, sock, cb, *args): 26 | if DEBUG and __debug__: 27 | log.debug("add_reader%s", (sock, cb, args)) 28 | if args: 29 | self.poller.register(sock, select.POLLIN) 30 | self.objmap[id(sock)] = (cb, args) 31 | else: 32 | self.poller.register(sock, select.POLLIN) 33 | self.objmap[id(sock)] = cb 34 | 35 | def remove_reader(self, sock): 36 | if DEBUG and __debug__: 37 | log.debug("remove_reader(%s)", sock) 38 | self.poller.unregister(sock) 39 | del self.objmap[id(sock)] 40 | 41 | def add_writer(self, sock, cb, *args): 42 | if DEBUG and __debug__: 43 | log.debug("add_writer%s", (sock, cb, args)) 44 | if args: 45 | self.poller.register(sock, select.POLLOUT) 46 | self.objmap[id(sock)] = (cb, args) 47 | else: 48 | self.poller.register(sock, select.POLLOUT) 49 | self.objmap[id(sock)] = cb 50 | 51 | def remove_writer(self, sock): 52 | if DEBUG and __debug__: 53 | log.debug("remove_writer(%s)", sock) 54 | try: 55 | self.poller.unregister(sock) 56 | self.objmap.pop(id(sock), None) 57 | except OSError as e: 58 | # StreamWriter.awrite() first tries to write to a socket, 59 | # and if that succeeds, yield IOWrite may never be called 60 | # for that socket, and it will never be added to poller. So, 61 | # ignore such error. 62 | if e.args[0] != uerrno.ENOENT: 63 | raise 64 | 65 | def wait(self, delay): 66 | if DEBUG and __debug__: 67 | log.debug("poll.wait(%d)", delay) 68 | # We need one-shot behavior (second arg of 1 to .poll()) 69 | res = self.poller.ipoll(delay, 1) 70 | #log.debug("poll result: %s", res) 71 | # Remove "if res" workaround after 72 | # https://github.com/micropython/micropython/issues/2716 fixed. 73 | if res: 74 | for sock, ev in res: 75 | cb = self.objmap[id(sock)] 76 | if ev & (select.POLLHUP | select.POLLERR): 77 | # These events are returned even if not requested, and 78 | # are sticky, i.e. will be returned again and again. 79 | # If the caller doesn't do proper error handling and 80 | # unregister this sock, we'll busy-loop on it, so we 81 | # as well can unregister it now "just in case". 82 | self.remove_reader(sock) 83 | if DEBUG and __debug__: 84 | log.debug("Calling IO callback: %r", cb) 85 | if isinstance(cb, tuple): 86 | cb[0](*cb[1]) 87 | else: 88 | cb.pend_throw(None) 89 | self.call_soon(cb) 90 | 91 | 92 | class StreamReader: 93 | 94 | def __init__(self, polls, ios=None): 95 | if ios is None: 96 | ios = polls 97 | self.polls = polls 98 | self.ios = ios 99 | 100 | def read(self, n=-1): 101 | while True: 102 | yield IORead(self.polls) 103 | res = self.ios.read(n) 104 | if res is not None: 105 | break 106 | # This should not happen for real sockets, but can easily 107 | # happen for stream wrappers (ssl, websockets, etc.) 108 | #log.warn("Empty read") 109 | if not res: 110 | yield IOReadDone(self.polls) 111 | return res 112 | 113 | def readexactly(self, n): 114 | buf = b"" 115 | while n: 116 | yield IORead(self.polls) 117 | res = self.ios.read(n) 118 | assert res is not None 119 | if not res: 120 | yield IOReadDone(self.polls) 121 | break 122 | buf += res 123 | n -= len(res) 124 | return buf 125 | 126 | def readline(self): 127 | if DEBUG and __debug__: 128 | log.debug("StreamReader.readline()") 129 | buf = b"" 130 | while True: 131 | yield IORead(self.polls) 132 | res = self.ios.readline() 133 | assert res is not None 134 | if not res: 135 | yield IOReadDone(self.polls) 136 | break 137 | buf += res 138 | if buf[-1] == 0x0a: 139 | break 140 | if DEBUG and __debug__: 141 | log.debug("StreamReader.readline(): %s", buf) 142 | return buf 143 | 144 | def aclose(self): 145 | yield IOReadDone(self.polls) 146 | self.ios.close() 147 | 148 | def __repr__(self): 149 | return "" % (self.polls, self.ios) 150 | 151 | 152 | class StreamWriter: 153 | 154 | def __init__(self, s, extra): 155 | self.s = s 156 | self.extra = extra 157 | 158 | def awrite(self, buf, off=0, sz=-1): 159 | # This method is called awrite (async write) to not proliferate 160 | # incompatibility with original asyncio. Unlike original asyncio 161 | # whose .write() method is both not a coroutine and guaranteed 162 | # to return immediately (which means it has to buffer all the 163 | # data), this method is a coroutine. 164 | if sz == -1: 165 | sz = len(buf) - off 166 | if DEBUG and __debug__: 167 | log.debug("StreamWriter.awrite(): spooling %d bytes", sz) 168 | while True: 169 | res = self.s.write(buf, off, sz) 170 | # If we spooled everything, return immediately 171 | if res == sz: 172 | if DEBUG and __debug__: 173 | log.debug("StreamWriter.awrite(): completed spooling %d bytes", res) 174 | return 175 | if res is None: 176 | res = 0 177 | if DEBUG and __debug__: 178 | log.debug("StreamWriter.awrite(): spooled partial %d bytes", res) 179 | assert res < sz 180 | off += res 181 | sz -= res 182 | yield IOWrite(self.s) 183 | #assert s2.fileno() == self.s.fileno() 184 | if DEBUG and __debug__: 185 | log.debug("StreamWriter.awrite(): can write more") 186 | 187 | # Write piecewise content from iterable (usually, a generator) 188 | def awriteiter(self, iterable): 189 | for buf in iterable: 190 | yield from self.awrite(buf) 191 | 192 | def aclose(self): 193 | yield IOWriteDone(self.s) 194 | self.s.close() 195 | 196 | def get_extra_info(self, name, default=None): 197 | return self.extra.get(name, default) 198 | 199 | def __repr__(self): 200 | return "" % self.s 201 | 202 | 203 | def open_connection(host, port, ssl=False): 204 | if DEBUG and __debug__: 205 | log.debug("open_connection(%s, %s)", host, port) 206 | ai = _socket.getaddrinfo(host, port, 0, _socket.SOCK_STREAM) 207 | ai = ai[0] 208 | s = _socket.socket(ai[0], ai[1], ai[2]) 209 | s.setblocking(False) 210 | try: 211 | s.connect(ai[-1]) 212 | except OSError as e: 213 | if e.args[0] != uerrno.EINPROGRESS: 214 | raise 215 | if DEBUG and __debug__: 216 | log.debug("open_connection: After connect") 217 | yield IOWrite(s) 218 | # if __debug__: 219 | # assert s2.fileno() == s.fileno() 220 | if DEBUG and __debug__: 221 | log.debug("open_connection: After iowait: %s", s) 222 | if ssl: 223 | print("Warning: uasyncio SSL support is alpha") 224 | import ussl 225 | s.setblocking(True) 226 | s2 = ussl.wrap_socket(s) 227 | s.setblocking(False) 228 | return StreamReader(s, s2), StreamWriter(s2, {}) 229 | return StreamReader(s), StreamWriter(s, {}) 230 | 231 | 232 | def start_server(client_coro, host, port, backlog=10): 233 | if DEBUG and __debug__: 234 | log.debug("start_server(%s, %s)", host, port) 235 | ai = _socket.getaddrinfo(host, port, 0, _socket.SOCK_STREAM) 236 | ai = ai[0] 237 | s = _socket.socket(ai[0], ai[1], ai[2]) 238 | s.setblocking(False) 239 | 240 | s.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR, 1) 241 | s.bind(ai[-1]) 242 | s.listen(backlog) 243 | while True: 244 | if DEBUG and __debug__: 245 | log.debug("start_server: Before accept") 246 | yield IORead(s) 247 | if DEBUG and __debug__: 248 | log.debug("start_server: After iowait") 249 | s2, client_addr = s.accept() 250 | s2.setblocking(False) 251 | if DEBUG and __debug__: 252 | log.debug("start_server: After accept: %s", s2) 253 | extra = {"peername": client_addr} 254 | yield client_coro(StreamReader(s2), StreamWriter(s2, extra)) 255 | 256 | 257 | import uasyncio.core 258 | uasyncio.core._event_loop_class = PollEventLoop 259 | -------------------------------------------------------------------------------- /src/uasyncio/core.py: -------------------------------------------------------------------------------- 1 | import utime as time 2 | import utimeq 3 | import ucollections 4 | 5 | 6 | type_gen = type((lambda: (yield))()) 7 | 8 | DEBUG = 0 9 | log = None 10 | 11 | def set_debug(val): 12 | global DEBUG, log 13 | DEBUG = val 14 | if val: 15 | import logging 16 | log = logging.getLogger("uasyncio.core") 17 | 18 | 19 | class CancelledError(Exception): 20 | pass 21 | 22 | 23 | class TimeoutError(CancelledError): 24 | pass 25 | 26 | 27 | class EventLoop: 28 | 29 | def __init__(self, runq_len=16, waitq_len=16): 30 | self.runq = ucollections.deque((), runq_len, True) 31 | self.waitq = utimeq.utimeq(waitq_len) 32 | # Current task being run. Task is a top-level coroutine scheduled 33 | # in the event loop (sub-coroutines executed transparently by 34 | # yield from/await, event loop "doesn't see" them). 35 | self.cur_task = None 36 | 37 | def time(self): 38 | return time.ticks_ms() 39 | 40 | def create_task(self, coro): 41 | # CPython 3.4.2 42 | self.call_later_ms(0, coro) 43 | # CPython asyncio incompatibility: we don't return Task object 44 | 45 | def call_soon(self, callback, *args): 46 | if __debug__ and DEBUG: 47 | log.debug("Scheduling in runq: %s", (callback, args)) 48 | self.runq.append(callback) 49 | if not isinstance(callback, type_gen): 50 | self.runq.append(args) 51 | 52 | def call_later(self, delay, callback, *args): 53 | self.call_at_(time.ticks_add(self.time(), int(delay * 1000)), callback, args) 54 | 55 | def call_later_ms(self, delay, callback, *args): 56 | if not delay: 57 | return self.call_soon(callback, *args) 58 | self.call_at_(time.ticks_add(self.time(), delay), callback, args) 59 | 60 | def call_at_(self, time, callback, args=()): 61 | if __debug__ and DEBUG: 62 | log.debug("Scheduling in waitq: %s", (time, callback, args)) 63 | self.waitq.push(time, callback, args) 64 | 65 | def wait(self, delay): 66 | # Default wait implementation, to be overriden in subclasses 67 | # with IO scheduling 68 | if __debug__ and DEBUG: 69 | log.debug("Sleeping for: %s", delay) 70 | time.sleep_ms(delay) 71 | 72 | def run_forever(self): 73 | cur_task = [0, 0, 0] 74 | while True: 75 | # Expire entries in waitq and move them to runq 76 | tnow = self.time() 77 | while self.waitq: 78 | t = self.waitq.peektime() 79 | delay = time.ticks_diff(t, tnow) 80 | if delay > 0: 81 | break 82 | self.waitq.pop(cur_task) 83 | if __debug__ and DEBUG: 84 | log.debug("Moving from waitq to runq: %s", cur_task[1]) 85 | self.call_soon(cur_task[1], *cur_task[2]) 86 | 87 | # Process runq 88 | l = len(self.runq) 89 | if __debug__ and DEBUG: 90 | log.debug("Entries in runq: %d", l) 91 | while l: 92 | cb = self.runq.popleft() 93 | l -= 1 94 | args = () 95 | if not isinstance(cb, type_gen): 96 | args = self.runq.popleft() 97 | l -= 1 98 | if __debug__ and DEBUG: 99 | log.info("Next callback to run: %s", (cb, args)) 100 | cb(*args) 101 | continue 102 | 103 | if __debug__ and DEBUG: 104 | log.info("Next coroutine to run: %s", (cb, args)) 105 | self.cur_task = cb 106 | delay = 0 107 | try: 108 | if args is (): 109 | ret = next(cb) 110 | else: 111 | ret = cb.send(*args) 112 | if __debug__ and DEBUG: 113 | log.info("Coroutine %s yield result: %s", cb, ret) 114 | if isinstance(ret, SysCall1): 115 | arg = ret.arg 116 | if isinstance(ret, SleepMs): 117 | delay = arg 118 | elif isinstance(ret, IORead): 119 | cb.pend_throw(False) 120 | self.add_reader(arg, cb) 121 | continue 122 | elif isinstance(ret, IOWrite): 123 | cb.pend_throw(False) 124 | self.add_writer(arg, cb) 125 | continue 126 | elif isinstance(ret, IOReadDone): 127 | self.remove_reader(arg) 128 | elif isinstance(ret, IOWriteDone): 129 | self.remove_writer(arg) 130 | elif isinstance(ret, StopLoop): 131 | return arg 132 | else: 133 | assert False, "Unknown syscall yielded: %r (of type %r)" % (ret, type(ret)) 134 | elif isinstance(ret, type_gen): 135 | self.call_soon(ret) 136 | elif isinstance(ret, int): 137 | # Delay 138 | delay = ret 139 | elif ret is None: 140 | # Just reschedule 141 | pass 142 | elif ret is False: 143 | # Don't reschedule 144 | continue 145 | else: 146 | assert False, "Unsupported coroutine yield value: %r (of type %r)" % (ret, type(ret)) 147 | except StopIteration as e: 148 | if __debug__ and DEBUG: 149 | log.debug("Coroutine finished: %s", cb) 150 | continue 151 | except CancelledError as e: 152 | if __debug__ and DEBUG: 153 | log.debug("Coroutine cancelled: %s", cb) 154 | continue 155 | # Currently all syscalls don't return anything, so we don't 156 | # need to feed anything to the next invocation of coroutine. 157 | # If that changes, need to pass that value below. 158 | if delay: 159 | self.call_later_ms(delay, cb) 160 | else: 161 | self.call_soon(cb) 162 | 163 | # Wait until next waitq task or I/O availability 164 | delay = 0 165 | if not self.runq: 166 | delay = -1 167 | if self.waitq: 168 | tnow = self.time() 169 | t = self.waitq.peektime() 170 | delay = time.ticks_diff(t, tnow) 171 | if delay < 0: 172 | delay = 0 173 | self.wait(delay) 174 | 175 | def run_until_complete(self, coro): 176 | def _run_and_stop(): 177 | yield from coro 178 | yield StopLoop(0) 179 | self.call_soon(_run_and_stop()) 180 | self.run_forever() 181 | 182 | def stop(self): 183 | self.call_soon((lambda: (yield StopLoop(0)))()) 184 | 185 | def close(self): 186 | pass 187 | 188 | 189 | class SysCall: 190 | 191 | def __init__(self, *args): 192 | self.args = args 193 | 194 | def handle(self): 195 | raise NotImplementedError 196 | 197 | # Optimized syscall with 1 arg 198 | class SysCall1(SysCall): 199 | 200 | def __init__(self, arg): 201 | self.arg = arg 202 | 203 | class StopLoop(SysCall1): 204 | pass 205 | 206 | class IORead(SysCall1): 207 | pass 208 | 209 | class IOWrite(SysCall1): 210 | pass 211 | 212 | class IOReadDone(SysCall1): 213 | pass 214 | 215 | class IOWriteDone(SysCall1): 216 | pass 217 | 218 | 219 | _event_loop = None 220 | _event_loop_class = EventLoop 221 | def get_event_loop(runq_len=16, waitq_len=16): 222 | global _event_loop 223 | if _event_loop is None: 224 | _event_loop = _event_loop_class(runq_len, waitq_len) 225 | return _event_loop 226 | 227 | def sleep(secs): 228 | yield int(secs * 1000) 229 | 230 | # Implementation of sleep_ms awaitable with zero heap memory usage 231 | class SleepMs(SysCall1): 232 | 233 | def __init__(self): 234 | self.v = None 235 | self.arg = None 236 | 237 | def __call__(self, arg): 238 | self.v = arg 239 | #print("__call__") 240 | return self 241 | 242 | def __iter__(self): 243 | #print("__iter__") 244 | return self 245 | 246 | def __next__(self): 247 | if self.v is not None: 248 | #print("__next__ syscall enter") 249 | self.arg = self.v 250 | self.v = None 251 | return self 252 | #print("__next__ syscall exit") 253 | _stop_iter.__traceback__ = None 254 | raise _stop_iter 255 | 256 | _stop_iter = StopIteration() 257 | sleep_ms = SleepMs() 258 | 259 | 260 | def cancel(coro): 261 | prev = coro.pend_throw(CancelledError()) 262 | if prev is False: 263 | _event_loop.call_soon(coro) 264 | 265 | 266 | class TimeoutObj: 267 | def __init__(self, coro): 268 | self.coro = coro 269 | 270 | 271 | def wait_for_ms(coro, timeout): 272 | 273 | def waiter(coro, timeout_obj): 274 | res = yield from coro 275 | if __debug__ and DEBUG: 276 | log.debug("waiter: cancelling %s", timeout_obj) 277 | timeout_obj.coro = None 278 | return res 279 | 280 | def timeout_func(timeout_obj): 281 | if timeout_obj.coro: 282 | if __debug__ and DEBUG: 283 | log.debug("timeout_func: cancelling %s", timeout_obj.coro) 284 | prev = timeout_obj.coro.pend_throw(TimeoutError()) 285 | #print("prev pend", prev) 286 | if prev is False: 287 | _event_loop.call_soon(timeout_obj.coro) 288 | 289 | timeout_obj = TimeoutObj(_event_loop.cur_task) 290 | _event_loop.call_later_ms(timeout, timeout_func, timeout_obj) 291 | return (yield from waiter(coro, timeout_obj)) 292 | 293 | 294 | def wait_for(coro, timeout): 295 | return wait_for_ms(coro, int(timeout * 1000)) 296 | 297 | 298 | def coroutine(f): 299 | return f 300 | 301 | # 302 | # The functions below are deprecated in uasyncio, and provided only 303 | # for compatibility with CPython asyncio 304 | # 305 | 306 | def ensure_future(coro, loop=_event_loop): 307 | _event_loop.call_soon(coro) 308 | # CPython asyncio incompatibility: we don't return Task object 309 | return coro 310 | 311 | 312 | # CPython asyncio incompatibility: Task is a function, not a class (for efficiency) 313 | def Task(coro, loop=_event_loop): 314 | # Same as async() 315 | _event_loop.call_soon(coro) 316 | -------------------------------------------------------------------------------- /src/urequests.py: -------------------------------------------------------------------------------- 1 | import usocket 2 | 3 | class Response: 4 | 5 | def __init__(self, f): 6 | self.raw = f 7 | self.encoding = "utf-8" 8 | self._cached = None 9 | 10 | def close(self): 11 | if self.raw: 12 | self.raw.close() 13 | self.raw = None 14 | self._cached = None 15 | 16 | @property 17 | def content(self): 18 | if self._cached is None: 19 | try: 20 | self._cached = self.raw.read() 21 | finally: 22 | self.raw.close() 23 | self.raw = None 24 | return self._cached 25 | 26 | @property 27 | def text(self): 28 | return str(self.content, self.encoding) 29 | 30 | def json(self): 31 | import ujson 32 | return ujson.loads(self.content) 33 | 34 | 35 | def request(method, url, data=None, json=None, headers={}, stream=None): 36 | try: 37 | proto, dummy, host, path = url.split("/", 3) 38 | except ValueError: 39 | proto, dummy, host = url.split("/", 2) 40 | path = "" 41 | if proto == "http:": 42 | port = 80 43 | elif proto == "https:": 44 | import ussl 45 | port = 443 46 | else: 47 | raise ValueError("Unsupported protocol: " + proto) 48 | 49 | if ":" in host: 50 | host, port = host.split(":", 1) 51 | port = int(port) 52 | 53 | ai = usocket.getaddrinfo(host, port, 0, usocket.SOCK_STREAM) 54 | ai = ai[0] 55 | 56 | s = usocket.socket(ai[0], ai[1], ai[2]) 57 | try: 58 | s.connect(ai[-1]) 59 | if proto == "https:": 60 | s = ussl.wrap_socket(s, server_hostname=host) 61 | s.write(b"%s /%s HTTP/1.0\r\n" % (method, path)) 62 | if not "Host" in headers: 63 | s.write(b"Host: %s\r\n" % host) 64 | # Iterate over keys to avoid tuple alloc 65 | for k in headers: 66 | s.write(k) 67 | s.write(b": ") 68 | s.write(headers[k]) 69 | s.write(b"\r\n") 70 | if json is not None: 71 | assert data is None 72 | import ujson 73 | data = ujson.dumps(json) 74 | s.write(b"Content-Type: application/json\r\n") 75 | if data: 76 | s.write(b"Content-Length: %d\r\n" % len(data)) 77 | s.write(b"\r\n") 78 | if data: 79 | s.write(data) 80 | 81 | l = s.readline() 82 | #print(l) 83 | l = l.split(None, 2) 84 | status = int(l[1]) 85 | reason = "" 86 | if len(l) > 2: 87 | reason = l[2].rstrip() 88 | while True: 89 | l = s.readline() 90 | if not l or l == b"\r\n": 91 | break 92 | #print(l) 93 | if l.startswith(b"Transfer-Encoding:"): 94 | if b"chunked" in l: 95 | raise ValueError("Unsupported " + l) 96 | elif l.startswith(b"Location:") and not 200 <= status <= 299: 97 | raise NotImplementedError("Redirects not yet supported") 98 | except OSError: 99 | s.close() 100 | raise 101 | 102 | resp = Response(s) 103 | resp.status_code = status 104 | resp.reason = reason 105 | return resp 106 | 107 | 108 | def head(url, **kw): 109 | return request("HEAD", url, **kw) 110 | 111 | def get(url, **kw): 112 | return request("GET", url, **kw) 113 | 114 | def post(url, **kw): 115 | return request("POST", url, **kw) 116 | 117 | def put(url, **kw): 118 | return request("PUT", url, **kw) 119 | 120 | def patch(url, **kw): 121 | return request("PATCH", url, **kw) 122 | 123 | def delete(url, **kw): 124 | return request("DELETE", url, **kw) 125 | -------------------------------------------------------------------------------- /uploadImageToFolder.gs: -------------------------------------------------------------------------------- 1 | function doPost(e) { 2 | dataDrive = e; 3 | testBlob(dataDrive); 4 | } 5 | 6 | function doGet(e) { 7 | dataDrive = e; 8 | testBlob(dataDrive); 9 | } 10 | 11 | function testBlob(dataIn) { 12 | var folderID = "putYourFolderIDHere" 13 | var strAux = ""; 14 | strAux = dataIn.parameter.data; 15 | var strInput = strAux.replace(/ /g, "+"); 16 | var decoded = Utilities.base64Decode(strInput, Utilities.Charset.UTF_8); 17 | var date = Utilities.formatDate(new Date(), "GMT-3", "yyyyMMdd_HHmmss"); 18 | var blob = Utilities.newBlob(decoded, "image/jpeg", 'img_'+date+'.jpg'); 19 | var folder = DriveApp.getFolderById(folderID); 20 | folder.createFile(blob); 21 | } 22 | --------------------------------------------------------------------------------