From 878b50fb65bcdab529ab8c584c19a2965c15ade4 Mon Sep 17 00:00:00 2001 From: Danny Staple Date: Fri, 4 Mar 2022 23:37:15 +0000 Subject: [PATCH] Chapter 9 --- ch-9/1-wifi-basics/connect_and_fetch.py | 32 ++++++++++ ch-9/1-wifi-basics/simpler.py | 27 +++++++++ ch-9/2-hello-world/hello.html | 4 ++ ch-9/2-hello-world/hello_from_file.py | 34 +++++++++++ ch-9/2-hello-world/hello_server.py | 32 ++++++++++ ch-9/2-hello-world/robot_wifi.py | 25 ++++++++ ch-9/3-sensors/pio_encoder.py | 77 +++++++++++++++++++++++++ ch-9/3-sensors/robot.py | 50 ++++++++++++++++ ch-9/3-sensors/robot_wifi.py | 25 ++++++++ ch-9/3-sensors/sensor.html | 17 ++++++ ch-9/3-sensors/sensor_remote.py | 61 ++++++++++++++++++++ ch-9/4-teleop/pio_encoder.py | 77 +++++++++++++++++++++++++ ch-9/4-teleop/robot.py | 50 ++++++++++++++++ ch-9/4-teleop/robot_wifi.py | 25 ++++++++ ch-9/4-teleop/teleop.html | 48 +++++++++++++++ ch-9/4-teleop/teleop.py | 61 ++++++++++++++++++++ 16 files changed, 645 insertions(+) create mode 100644 ch-9/1-wifi-basics/connect_and_fetch.py create mode 100644 ch-9/1-wifi-basics/simpler.py create mode 100644 ch-9/2-hello-world/hello.html create mode 100644 ch-9/2-hello-world/hello_from_file.py create mode 100644 ch-9/2-hello-world/hello_server.py create mode 100644 ch-9/2-hello-world/robot_wifi.py create mode 100644 ch-9/3-sensors/pio_encoder.py create mode 100755 ch-9/3-sensors/robot.py create mode 100644 ch-9/3-sensors/robot_wifi.py create mode 100644 ch-9/3-sensors/sensor.html create mode 100644 ch-9/3-sensors/sensor_remote.py create mode 100644 ch-9/4-teleop/pio_encoder.py create mode 100755 ch-9/4-teleop/robot.py create mode 100644 ch-9/4-teleop/robot_wifi.py create mode 100644 ch-9/4-teleop/teleop.html create mode 100644 ch-9/4-teleop/teleop.py diff --git a/ch-9/1-wifi-basics/connect_and_fetch.py b/ch-9/1-wifi-basics/connect_and_fetch.py new file mode 100644 index 0000000..4d78e4f --- /dev/null +++ b/ch-9/1-wifi-basics/connect_and_fetch.py @@ -0,0 +1,32 @@ +import board +import busio +from digitalio import DigitalInOut +from adafruit_esp32spi import adafruit_esp32spi +from adafruit_esp32spi import adafruit_esp32spi_wifimanager + +try: + from secrets import secrets +except ImportError: + print("WiFi secrets are kept in secrets.py, please add them there!") + raise + +esp32_cs = DigitalInOut(board.GP10) +esp32_ready = DigitalInOut(board.GP9) +esp32_reset = DigitalInOut(board.GP8) + +spi = busio.SPI(board.GP14, MOSI=board.GP11, MISO=board.GP12) +esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) + +status_led = DigitalInOut(board.LED) +status_led.switch_to_output() + +wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets) +status_led.value = 1 +print("Connected. Making connection") +try: + response = wifi.get("https://api.ipify.org") + print(response.text) +except: + status_led.value = 0 + wifi.reset() + raise diff --git a/ch-9/1-wifi-basics/simpler.py b/ch-9/1-wifi-basics/simpler.py new file mode 100644 index 0000000..a04c571 --- /dev/null +++ b/ch-9/1-wifi-basics/simpler.py @@ -0,0 +1,27 @@ +import board +import busio +from digitalio import DigitalInOut +from adafruit_esp32spi import adafruit_esp32spi +import time + + +print("ESP32 SPI hardware test") + +esp32_cs = DigitalInOut(board.GP10) +esp32_ready = DigitalInOut(board.GP9) +esp32_reset = DigitalInOut(board.GP8) + +spi = busio.SPI(board.GP14, board.GP11, board.GP12) +esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) + +if esp.status == adafruit_esp32spi.WL_IDLE_STATUS: + print("ESP32 found and in idle mode") +print("Firmware vers.", esp.firmware_version) +print("MAC addr:", [hex(i) for i in esp.MAC_address]) +# esp.start_scan_networks() + +# time.sleep(5) + +for ap in esp.scan_networks(): + print("\t%s\t\tRSSI: %d" % (str(ap['ssid'], 'utf-8'), ap['rssi'])) +print("Done!") diff --git a/ch-9/2-hello-world/hello.html b/ch-9/2-hello-world/hello.html new file mode 100644 index 0000000..f82bf30 --- /dev/null +++ b/ch-9/2-hello-world/hello.html @@ -0,0 +1,4 @@ + + +

Hello

+

World

diff --git a/ch-9/2-hello-world/hello_from_file.py b/ch-9/2-hello-world/hello_from_file.py new file mode 100644 index 0000000..0113ce3 --- /dev/null +++ b/ch-9/2-hello-world/hello_from_file.py @@ -0,0 +1,34 @@ +import robot_wifi + +from adafruit_esp32spi import adafruit_esp32spi_wsgiserver +from adafruit_wsgi.wsgi_app import WSGIApp + +app = WSGIApp() + +@app.route("/") +def index(request): + with open("hello.html") as fd: + hello_html = fd.read() + return 200, [('Content-Type',"text/html")], hello_html + +print("Setting up wifi.") +wifi, esp = robot_wifi.connect_to_wifi() +server = adafruit_esp32spi_wsgiserver.WSGIServer( + 80, + application=app +) +adafruit_esp32spi_wsgiserver.set_interface(esp) + +print("Starting server") + +server.start() +ip_int = ".".join(str(int(n)) for n in esp.ip_address) +print(f"IP Address is {ip_int}") +while True: + try: + server.update_poll() + # background task + except: + print("Shutting down wifi on failure. resetting ESP") + wifi.reset() + raise diff --git a/ch-9/2-hello-world/hello_server.py b/ch-9/2-hello-world/hello_server.py new file mode 100644 index 0000000..9130886 --- /dev/null +++ b/ch-9/2-hello-world/hello_server.py @@ -0,0 +1,32 @@ +import robot_wifi + +from adafruit_esp32spi import adafruit_esp32spi_wsgiserver +from adafruit_wsgi.wsgi_app import WSGIApp + +app = WSGIApp() + +@app.route("/") +def index(request): + return 200, [], "Hello world!" + +print("Setting up wifi.") +wifi, esp = robot_wifi.connect_to_wifi() +server = adafruit_esp32spi_wsgiserver.WSGIServer( + 80, + application=app +) +adafruit_esp32spi_wsgiserver.set_interface(esp) + +print("Starting server") + +server.start() +ip_int = ".".join(str(int(n)) for n in esp.ip_address) +print(f"IP Address is {ip_int}") +while True: + try: + server.update_poll() + # background task + except: + print("Shutting down wifi on failure. resetting ESP") + wifi.reset() + raise diff --git a/ch-9/2-hello-world/robot_wifi.py b/ch-9/2-hello-world/robot_wifi.py new file mode 100644 index 0000000..1176507 --- /dev/null +++ b/ch-9/2-hello-world/robot_wifi.py @@ -0,0 +1,25 @@ +import board +import busio +from digitalio import DigitalInOut +from adafruit_esp32spi import adafruit_esp32spi +from adafruit_esp32spi import adafruit_esp32spi_wifimanager + +try: + from secrets import secrets +except ImportError: + print("WiFi secrets are kept in secrets.py, please add them there!") + raise + + +def connect_to_wifi(): + esp32_cs = DigitalInOut(board.GP10) + esp32_ready = DigitalInOut(board.GP9) + esp32_reset = DigitalInOut(board.GP8) + + spi = busio.SPI(board.GP14, MOSI=board.GP11, MISO=board.GP12) + esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) + esp.reset() + wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets) + wifi.connect() + + return wifi, esp diff --git a/ch-9/3-sensors/pio_encoder.py b/ch-9/3-sensors/pio_encoder.py new file mode 100644 index 0000000..e9202f8 --- /dev/null +++ b/ch-9/3-sensors/pio_encoder.py @@ -0,0 +1,77 @@ +import rp2pio +import adafruit_pioasm +import array + +program = """ +; use the osr for count +; input pins c1 c2 + + set y, 0 ; clear y + mov osr, y ; and clear osr +read: + ; x will be the old value + ; y the new values + mov x, y ; store old Y in x + in null, 32 ; Clear ISR - using y + in pins, 2 ; read two pins into y + mov y, isr + jmp x!=y, different ; Jump if its different + jmp read ; otherwise loop back to read + +different: + ; x has old value, y has new. + ; extract the upper bit of X. + in x, 31 ; get bit 31 - old p1 (remember which direction it came in) + in null, 31 ; keep only 1 bit + mov x, isr ; put this back in x + jmp !x, c1_old_zero + +c1_old_not_zero: + jmp pin, count_up + jmp count_down + +c1_old_zero: + jmp pin, count_down + ; fall through +count_up: + ; for a clockwise move - we'll add 1 by inverting + mov x, ~ osr ; store inverted OSR on x + jmp x--, fake ; use jump to take off 1 +fake: + mov x, ~ x ; invert back + jmp send +count_down: + ; for a clockwise move, just take one off + mov x, osr ; store osr in x + jmp x--, send ; dec and send +send: + ; send x. + mov isr, x ; send it + push noblock ; put ISR into input FIFO + mov osr, x ; put X back in OSR + jmp read ; loop back +""" + +assembled = adafruit_pioasm.assemble(program) + +class QuadratureEncoder: + def __init__(self, first_pin, second_pin, reversed=False): + """Encoder with 2 pins. Must use sequential pins on the board""" + self.sm = rp2pio.StateMachine( + assembled, + frequency=0, + first_in_pin=first_pin, + jmp_pin=second_pin, + in_pin_count=2 + ) + self.reversed = reversed + self._buffer = array.array('i', [0]) + + def read(self): + while self.sm.in_waiting: + self.sm.readinto(self._buffer) + if self.reversed: + return -self._buffer[0] + else: + return self._buffer[0] + \ No newline at end of file diff --git a/ch-9/3-sensors/robot.py b/ch-9/3-sensors/robot.py new file mode 100755 index 0000000..e2fb14d --- /dev/null +++ b/ch-9/3-sensors/robot.py @@ -0,0 +1,50 @@ +import board +import pwmio +import pio_encoder +import busio +import adafruit_vl53l1x + + +motor_A1 = pwmio.PWMOut(board.GP17) +motor_A2 = pwmio.PWMOut(board.GP16) +motor_B1 = pwmio.PWMOut(board.GP18) +motor_B2 = pwmio.PWMOut(board.GP19) + +right_motor = motor_A1, motor_A2 +left_motor = motor_B1, motor_B2 + +right_encoder = pio_encoder.QuadratureEncoder(board.GP20, board.GP21, reversed=True) +left_encoder = pio_encoder.QuadratureEncoder(board.GP26, board.GP27) + +i2c0 = busio.I2C(sda=board.GP0, scl=board.GP1) +i2c1 = busio.I2C(sda=board.GP2, scl=board.GP3) + +left_distance = adafruit_vl53l1x.VL53L1X(i2c1) +right_distance = adafruit_vl53l1x.VL53L1X(i2c0) + + +def stop(): + motor_A1.duty_cycle = 0 + motor_A2.duty_cycle = 0 + motor_B1.duty_cycle = 0 + motor_B2.duty_cycle = 0 + +def set_speed(motor, speed): + # Swap motor pins if we reverse the speed + if speed < 0: + direction = motor[1], motor[0] + speed = -speed + else: + direction = motor + speed = min(speed, 1) # limit to 1.0 + max_speed = 2**16-1 + + + direction[0].duty_cycle = int(max_speed * speed) + direction[1].duty_cycle = 0 + +def set_left(speed): + set_speed(left_motor, speed) + +def set_right(speed): + set_speed(right_motor, speed) diff --git a/ch-9/3-sensors/robot_wifi.py b/ch-9/3-sensors/robot_wifi.py new file mode 100644 index 0000000..1176507 --- /dev/null +++ b/ch-9/3-sensors/robot_wifi.py @@ -0,0 +1,25 @@ +import board +import busio +from digitalio import DigitalInOut +from adafruit_esp32spi import adafruit_esp32spi +from adafruit_esp32spi import adafruit_esp32spi_wifimanager + +try: + from secrets import secrets +except ImportError: + print("WiFi secrets are kept in secrets.py, please add them there!") + raise + + +def connect_to_wifi(): + esp32_cs = DigitalInOut(board.GP10) + esp32_ready = DigitalInOut(board.GP9) + esp32_reset = DigitalInOut(board.GP8) + + spi = busio.SPI(board.GP14, MOSI=board.GP11, MISO=board.GP12) + esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) + esp.reset() + wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets) + wifi.connect() + + return wifi, esp diff --git a/ch-9/3-sensors/sensor.html b/ch-9/3-sensors/sensor.html new file mode 100644 index 0000000..37b165e --- /dev/null +++ b/ch-9/3-sensors/sensor.html @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/ch-9/3-sensors/sensor_remote.py b/ch-9/3-sensors/sensor_remote.py new file mode 100644 index 0000000..5435704 --- /dev/null +++ b/ch-9/3-sensors/sensor_remote.py @@ -0,0 +1,61 @@ +# .deploy/send-it.sh \ +# ch-9/3-sensors/sensor_remote.py \ +# ch-9/3-sensors/robot_wifi.py \ +# ch-9/3-sensors/robot.py \ +# ch-9/3-sensors/pio_encoder.py \ +# ch-9/3-sensors/sensor.html +import json + +from adafruit_esp32spi import adafruit_esp32spi_wsgiserver +from adafruit_wsgi.wsgi_app import WSGIApp + +import robot_wifi +import robot + +app = WSGIApp() + +@app.route("/") +def index(request): + with open("sensor.html") as fd: + hello_html = fd.read() + return 200, [('Content-Type',"text/html")], hello_html + +@app.route("/sensors") +def sensors(request): + sensor_data = { + "left_distance": robot.left_distance.distance, + } + robot.left_distance.clear_interrupt() + + return 200, [('Content-Type', 'application/json')], json.dumps({sensor_data}) + +print("Setting up wifi.") +wifi, esp = robot_wifi.connect_to_wifi() +server = adafruit_esp32spi_wsgiserver.WSGIServer( + 80, + application=app +) +adafruit_esp32spi_wsgiserver.set_interface(esp) + +print("Initialising sensors") +robot.left_distance.distance_mode = 1 +robot.left_distance.start_ranging() + + +print("Starting server") + +server.start() +ip_int = ".".join(str(int(n)) for n in esp.ip_address) +print(f"IP Address is {ip_int}") +while True: + try: + server.update_poll() + # background task + except: + print("Shutting down wifi on failure. resetting ESP") + wifi.reset() + raise + +# Reader exercise +# add the right distance to the distance sensor remote. Consider where to position the meter for this, how to return both sensors in +# the sensors call. Don't forget to clear the interrupt to get new readings. diff --git a/ch-9/4-teleop/pio_encoder.py b/ch-9/4-teleop/pio_encoder.py new file mode 100644 index 0000000..e9202f8 --- /dev/null +++ b/ch-9/4-teleop/pio_encoder.py @@ -0,0 +1,77 @@ +import rp2pio +import adafruit_pioasm +import array + +program = """ +; use the osr for count +; input pins c1 c2 + + set y, 0 ; clear y + mov osr, y ; and clear osr +read: + ; x will be the old value + ; y the new values + mov x, y ; store old Y in x + in null, 32 ; Clear ISR - using y + in pins, 2 ; read two pins into y + mov y, isr + jmp x!=y, different ; Jump if its different + jmp read ; otherwise loop back to read + +different: + ; x has old value, y has new. + ; extract the upper bit of X. + in x, 31 ; get bit 31 - old p1 (remember which direction it came in) + in null, 31 ; keep only 1 bit + mov x, isr ; put this back in x + jmp !x, c1_old_zero + +c1_old_not_zero: + jmp pin, count_up + jmp count_down + +c1_old_zero: + jmp pin, count_down + ; fall through +count_up: + ; for a clockwise move - we'll add 1 by inverting + mov x, ~ osr ; store inverted OSR on x + jmp x--, fake ; use jump to take off 1 +fake: + mov x, ~ x ; invert back + jmp send +count_down: + ; for a clockwise move, just take one off + mov x, osr ; store osr in x + jmp x--, send ; dec and send +send: + ; send x. + mov isr, x ; send it + push noblock ; put ISR into input FIFO + mov osr, x ; put X back in OSR + jmp read ; loop back +""" + +assembled = adafruit_pioasm.assemble(program) + +class QuadratureEncoder: + def __init__(self, first_pin, second_pin, reversed=False): + """Encoder with 2 pins. Must use sequential pins on the board""" + self.sm = rp2pio.StateMachine( + assembled, + frequency=0, + first_in_pin=first_pin, + jmp_pin=second_pin, + in_pin_count=2 + ) + self.reversed = reversed + self._buffer = array.array('i', [0]) + + def read(self): + while self.sm.in_waiting: + self.sm.readinto(self._buffer) + if self.reversed: + return -self._buffer[0] + else: + return self._buffer[0] + \ No newline at end of file diff --git a/ch-9/4-teleop/robot.py b/ch-9/4-teleop/robot.py new file mode 100755 index 0000000..e2fb14d --- /dev/null +++ b/ch-9/4-teleop/robot.py @@ -0,0 +1,50 @@ +import board +import pwmio +import pio_encoder +import busio +import adafruit_vl53l1x + + +motor_A1 = pwmio.PWMOut(board.GP17) +motor_A2 = pwmio.PWMOut(board.GP16) +motor_B1 = pwmio.PWMOut(board.GP18) +motor_B2 = pwmio.PWMOut(board.GP19) + +right_motor = motor_A1, motor_A2 +left_motor = motor_B1, motor_B2 + +right_encoder = pio_encoder.QuadratureEncoder(board.GP20, board.GP21, reversed=True) +left_encoder = pio_encoder.QuadratureEncoder(board.GP26, board.GP27) + +i2c0 = busio.I2C(sda=board.GP0, scl=board.GP1) +i2c1 = busio.I2C(sda=board.GP2, scl=board.GP3) + +left_distance = adafruit_vl53l1x.VL53L1X(i2c1) +right_distance = adafruit_vl53l1x.VL53L1X(i2c0) + + +def stop(): + motor_A1.duty_cycle = 0 + motor_A2.duty_cycle = 0 + motor_B1.duty_cycle = 0 + motor_B2.duty_cycle = 0 + +def set_speed(motor, speed): + # Swap motor pins if we reverse the speed + if speed < 0: + direction = motor[1], motor[0] + speed = -speed + else: + direction = motor + speed = min(speed, 1) # limit to 1.0 + max_speed = 2**16-1 + + + direction[0].duty_cycle = int(max_speed * speed) + direction[1].duty_cycle = 0 + +def set_left(speed): + set_speed(left_motor, speed) + +def set_right(speed): + set_speed(right_motor, speed) diff --git a/ch-9/4-teleop/robot_wifi.py b/ch-9/4-teleop/robot_wifi.py new file mode 100644 index 0000000..1176507 --- /dev/null +++ b/ch-9/4-teleop/robot_wifi.py @@ -0,0 +1,25 @@ +import board +import busio +from digitalio import DigitalInOut +from adafruit_esp32spi import adafruit_esp32spi +from adafruit_esp32spi import adafruit_esp32spi_wifimanager + +try: + from secrets import secrets +except ImportError: + print("WiFi secrets are kept in secrets.py, please add them there!") + raise + + +def connect_to_wifi(): + esp32_cs = DigitalInOut(board.GP10) + esp32_ready = DigitalInOut(board.GP9) + esp32_reset = DigitalInOut(board.GP8) + + spi = busio.SPI(board.GP14, MOSI=board.GP11, MISO=board.GP12) + esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) + esp.reset() + wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets) + wifi.connect() + + return wifi, esp diff --git a/ch-9/4-teleop/teleop.html b/ch-9/4-teleop/teleop.html new file mode 100644 index 0000000..a64b9bd --- /dev/null +++ b/ch-9/4-teleop/teleop.html @@ -0,0 +1,48 @@ + + + + + + + + \ No newline at end of file diff --git a/ch-9/4-teleop/teleop.py b/ch-9/4-teleop/teleop.py new file mode 100644 index 0000000..463dc89 --- /dev/null +++ b/ch-9/4-teleop/teleop.py @@ -0,0 +1,61 @@ +# .deploy/send-it.sh \ +# ch-9/4-teleop/sensor_remote.py \ +# ch-9/4-teleop/robot_wifi.py \ +# ch-9/4-teleop/robot.py \ +# ch-9/4-teleop/pio_encoder.py \ +# ch-9/4-teleop/sensor.html +import json +import time + +from adafruit_esp32spi import adafruit_esp32spi_wsgiserver +from adafruit_wsgi.wsgi_app import WSGIApp + +import robot_wifi +import robot + +app = WSGIApp() + +class State: + stop_at = 0 + +state = State() + +@app.route("/") +def index(request): + with open("teleop.html") as fd: + hello_html = fd.read() + return 200, [('Content-Type',"text/html")], hello_html + +@app.route("/move", methods=["POST"]) +def movement(request): + movement = json.loads(request.body) + robot.set_left(movement['y'] + movement['x']) + robot.set_right(movement['y'] - movement['x']) + state.stop_at = time.time() + 1 + +print("Setting up wifi.") +wifi, esp = robot_wifi.connect_to_wifi() +server = adafruit_esp32spi_wsgiserver.WSGIServer( + 80, + application=app +) +adafruit_esp32spi_wsgiserver.set_interface(esp) + +print("Starting server") + +server.start() +ip_int = ".".join(str(int(n)) for n in esp.ip_address) +print(f"IP Address is {ip_int}") +while True: + try: + server.update_poll() + # background task + if state.stop_at < time.time(): + robot.stop() + except: + print("Shutting down wifi on failure. resetting ESP") + wifi.reset() + raise + +# Reader exercise +# Can you combine the sensors and the control app? Can you think about how to control it using the sensors for feedback.