├── LICENSE.txt ├── README.md ├── Source_Ch02 ├── example1_blue_led.py ├── example2_blink_leds.py ├── example3_button.py ├── file_io.py └── log.txt ├── Source_Ch03 └── CC3K.py ├── Source_Ch04 ├── challenges │ └── conversions.py ├── conversions.py ├── fibonacci.py ├── my_data.json ├── pickup.py ├── pickup_truck.py ├── roman.py ├── roman_numerals.py ├── rw_json.py ├── sedan.py └── vehicle.py ├── Source_Ch05 ├── data.bin ├── exception_example.py ├── import_errors.py ├── my_helper │ ├── __init__.py │ ├── helper_functions.py │ └── sensor_convert.py ├── my_vehicles.json ├── sys_example.py ├── uio_example.py ├── ujson_example.py ├── uos_example.py ├── utime_example.py └── wipy_ntp.py ├── Source_Ch06 ├── pyboard_SPI.py ├── pyboard_accel.py ├── pyboard_button.py ├── pyboard_lcd.py ├── secret_log.txt ├── wipy_RGB.py ├── wipy_encryption.py └── wipy_heartbeat.py ├── Source_Ch08 ├── Pyboard │ ├── clock.py │ ├── main.py │ ├── ssd1306.py │ └── urtc.py ├── WiPy │ ├── clock.py │ ├── main.py │ ├── ssd1306.py │ └── urtc.py ├── clock_pyb_wipy.diff ├── oled_test.py ├── rtc_test.py ├── ssd1306_pyb.diff ├── ssd1306_wipy.diff └── urtc_pyboard.diff ├── Source_Ch09 ├── Pyboard │ ├── ped_part1_pyb.py │ └── ped_part2_pyb.py ├── WiPy │ ├── ped_part1_wipy.py │ └── ped_part2_wipy.py └── response.html ├── Source_Ch10 ├── Pyboard │ ├── plant_monitor.py │ └── plant_pyboard.py ├── WiPy │ ├── plant_monitor.py │ └── plant_wipy.py ├── part1.html ├── part2.html ├── plant_data.csv ├── sample.html └── threshold.py ├── Source_Ch11 ├── weather.csv ├── weather.py └── weather_node.py └── contributing.md /LICENSE.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/micropython-for-internet-of-things/c769013c4ea965375bda1964efdbede8489d4a1f/LICENSE.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apress Source Code 2 | 3 | This repository accompanies [*MicroPython for the Internet of Things*](http://www.apress.com/9781484231227) by Charles Bell (Apress, 2017). 4 | 5 | [comment]: #cover 6 | 7 | 8 | Download the files as a zip using the green button, or clone the repository to your machine using Git. 9 | 10 | ## Releases 11 | 12 | Release v1.0 corresponds to the code in the published book, without corrections or updates. 13 | 14 | ## Contributions 15 | 16 | See the file Contributing.md for more information on how you can contribute to this repository. 17 | -------------------------------------------------------------------------------- /Source_Ch02/example1_blue_led.py: -------------------------------------------------------------------------------- 1 | # 2 | # MicroPython for the IOT 3 | # 4 | # Example 1 5 | # 6 | # Turn on the blue LED (4 = Blue) 7 | # 8 | # Dr. Charles Bell 9 | # 10 | import pyb # Import the Pyboard library 11 | 12 | led = pyb.LED(4) # Get the LED instance 13 | led.off() # Make sure it's off first 14 | 15 | for i in range(1, 20): # Run the indented code 20 times 16 | led.on() # Turn LED on 17 | pyb.delay(250) # Wait for 250 milleseconds 18 | led.off() # Turn LED off 19 | pyb.delay(250) # Wait for 250 milleseconds 20 | 21 | led.off() # Turn the LED off at the end 22 | print("Done!") # Goodbye! 23 | -------------------------------------------------------------------------------- /Source_Ch02/example2_blink_leds.py: -------------------------------------------------------------------------------- 1 | # 2 | # MicroPython for the IOT 3 | # 4 | # Example 2 5 | # 6 | # Turn on the four LEDs on the board in order 7 | # 8 | # 1 = Red 9 | # 2 = Green 10 | # 3 = Orange 11 | # 4 = Blue 12 | # 13 | # Dr. Charles Bell 14 | # 15 | import pyb # Import the Pyboard library 16 | 17 | for j in range(1, 4): # Turn off all of the LEDs 18 | led = pyb.LED(j) # Get the LED 19 | led.off() # Turn the LED off 20 | 21 | i = 1 # LED counter 22 | while True: # Loop forever 23 | led = pyb.LED(i) # Get next LED 24 | led.on() # Turn LED on 25 | pyb.delay(500) # Wait for 1/2 second 26 | led.off() # Turn LED off 27 | pyb.delay(500) # Wait for 1/2 second 28 | i = i + 1 # Increment the LED counter 29 | if i > 4: # If > 4, start over at 1 30 | i = 1 31 | 32 | -------------------------------------------------------------------------------- /Source_Ch02/example3_button.py: -------------------------------------------------------------------------------- 1 | # 2 | # MicroPython for the IOT 3 | # 4 | # Example 3 5 | # 6 | # Flash the green LED when button is pushed 7 | # 8 | # Dr. Charles Bell 9 | # 10 | import pyb # Import the Pyboard library 11 | led = pyb.LED(2) # Get LED instance (2 = green) 12 | led.off() # Make sure the LED if off 13 | 14 | # Setup a callback function to handle button pressed 15 | # using an interrupt service routine 16 | def flash_led(): 17 | for i in range(1, 25): 18 | led.on() 19 | pyb.delay(100) 20 | led.off() 21 | pyb.delay(100) 22 | 23 | button = pyb.Switch() # Get the button (switch) 24 | button.callback(flash_led) # Register the callback (ISR) 25 | 26 | print("Ready for testing!") 27 | 28 | -------------------------------------------------------------------------------- /Source_Ch02/file_io.py: -------------------------------------------------------------------------------- 1 | # Example code to demonstrate writing and reading data to/from files 2 | 3 | # Step 1: Create a file and write some data 4 | new_file = open("log.txt", "w") # use "write" mode 5 | new_file.write("1,apples,2.5\n") # write some data 6 | new_file.write("2,oranges,1\n") # write some data 7 | new_file.write("3,peaches,3\n") # write some data 8 | new_file.write("4,grapes,21\n") # write some data 9 | new_file.close() # close the file 10 | 11 | # Step 2: Open a file and read data 12 | old_file = open("log.txt", "r") # use "read" mode 13 | # Use a loop to read all rows in the file 14 | for row in old_file.readlines(): 15 | columns = row.strip("\n").split(",") # split row by commas 16 | print(" : ".join(columns)) # print the row with colon separator 17 | old_file.close() 18 | -------------------------------------------------------------------------------- /Source_Ch02/log.txt: -------------------------------------------------------------------------------- 1 | 1,apples,2.5 2 | 2,oranges,1 3 | 3,peaches,3 4 | 4,grapes,21 5 | -------------------------------------------------------------------------------- /Source_Ch03/CC3K.py: -------------------------------------------------------------------------------- 1 | # connect/ show IP config a specific network interface 2 | import network 3 | import pyb 4 | 5 | def test_connect(): 6 | nic = network.CC3K(pyb.SPI(2), pyb.Pin.board.Y5, pyb.Pin.board.Y4, pyb.Pin.board.Y3) 7 | nic.connect('YOUR_ROUTER_HERE', 'YOUR_ROUTER_PASSWORD_HERE') 8 | while not nic.isconnected(): 9 | pyb.delay(50) 10 | print(nic.ifconfig()) 11 | 12 | # now use usocket as usual 13 | import usocket as socket 14 | addr = socket.getaddrinfo('micropython.org', 80)[0][-1] 15 | s = socket.socket() 16 | s.connect(addr) 17 | s.send(b'GET / HTTP/1.1\r\nHost: micropython.org\r\n\r\n') 18 | data = s.recv(1000) 19 | print(data) 20 | s.close() -------------------------------------------------------------------------------- /Source_Ch04/challenges/conversions.py: -------------------------------------------------------------------------------- 1 | # 2 | # MicroPython for the IOT 3 | # 4 | # Challenge Example: Convert integer to binary, hex, and octal 5 | # 6 | # Dr. Charles Bell 7 | # 8 | import argparse 9 | # Setup the argument parser 10 | parser = argparse.ArgumentParser() 11 | # We need two arguments: integer, and conversion 12 | parser.add_argument("original_val", help="Value to convert.") 13 | parser.add_argument("conversion", help="Conversion: hex, bin, or oct.") 14 | # Get the arguments 15 | args = parser.parse_args() 16 | # Convert string to integer 17 | value = int(args.original_val) 18 | if args.conversion == 'bin': 19 | print("{0} in binary is {1}".format(value, bin(value))) 20 | elif args.conversion == 'oct': 21 | print("{0} in octal is {1}".format(value, oct(value))) 22 | elif args.conversion == 'hex': 23 | print("{0} in hexadecimal is {1}".format(value, hex(value))) 24 | else: 25 | print("Sorry, I don't understand, {0}.".format(args.conversion)) 26 | -------------------------------------------------------------------------------- /Source_Ch04/conversions.py: -------------------------------------------------------------------------------- 1 | # 2 | # MicroPython for the IOT 3 | # 4 | # Example: Convert integer to binary, hex, and octal 5 | # 6 | # Dr. Charles Bell 7 | # 8 | 9 | # Create a tuple of integer values 10 | values = (12, 450, 1, 89, 2017, 90125) 11 | 12 | # Loop through the values and convert each to binary, hex, and octal 13 | for value in values: 14 | print("{0} in binary is {1}".format(value, bin(value))) 15 | print("{0} in octal is {1}".format(value, oct(value))) 16 | print("{0} in hexadecimal is {1}".format(value, hex(value))) 17 | 18 | -------------------------------------------------------------------------------- /Source_Ch04/fibonacci.py: -------------------------------------------------------------------------------- 1 | # 2 | # MicroPython for the IOT 3 | # 4 | # Example: Fibonacci series using recursion 5 | # 6 | # Calculate the Fibonacci series based on user input 7 | # 8 | # Dr. Charles Bell 9 | # 10 | 11 | # Create a function to calculate Fibonacci series (iterative) 12 | # Returns a list. 13 | def fibonacci_iterative(count): 14 | i = 1 15 | if count == 0: 16 | fib = [] 17 | elif count == 1: 18 | fib = [1] 19 | elif count == 2: 20 | fib = [1,1] 21 | elif count > 2: 22 | fib = [1,1] 23 | while i < (count - 1): 24 | fib.append(fib[i] + fib[i-1]) 25 | i += 1 26 | return fib 27 | 28 | # Create a function to calculate the nth Fibonacci number (recursive) 29 | # Returns an integer. 30 | def fibonacci_recursive(number): 31 | if number == 0: 32 | return 0 33 | elif number == 1: 34 | return 1 35 | else: 36 | # Call our self counting down. 37 | value = fibonacci_recursive(number-1) + fibonacci_recursive(number-2) 38 | return value 39 | 40 | # Main code 41 | print("Welcome to my Fibonacci calculator!") 42 | index = int(input("Please enter the number of integers in the series: ")) 43 | 44 | # Recursive example 45 | print("We calculate the value using a recursive algoritm.") 46 | nth_fibonacci = fibonacci_recursive(index) 47 | print("The {0}{1} fibonacci number is {2}." 48 | "".format(index, "th" if index > 1 else "st", nth_fibonacci)) 49 | see_series = str(input("Do you want to see all of the values in the series? ")) 50 | if see_series in ["Y","y"]: 51 | series = [] 52 | for i in range(1,index+1): 53 | series.append(fibonacci_recursive(i)) 54 | print("Series: {0}: ".format(series)) 55 | 56 | # Iterative example 57 | print("We calculate the value using an iterative algoritm.") 58 | series = fibonacci_iterative(index) 59 | print("The {0}{1} fibonacci number is {2}." 60 | "".format(index, "th" if index > 1 else "st", series[index-1])) 61 | see_series = str(input("Do you want to see all of the values in the series? ")) 62 | if see_series in ["Y","y"]: 63 | print("Series: {0}: ".format(series)) 64 | print("bye!") 65 | -------------------------------------------------------------------------------- /Source_Ch04/my_data.json: -------------------------------------------------------------------------------- 1 | {"age": 6, "breed": "dachshund", "type": "dog", "name": "Violet"} 2 | {"age": 15, "breed": "poodle", "type": "dog", "name": "JonJon"} 3 | {"age": 4, "breed": "siberian khatru", "type": "cat", "name": "Mister"} 4 | {"age": 7, "breed": "koi", "type": "fish", "name": "Spot"} 5 | {"age": 6, "breed": "dachshund", "type": "dog", "name": "Charlie"} 6 | -------------------------------------------------------------------------------- /Source_Ch04/pickup.py: -------------------------------------------------------------------------------- 1 | # 2 | # MicroPython for the IOT 3 | # 4 | # Class Example: Exeercising the PickupTruck class. 5 | # 6 | # Dr. Charles Bell 7 | # 8 | from pickup_truck import PickupTruck 9 | 10 | pickup = PickupTruck(500) 11 | pickup.add_occupant() 12 | pickup.add_occupant() 13 | pickup.add_occupant() 14 | pickup.add_occupant() 15 | pickup.add_payload(100) 16 | pickup.add_payload(300) 17 | print("Number of occupants in truck = {0}.".format(pickup.num_occupants())) 18 | print("Weight in truck = {0}.".format(pickup.get_payload())) 19 | pickup.add_payload(200) 20 | pickup.remove_payload(400) 21 | pickup.remove_payload(10) 22 | 23 | print("PickupTruck.__doc__:", PickupTruck.__doc__) 24 | print("PickupTruck.__name__:", PickupTruck.__name__) 25 | print("PickupTruck.__module__:", PickupTruck.__module__) 26 | print("PickupTruck.__bases__:", PickupTruck.__bases__) 27 | print("PickupTruck.__dict__:", PickupTruck.__dict__) 28 | 29 | -------------------------------------------------------------------------------- /Source_Ch04/pickup_truck.py: -------------------------------------------------------------------------------- 1 | # 2 | # MicroPython for the IOT 3 | # 4 | # Class Example: Inheriting the Vehicle class to form a 5 | # model of a pickup truck with maximum occupants and maximum 6 | # payload. 7 | # 8 | # Dr. Charles Bell 9 | # 10 | from vehicle import Vehicle 11 | 12 | class PickupTruck(Vehicle): 13 | """This is a pickup truck that has: 14 | axles = 2, 15 | doors = 2, 16 | __max occupants = 3 17 | The maximum payload is set on instantiation. 18 | """ 19 | occupants = 0 20 | payload = 0 21 | max_payload = 0 22 | __max_occupants = 3 23 | 24 | def __init__(self, max_weight): 25 | super().__init__(2,2) 26 | self.max_payload = max_weight 27 | 28 | def add_occupant(self): 29 | if (self.occupants < self.__max_occupants): 30 | super().add_occupant() 31 | else: 32 | print("Sorry, only 3 occupants are permitted in the truck.") 33 | 34 | def add_payload(self, num_pounds): 35 | if ((self.payload + num_pounds) < self.max_payload): 36 | self.payload += num_pounds 37 | else: 38 | print("Overloaded!") 39 | 40 | def remove_payload(self, num_pounds): 41 | if ((self.payload - num_pounds) >= 0): 42 | self.payload -= num_pounds 43 | else: 44 | print("Nothing in the truck.") 45 | 46 | def get_payload(self): 47 | return self.payload 48 | -------------------------------------------------------------------------------- /Source_Ch04/roman.py: -------------------------------------------------------------------------------- 1 | # 2 | # MicroPython for the IOT 3 | # 4 | # Example: Convert roman numerals using a class 5 | # 6 | # Convert integers to roman numerals 7 | # Convert roman numerals to integers 8 | # 9 | # Dr. Charles Bell 10 | # 11 | 12 | from roman_numerals import Roman_Numerals 13 | 14 | roman_str = input("Enter a valid roman numeral: ") 15 | roman_num = Roman_Numerals() 16 | 17 | # Convert to roman numberals 18 | value = roman_num.convert_to_int(roman_str) 19 | print("Convert to integer: {0} = {1}".format(roman_str, value)) 20 | 21 | # Convert to integer 22 | new_str = roman_num.convert_to_roman(value) 23 | print("Convert to Roman Numerals: {0} = {1}".format(value, new_str)) 24 | 25 | print("bye!") 26 | -------------------------------------------------------------------------------- /Source_Ch04/roman_numerals.py: -------------------------------------------------------------------------------- 1 | # 2 | # MicroPython for the IOT 3 | # 4 | # Example: Roman numerals class 5 | # 6 | # Convert integers to roman numerals 7 | # Convert roman numerals to integers 8 | # 9 | # Dr. Charles Bell 10 | # 11 | 12 | class Roman_Numerals: 13 | 14 | # Private dictionary of roman numerals 15 | __roman_dict = { 16 | 'I': 1, 17 | 'IV': 4, 18 | 'V': 5, 19 | 'IX': 9, 20 | 'X': 10, 21 | 'XL': 40, 22 | 'L': 50, 23 | 'XC': 90, 24 | 'C': 100, 25 | 'CD': 400, 26 | 'D': 500, 27 | 'CM': 900, 28 | 'M': 1000, 29 | } 30 | 31 | def convert_to_int(self, roman_num): 32 | value = 0 33 | for i in range(len(roman_num)): 34 | if i > 0 and self.__roman_dict[roman_num[i]] > self.__roman_dict[roman_num[i - 1]]: 35 | value += self.__roman_dict[roman_num[i]] - 2 * self.__roman_dict[roman_num[i - 1]] 36 | else: 37 | value += self.__roman_dict[roman_num[i]] 38 | return value 39 | 40 | def convert_to_roman(self, int_value): 41 | # First, get the values of all of entries in the dictionary 42 | roman_values = list(self.__roman_dict.values()) 43 | roman_keys = list(self.__roman_dict.keys()) 44 | # Prepare the string 45 | roman_str = "" 46 | remainder = int_value 47 | # Loop through the values in reverse 48 | for i in range(len(roman_values)-1, -1, -1): 49 | count = int(remainder / roman_values[i]) 50 | if count > 0: 51 | for j in range(0,count): 52 | roman_str += roman_keys[i] 53 | remainder -= count * roman_values[i] 54 | return roman_str 55 | 56 | -------------------------------------------------------------------------------- /Source_Ch04/rw_json.py: -------------------------------------------------------------------------------- 1 | # 2 | # MicroPython for the IOT 3 | # 4 | # Example: Storing and retrieving JSON objects in files 5 | # 6 | # Dr. Charles Bell 7 | # 8 | 9 | import json 10 | 11 | # Prepare a list of JSON documents for pets by converting JSON to a dictionary 12 | pets = [] 13 | parsed_json = json.loads('{"name":"Violet", "age": 6, "breed":"dachshund", "type":"dog"}') 14 | pets.append(parsed_json) 15 | parsed_json = json.loads('{"name": "JonJon", "age": 15, "breed":"poodle", "type":"dog"}') 16 | pets.append(parsed_json) 17 | parsed_json = json.loads('{"name": "Mister", "age": 4, "breed":"siberian khatru", "type":"cat"}') 18 | pets.append(parsed_json) 19 | parsed_json = json.loads('{"name": "Spot", "age": 7, "breed":"koi", "type":"fish"}') 20 | pets.append(parsed_json) 21 | parsed_json = json.loads('{"name": "Charlie", "age": 6, "breed":"dachshund", "type":"dog"}') 22 | pets.append(parsed_json) 23 | 24 | # Now, write these entries to a file. Note: overwrites the file 25 | json_file = open("my_data.json", "w") 26 | for pet in pets: 27 | json_file.write(json.dumps(pet)) 28 | json_file.write("\n") 29 | json_file.close() 30 | 31 | # Now, let's read the JSON documents then print the name and age for all of the dogs in the list 32 | my_pets = [] 33 | json_file = open("my_data.json", "r") 34 | for pet in json_file.readlines(): 35 | parsed_json = json.loads(pet) 36 | my_pets.append(parsed_json) 37 | json_file.close() 38 | 39 | print("Name, Age") 40 | for pet in my_pets: 41 | if pet['type'] == 'dog': 42 | print("{0}, {1}".format(pet['name'], pet['age'])) 43 | 44 | 45 | -------------------------------------------------------------------------------- /Source_Ch04/sedan.py: -------------------------------------------------------------------------------- 1 | # 2 | # MicroPython for the IOT 3 | # 4 | # Class Example: Using the generic Vehicle class 5 | # 6 | # Dr. Charles Bell 7 | # 8 | from vehicle import Vehicle 9 | 10 | sedan = Vehicle(2, 4) 11 | sedan.add_occupant() 12 | sedan.add_occupant() 13 | sedan.add_occupant() 14 | print("The car has {0} occupants.".format(sedan.num_occupants())) 15 | -------------------------------------------------------------------------------- /Source_Ch04/vehicle.py: -------------------------------------------------------------------------------- 1 | # 2 | # MicroPython for the IOT 3 | # 4 | # Class Example: A generic vehicle 5 | # 6 | # Dr. Charles Bell 7 | # 8 | class Vehicle: 9 | """Base class for defining vehicles""" 10 | axles = 0 11 | doors = 0 12 | occupants = 0 13 | 14 | def __init__(self, num_axles, num_doors): 15 | self.axles = num_axles 16 | self.doors = num_doors 17 | 18 | def get_axles(self): 19 | return self.axles 20 | 21 | def get_doors(self): 22 | return self.doors 23 | 24 | def add_occupant(self): 25 | self.occupants += 1 26 | 27 | def num_occupants(self): 28 | return self.occupants -------------------------------------------------------------------------------- /Source_Ch05/data.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/micropython-for-internet-of-things/c769013c4ea965375bda1964efdbede8489d4a1f/Source_Ch05/data.bin -------------------------------------------------------------------------------- /Source_Ch05/exception_example.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 5 2 | # Example use of exceptions 3 | values = [] 4 | print("Start sensor read.") 5 | try: 6 | values.append(read_sensor(pin11)) 7 | values.append(read_sensor(pin12)) 8 | values.append(read_sensor(pin13)) 9 | values.append(read_sensor(pin17)) 10 | values.append(read_sensor(pin18)) 11 | except ValueError as err: 12 | print("WARNING: One or more sensors valued to read a correct value.", err) 13 | else: 14 | print("ERROR: fatal error reading sensors.") 15 | finally: 16 | print("Sensor read complete.") 17 | -------------------------------------------------------------------------------- /Source_Ch05/import_errors.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 5 2 | # Try to import the keys() function from piano 3 | try: 4 | from piano import keys 5 | except ImportError as err: 6 | print("WARNING:", err) 7 | def keys(): 8 | return(['A','B','C','D','E','F','G']) 9 | 10 | print("Keys:", keys()) 11 | -------------------------------------------------------------------------------- /Source_Ch05/my_helper/__init__.py: -------------------------------------------------------------------------------- 1 | # Metadata 2 | __name__ = "Chuck's Python Helper Library" 3 | __all__ = ['format_time', 'get_rand', 'get_moisture_level'] 4 | # Library-level imports 5 | from my_helper.helper_functions import format_time, get_rand 6 | from my_helper.sensor_convert import get_moisture_level 7 | -------------------------------------------------------------------------------- /Source_Ch05/my_helper/helper_functions.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 5 2 | # Example module for the my_helper library 3 | # This module contains helper functions for general use. 4 | 5 | try: 6 | import pyb 7 | _PYBOARD = True 8 | except ImportError: 9 | import machine 10 | _PYBOARD = False 11 | 12 | # Get a random number from 0-1 from a 2^24 bit random value 13 | # if run on the WiPy, return 0-1 from a 2^30 bit random 14 | # value if the Pyboard is used. 15 | def get_rand(): 16 | if _PYBOARD: 17 | return pyb.rng() / (2 ** 30 - 1) 18 | return machine.rng() / (2 ** 24 - 1) 19 | 20 | # Format the time (epoch) for a better view 21 | def format_time(tm_data): 22 | # Use a special shortcut to unpack tuple: *tm_data 23 | return "{0}-{1:0>2}-{2:0>2} {3:0>2}:{4:0>2}:{5:0>2}".format(*tm_data) 24 | -------------------------------------------------------------------------------- /Source_Ch05/my_helper/sensor_convert.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 5 2 | # Example module for the my_helper library 3 | 4 | # This function converts values read from the sensor to a 5 | # string for use in qualifying the moisture level read. 6 | 7 | # Constants - adjust to "tune" your sensor 8 | 9 | _UPPER_BOUND = 400 10 | _LOWER_BOUND = 250 11 | 12 | def get_moisture_level(raw_value): 13 | if raw_value <= _LOWER_BOUND: 14 | return("dry") 15 | elif raw_value >= _UPPER_BOUND: 16 | return("wet") 17 | return("ok") 18 | 19 | 20 | -------------------------------------------------------------------------------- /Source_Ch05/my_vehicles.json: -------------------------------------------------------------------------------- 1 | {"color": "Pull me over red", "Make": "Chevrolet", "type": "pickup", "Model": "Silverado", "Year": 2015} 2 | -------------------------------------------------------------------------------- /Source_Ch05/sys_example.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 5 2 | # Example use of the sys library 3 | import sys 4 | print("Modules loaded: " , sys.modules) 5 | sys.path.append("/sd") 6 | print("Path: ", sys.path) 7 | sys.stdout.write("Platform: ") 8 | sys.stdout.write(sys.platform) 9 | sys.stdout.write("\n") 10 | sys.stdout.write("Version: ") 11 | sys.stdout.write(sys.version) 12 | sys.stdout.write("\n") 13 | sys.exit(1) 14 | -------------------------------------------------------------------------------- /Source_Ch05/uio_example.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 5 2 | # Example use of the uio library 3 | # Note: change uio to io to run this on your PC! 4 | import uio 5 | # Create the binary file 6 | fio_out = uio.FileIO('data.bin', 'wb') 7 | fio_out.write("\x5F\x9E\xAE\x09\x3E\x96\x68\x65\x6C\x6C\x6F") 8 | fio_out.write("\x00") 9 | fio_out.close() 10 | # Read the binary file and print out the results in hex and char. 11 | fio_in = uio.FileIO('data.bin', 'rb') 12 | print("Raw,Dec,Hex from file:") 13 | byte_val = fio_in.read(1) # read a byte 14 | while byte_val: 15 | print(byte_val, ",", ord(byte_val), hex(ord(byte_val))) 16 | byte_val = fio_in.read(1) # read a byte 17 | fio_in.close() 18 | 19 | -------------------------------------------------------------------------------- /Source_Ch05/ujson_example.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 5 2 | # Example use of the ujson library 3 | # Note: change ujson to json to run it on your PC! 4 | import ujson 5 | 6 | # Prepare a list of JSON documents for pets by converting JSON to a dictionary 7 | vehicles = [] 8 | vehicles.append(ujson.loads('{"make":"Chevrolet", "year":2015, "model":"Silverado", "color":"Pull me over red", "type":"pickup"}')) 9 | vehicles.append(ujson.loads('{"make":"Yamaha", "year":2009, "model":"R1", "color":"Blue/Silver", "type":"motorcycle"}')) 10 | vehicles.append(ujson.loads('{"make":"SeaDoo", "year":1997, "model":"Speedster", "color":"White", "type":"boat"}')) 11 | vehicles.append(ujson.loads('{"make":"TaoJen", "year":2013, "model":"Sicily", "color":"Black", "type":"Scooter"}')) 12 | 13 | # Now, write these entries to a file. Note: overwrites the file 14 | json_file = open("my_vehicles.json", "w") 15 | for vehicle in vehicles: 16 | json_file.write(ujson.dumps(vehicle)) 17 | json_file.write("\n") 18 | json_file.close() 19 | 20 | # Now, let's read the list of vehicles and print out their data 21 | my_vehicles = [] 22 | json_file = open("my_vehicles.json", "r") 23 | for vehicle in json_file.readlines(): 24 | parsed_json = ujson.loads(vehicle) 25 | my_vehicles.append(parsed_json) 26 | json_file.close() 27 | 28 | # Finally, print a summary of the vehicles 29 | print("Year Make Model Color") 30 | for vehicle in my_vehicles: 31 | print(vehicle['year'],vehicle['make'],vehicle['model'],vehicle['color']) 32 | 33 | -------------------------------------------------------------------------------- /Source_Ch05/uos_example.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 5 2 | # Example use of the uos library 3 | # Note: change uos to os to run it on your PC! 4 | import sys 5 | import uos 6 | 7 | # Create a method to display files in directory 8 | def show_files(): 9 | files = uos.listdir() 10 | sys.stdout.write("\nShow Files Output:\n") 11 | sys.stdout.write("\tname\tsize\n") 12 | for file in files: 13 | stats = uos.stat(file) 14 | # Print a directory with a "d" prefix and the size 15 | is_dir = True 16 | if stats[0] > 16384: 17 | is_dir = False 18 | if is_dir: 19 | sys.stdout.write("d\t") 20 | else: 21 | sys.stdout.write("\t") 22 | sys.stdout.write(file) 23 | if not is_dir: 24 | sys.stdout.write("\t") 25 | sys.stdout.write(str(stats[6])) 26 | sys.stdout.write("\n") 27 | 28 | # List the current directory 29 | show_files() 30 | # Change to the examples directory 31 | uos.chdir('examples') 32 | show_files() 33 | 34 | # Show how you can now use the import statement with the current dir 35 | print("\nRun the ujson_example with import ujson_example after chdir()") 36 | import ujson_example 37 | 38 | # Create a directory 39 | uos.mkdir("test") 40 | show_files() 41 | 42 | # Remove the directory 43 | uos.rmdir('test') 44 | show_files() 45 | -------------------------------------------------------------------------------- /Source_Ch05/utime_example.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 5 2 | # Example use of the utime library 3 | # Note: This example only works on the WiPy 4 | # Note: This example will not on your PC. 5 | import machine 6 | import sys 7 | import utime 8 | 9 | # Since we don't have a RTC, we have to set the current time 10 | # initialized the RTC in the WiPy machine library 11 | from machine import RTC 12 | 13 | # Init with default time and date 14 | rtc = RTC() 15 | # Init with a specific time and date. We use a specific 16 | # datetime, but we could get this from a network time 17 | # service. 18 | rtc = RTC(datetime=(2017, 7, 15, 21, 32, 11, 0, None)) 19 | 20 | # Get a random number from 0-1 from a 2^24 bit random value 21 | def get_rand(): 22 | return machine.rng() / (2 ** 24 - 1) 23 | 24 | # Format the time (epoch) for a better view 25 | def format_time(tm_data): 26 | # Use a special shortcut to unpack tuple: *tm_data 27 | return "{0}-{1:0>2}-{2:0>2} {3:0>2}:{4:0>2}:{5:0>2}".format(*tm_data) 28 | 29 | # Generate a random number of rows from 0-25 30 | num_rows = get_rand() * 25 31 | 32 | # Print a row using random seconds, milleseconds, or microseconds 33 | # to simulate time. 34 | print("Datetime Value") 35 | print("------------------- --------") 36 | for i in range(0,num_rows): 37 | # Generate random value for our sensor 38 | value = get_rand() * 100 39 | # Wait a random number of seconds for time 40 | utime.sleep(int(get_rand() * 15)) # sleep up to 15 seconds 41 | print("{0} {1:0>{width}.4f}".format(format_time(rtc.now()), value, width=8)) 42 | 43 | 44 | -------------------------------------------------------------------------------- /Source_Ch05/wipy_ntp.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 5 2 | # Example module for using the ntptime server to set the datetime 3 | # Note: only works on WiPy! 4 | 5 | from network import WLAN 6 | from machine import RTC 7 | import machine 8 | import sys 9 | import utime 10 | 11 | wlan = WLAN(mode=WLAN.STA) 12 | 13 | def connect(): 14 | wifi_networks = wlan.scan() 15 | for wifi in wifi_networks: 16 | if wifi.ssid == "YOUR_SSID": 17 | wlan.connect(wifi.ssid, auth=(wifi.sec, "YOUR_SSID_PASSWORD"), timeout=5000) 18 | while not wlan.isconnected(): 19 | machine.idle() # save power while waiting 20 | print("Connected!") 21 | return True 22 | if not wlan.isconnected(): 23 | print("ERROR: Cannot connect! Exiting...") 24 | return False 25 | 26 | if not connect(): 27 | sys.exit(-1) 28 | 29 | # Now, setup the RTC with the NTP service. 30 | rtc = RTC() 31 | print("Time before sync:", rtc.now()) 32 | rtc.ntp_sync("pool.ntp.org") 33 | while not rtc.synced(): 34 | utime.sleep(1) 35 | print("waiting for NTP server...") 36 | print("Time after sync:", rtc.now()) 37 | 38 | -------------------------------------------------------------------------------- /Source_Ch06/pyboard_SPI.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 6 2 | # Simple example for working with the SPI interface 3 | # using the Adafruit Thermocouple Amplifier. See 4 | # https://www.adafruit.com/product/269. 5 | # 6 | # Note: this only runs on the Pyboard 7 | # 8 | import machine 9 | import ubinascii 10 | 11 | # First, make sure this is running on a Pyboard 12 | try: 13 | import pyb 14 | from pyb import SPI 15 | except ImportError: 16 | print("ERROR: not on a Pyboard!") 17 | sys.exit(-1) 18 | 19 | # Create a method to normalize the data into degrees Celsius 20 | def normalize_data(data): 21 | temp = data[0] << 8 | data[1] 22 | if temp & 0x0001: 23 | return float('NaN') 24 | temp >>= 2 25 | if temp & 0x2000: 26 | temp -= 16384 27 | return (temp * 0.25) 28 | 29 | # Setup the SPI interfaceon the "Y" interface 30 | spi = SPI(2, SPI.MASTER, baudrate=5000000, polarity=0, phase=0) 31 | cs = machine.Pin("Y5", machine.Pin.OUT) 32 | cs.high() 33 | 34 | # read from the chip 35 | print("Reading temperature every second.") 36 | print("Press CTRL-C to stop.") 37 | while True: 38 | pyb.delay(1000) 39 | cs.low() 40 | print("Temperature is {0} Celsius.".format(normalize_data(spi.recv(4)))) 41 | cs.high() 42 | 43 | 44 | -------------------------------------------------------------------------------- /Source_Ch06/pyboard_accel.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 6 2 | # Example for working with the accelerometer on a Pyboard 3 | # Note: only works for the Pyboard! 4 | 5 | import pyb 6 | from pyb import Accel 7 | acc = Accel() 8 | print("Press CTRL-C to stop.") 9 | while True: 10 | pyb.delay(500) # Short delay before sampling 11 | print("X = {0:03} Y = {1:03} Z = {2:03}".format(acc.x(), acc.y(), acc.z())) 12 | -------------------------------------------------------------------------------- /Source_Ch06/pyboard_button.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 6 2 | # Simple example for working with interrupts using a class 3 | # to store state. 4 | # 5 | import sys 6 | 7 | # First, make sure this is running on a Pyboard 8 | try: 9 | import pyb 10 | except ImportError: 11 | print("ERROR: not on a Pyboard!") 12 | sys.exit(-1) 13 | 14 | # Initiate the switch class 15 | switch = pyb.Switch() 16 | 17 | class Cycle_LED(object): 18 | # Constructor 19 | def __init__(self, sw): 20 | self.cur_led = 0 21 | sw.callback(self.do_switch_press) 22 | 23 | # Switch callback function 24 | def do_switch_press(self):# 25 | # Turn off the last led unless this is the first time through the cycle 26 | if self.cur_led > 0: 27 | pyb.LED(self.cur_led).off() 28 | if self.cur_led < 4: 29 | self.cur_led = self.cur_led + 1 30 | else: 31 | self.cur_led = 1 32 | # Turn on the next led 33 | pyb.LED(self.cur_led).on() 34 | 35 | # Initiate the Cycle_LED class and setup the callback 36 | cycle = Cycle_LED(switch) 37 | 38 | # Now, simulate doing something else 39 | print("Testing the switch callback. Press CTRL-C to quit.") 40 | while True: 41 | sys.stdout.write(".") # Print something to show we're still in the loop 42 | pyb.delay(1000) # Wait a second... 43 | 44 | -------------------------------------------------------------------------------- /Source_Ch06/pyboard_lcd.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 6 2 | # Example module for the LCD skin on a Pyboard 3 | # 4 | # Note: The LCD is positioned in the "X" position. 5 | # 6 | import sys 7 | 8 | # First, make sure this is running on a Pyboard 9 | try: 10 | import pyb 11 | except ImportError: 12 | print("ERROR: not on a Pyboard!") 13 | sys.exit(-1) 14 | 15 | # Next, make sure the LCD skin library in the firmware 16 | try: 17 | import lcd160cr 18 | except ImportError: 19 | print("ERROR: LCD160CR library missing!") 20 | sys.exit(-1) 21 | 22 | # Return color of LED 23 | def led_color(num): 24 | if num == 1: return "red" 25 | elif num == 2: return "green" 26 | elif num == 3: return "orange" 27 | else: return "blue" 28 | 29 | # Use a method to turn off last LED and the next one on 30 | def turn_on_led(num, led_last): 31 | # turn last LED off 32 | led = pyb.LED(led_last) 33 | led.off() 34 | led = pyb.LED(num) 35 | led.on() 36 | sys.stdout.write("Turning off ") 37 | sys.stdout.write(led_color(led_last)) 38 | sys.stdout.write(" - Turning on ") 39 | sys.stdout.write(led_color(num)) 40 | sys.stdout.write("\n") 41 | 42 | # Setup the LCD in the "X" position 43 | lcd = lcd160cr.LCD160CR('X') 44 | 45 | for j in range(1, 4): # Turn off all of the LEDs 46 | led = pyb.LED(j) # Get the LED 47 | led.off() # Turn the LED off 48 | 49 | # Now, let's play a game. Let's change the LEDs to 50 | # different colors depending on where the user touches 51 | # the screen. 52 | print("Welcome to the touch screen demo!") 53 | print("Touch the screen in the corners to change the LED lit.") 54 | print("Touch the center to exit.") 55 | center = False 56 | last_led = 1 57 | while not center: 58 | pyb.delay(50) 59 | if lcd.is_touched: 60 | touch = lcd.get_touch() 61 | if (touch[0] == 0): 62 | continue 63 | # Upper-left corner 64 | if ((touch[1] <= 60) and (touch[2] <= 60)): 65 | turn_on_led(1, last_led) 66 | last_led = 1 67 | # Upper-right corner 68 | elif ((touch[1] >= 100) and (touch[2] <= 60)): 69 | turn_on_led(2, last_led) 70 | last_led = 2 71 | # Lower-right corner 72 | elif ((touch[1] >= 100) and (touch[2] >= 100)): 73 | turn_on_led(3, last_led) 74 | last_led = 3 75 | # Lower-left corner 76 | elif ((touch[1] <= 60) and (touch[2] >= 100)): 77 | turn_on_led(4, last_led) 78 | last_led = 4 79 | # Center 80 | elif ((touch[1] > 60) and (touch[1] < 100) and (touch[2] > 60) and (touch[2] < 100)): 81 | led = pyb.LED(last_led) 82 | led.off() 83 | center = True 84 | 85 | print("Thanks for playing!") 86 | sys.exit(0) 87 | -------------------------------------------------------------------------------- /Source_Ch06/secret_log.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/micropython-for-internet-of-things/c769013c4ea965375bda1964efdbede8489d4a1f/Source_Ch06/secret_log.txt -------------------------------------------------------------------------------- /Source_Ch06/wipy_RGB.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 6 2 | # Example of using the I2C interface via a driver 3 | # for the Adafruit RGB Sensor tcs34725 4 | # 5 | # Requires library: 6 | # https://github.com/adafruit/micropython-adafruit-tcs34725 7 | # 8 | from machine import I2C, Pin 9 | import sys 10 | import tcs34725 11 | import utime 12 | 13 | # Method to read sensor and display results 14 | def read_sensor(rgb_sense, led): 15 | sys.stdout.write("Place object in front of sensor now...") 16 | led.value(1) # Turn on the LED 17 | utime.sleep(5) # Wait 5 seconds 18 | data = rgb_sense.read(True) # Get the RGBC values 19 | print("color detected: {") 20 | print(" Red: {0:03}".format(data[0])) 21 | print(" Green: {0:03}".format(data[1])) 22 | print(" Blue: {0:03}".format(data[2])) 23 | print(" Clear: {0:03}".format(data[3])) 24 | print("}") 25 | led.value(0) 26 | 27 | # Setup the I2C - easy, yes? 28 | i2c = I2C(0, I2C.MASTER) # create and init as a master 29 | i2c.init(I2C.MASTER, baudrate=20000) # init as a master 30 | i2c.scan() 31 | 32 | # Setup the sensor 33 | sensor = tcs34725.TCS34725(i2c) 34 | 35 | # Setup the LED pin 36 | led_pin = Pin("P8", Pin.OUT) 37 | led_pin.value(0) 38 | 39 | print("Reading Colors every 10 seconds. When LED is on, place object in front of sensor.") 40 | print("Press CTRL-C to quit. (wait for it)") 41 | while True: 42 | utime.sleep(10) # Sleep for 10 seconds 43 | read_sensor(sensor, led_pin) # Read sensor and display values 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Source_Ch06/wipy_encryption.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 6 2 | # Simple example for working with encrypting data 3 | # 4 | import sys 5 | 6 | # First, make sure this is running on a WiPy (pycom) 7 | try: 8 | import pycom 9 | from crypto import AES 10 | except ImportError: 11 | print("ERROR: not on a WiPy (or Pycom) board!") 12 | sys.exit(-1) 13 | 14 | # Setup encryption using simple, basic encryption 15 | # NOTICE: you normally would protect this key! 16 | my_key = b'monkeybreadyummy' # 128 bit (16 bytes) key 17 | cipher = AES(my_key, AES.MODE_ECB) 18 | 19 | # Create the file and encrypt the data 20 | new_file = open("secret_log.txt", "w") # use "write" mode 21 | new_file.write(cipher.encrypt("1,apples,2.5 \n")) # write some data 22 | new_file.write(cipher.encrypt("2,oranges,1 \n")) # write some data 23 | new_file.write(cipher.encrypt("3,peaches,3 \n")) # write some data 24 | new_file.write(cipher.encrypt("4,grapes,21 \n")) # write some data 25 | new_file.close() # close the file 26 | 27 | # Step 2: Open the file and read data 28 | old_file = open("secret_log.txt", "r") # use "read" mode 29 | # Use a loop to read all rows in the file 30 | for row in old_file.readlines(): 31 | data = cipher.decrypt(row).decode('ascii') 32 | columns = data.strip("\n").split(",") # split row by commas 33 | print(" : ".join(columns)) # print the row with colon separator 34 | 35 | old_file.close() 36 | -------------------------------------------------------------------------------- /Source_Ch06/wipy_heartbeat.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 6 2 | # Example for working with the hearbeat LED as a 3 | # status/state indicator 4 | # 5 | import utime 6 | 7 | # First, make sure this is running on a Pyboard 8 | try: 9 | import pycom 10 | except ImportError: 11 | print("ERROR: not on a WiPy (or Pycom) board!") 12 | sys.exit(-1) 13 | 14 | # State/status enumeration 15 | _STATES = { 16 | 'ERROR' : 0xFF0000, # Bright red 17 | 'READING' : 0x00FF00, # Green 18 | 'WRITING' : 0x0000FF, # Blue 19 | 'OK' : 0xFF33FF, # Pinkish 20 | } 21 | 22 | # Clear the state (return to normal operation) 23 | def clear_status(): 24 | pycom.heartbeat(True) 25 | 26 | # Show state/status with the heatbeat LED 27 | def show_status(state): 28 | pycom.heartbeat(False) 29 | pycom.rgbled(state) 30 | 31 | # Now, demonstrate the state changes 32 | for state in _STATES.keys(): 33 | show_status(_STATES[state]) 34 | utime.sleep(3) 35 | 36 | # Return heartbeat to normal 37 | clear_status() 38 | -------------------------------------------------------------------------------- /Source_Ch08/Pyboard/clock.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 8 2 | # 3 | # Project 1: A MicroPython Clock! 4 | # 5 | # Required Components: 6 | # - Pyboard 7 | # - OLED SPI display 8 | # - RTC I2C module 9 | # 10 | # Note: this only runs on the Pyboard. See chapter text 11 | # for how to modify this to run on the WiPy. 12 | # 13 | 14 | # Imports for the project 15 | import pyb 16 | import urtc 17 | from machine import SPI, Pin as pin 18 | from pyb import I2C 19 | from ssd1306 import SSD1306_SPI as ssd 20 | 21 | # Setup SPI and I2C 22 | spi = SPI(2, baudrate=8000000, polarity=0, phase=0) 23 | i2c = I2C(1, I2C.MASTER) 24 | i2c.init(I2C.MASTER, baudrate=500000) 25 | 26 | # Setup the OLED : D/C, RST, CS 27 | oled_module = ssd(128,32,spi,pin("Y4"),pin("Y3"),pin("Y5")) 28 | 29 | # Setup the RTC 30 | rtc_module = urtc.DS1307(i2c) 31 | # 32 | # NOTE: We only need to set the datetime once. Uncomment these 33 | # lines only on the first run of a new RTC module or 34 | # whenever you change the battery. 35 | # (year, month, day, weekday, hour, minute, second, millisecond) 36 | #start_datetime = (2017,07,20,4,9,0,0,0) 37 | #rtc_module.datetime(start_datetime) 38 | 39 | # Return a string to print the day of the week 40 | def get_weekday(day): 41 | if day == 1: return "Sunday" 42 | elif day == 2: return "Monday" 43 | elif day == 3: return "Tuesday" 44 | elif day == 4: return "Wednesday" 45 | elif day == 5: return "Thursday" 46 | elif day == 6: return "Friday" 47 | else: return "Saturday" 48 | 49 | # Display the date and time 50 | def write_time(oled, rtc): 51 | # Get datetime 52 | dt = rtc.datetime() 53 | # Print the date 54 | oled.text("Date: {0:02}/{1:02}/{2:04}".format(dt[1], dt[2], dt[0]), 0, 0) 55 | # Print the time 56 | oled.text("Time: {0:02}:{1:02}:{2:02}".format(dt[4], dt[5], dt[6]), 0, 10) 57 | # Print the day of the week 58 | oled.text("Day: {0}".format(get_weekday(dt[3])), 0, 20) 59 | # Update the OLED 60 | oled.show() 61 | 62 | # Clear the screen 63 | def clear_screen(oled): 64 | oled.fill(0) 65 | oled.show() 66 | 67 | # Here, we make a "run" function to be used from the main.py 68 | # code module. This is preferable to direct "main" execution. 69 | def run(): 70 | # Display the deate and time every second 71 | while True: 72 | clear_screen(oled_module) 73 | write_time(oled_module, rtc_module) 74 | pyb.delay(1000) 75 | -------------------------------------------------------------------------------- /Source_Ch08/Pyboard/main.py: -------------------------------------------------------------------------------- 1 | # main.py -- put your code here! 2 | import clock 3 | clock.run() 4 | -------------------------------------------------------------------------------- /Source_Ch08/Pyboard/ssd1306.py: -------------------------------------------------------------------------------- 1 | # MicroPython SSD1306 OLED driver, I2C and SPI interfaces 2 | 3 | import time 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 | # Note the subclass must initialize self.framebuf to a framebuffer. 34 | # This is necessary because the underlying data buffer is different 35 | # between I2C and SPI implementations (I2C needs an extra byte). 36 | self.poweron() 37 | self.init_display() 38 | 39 | def init_display(self): 40 | for cmd in ( 41 | SET_DISP | 0x00, # off 42 | # address setting 43 | SET_MEM_ADDR, 0x00, # horizontal 44 | # resolution and layout 45 | SET_DISP_START_LINE | 0x00, 46 | SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0 47 | SET_MUX_RATIO, self.height - 1, 48 | SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0 49 | SET_DISP_OFFSET, 0x00, 50 | SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12, 51 | # timing and driving scheme 52 | SET_DISP_CLK_DIV, 0x80, 53 | SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1, 54 | SET_VCOM_DESEL, 0x30, # 0.83*Vcc 55 | # display 56 | SET_CONTRAST, 0xff, # maximum 57 | SET_ENTIRE_ON, # output follows RAM contents 58 | SET_NORM_INV, # not inverted 59 | # charge pump 60 | SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14, 61 | SET_DISP | 0x01): # on 62 | self.write_cmd(cmd) 63 | self.fill(0) 64 | self.show() 65 | 66 | def poweroff(self): 67 | self.write_cmd(SET_DISP | 0x00) 68 | 69 | def contrast(self, contrast): 70 | self.write_cmd(SET_CONTRAST) 71 | self.write_cmd(contrast) 72 | 73 | def invert(self, invert): 74 | self.write_cmd(SET_NORM_INV | (invert & 1)) 75 | 76 | def show(self): 77 | x0 = 0 78 | x1 = self.width - 1 79 | if self.width == 64: 80 | # displays with width of 64 pixels are shifted by 32 81 | x0 += 32 82 | x1 += 32 83 | self.write_cmd(SET_COL_ADDR) 84 | self.write_cmd(x0) 85 | self.write_cmd(x1) 86 | self.write_cmd(SET_PAGE_ADDR) 87 | self.write_cmd(0) 88 | self.write_cmd(self.pages - 1) 89 | self.write_framebuf() 90 | 91 | def fill(self, col): 92 | self.framebuf.fill(col) 93 | 94 | def pixel(self, x, y, col): 95 | self.framebuf.pixel(x, y, col) 96 | 97 | def scroll(self, dx, dy): 98 | self.framebuf.scroll(dx, dy) 99 | 100 | def text(self, string, x, y, col=1): 101 | self.framebuf.text(string, x, y, col) 102 | 103 | 104 | class SSD1306_I2C(SSD1306): 105 | def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False): 106 | self.i2c = i2c 107 | self.addr = addr 108 | self.temp = bytearray(2) 109 | # Add an extra byte to the data buffer to hold an I2C data/command byte 110 | # to use hardware-compatible I2C transactions. A memoryview of the 111 | # buffer is used to mask this byte from the framebuffer operations 112 | # (without a major memory hit as memoryview doesn't copy to a separate 113 | # buffer). 114 | self.buffer = bytearray(((height // 8) * width) + 1) 115 | self.buffer[0] = 0x40 # Set first byte of data buffer to Co=0, D/C=1 116 | self.framebuf = framebuf.FrameBuffer1(memoryview(self.buffer)[1:], width, height) 117 | super().__init__(width, height, external_vcc) 118 | 119 | def write_cmd(self, cmd): 120 | self.temp[0] = 0x80 # Co=1, D/C#=0 121 | self.temp[1] = cmd 122 | self.i2c.writeto(self.addr, self.temp) 123 | 124 | def write_framebuf(self): 125 | # Blast out the frame buffer using a single I2C transaction to support 126 | # hardware I2C interfaces. 127 | self.i2c.writeto(self.addr, self.buffer) 128 | 129 | def poweron(self): 130 | pass 131 | 132 | 133 | class SSD1306_SPI(SSD1306): 134 | def __init__(self, width, height, spi, dc, res, cs, external_vcc=False): 135 | self.rate = 10 * 1024 * 1024 136 | dc.init(dc.OUT, value=0) 137 | res.init(res.OUT, value=0) 138 | cs.init(cs.OUT, value=1) 139 | self.spi = spi 140 | self.dc = dc 141 | self.res = res 142 | self.cs = cs 143 | self.buffer = bytearray((height // 8) * width) 144 | self.framebuf = framebuf.FrameBuffer1(self.buffer, width, height) 145 | super().__init__(width, height, external_vcc) 146 | 147 | def write_cmd(self, cmd): 148 | self.spi.init(baudrate=self.rate, polarity=0, phase=0) 149 | self.cs.high() 150 | self.dc.low() 151 | self.cs.low() 152 | self.spi.write(bytearray([cmd])) 153 | self.cs.high() 154 | 155 | def write_framebuf(self): 156 | self.spi.init(baudrate=self.rate, polarity=0, phase=0) 157 | self.cs.high() 158 | self.dc.high() 159 | self.cs.low() 160 | self.spi.write(self.buffer) 161 | self.cs.high() 162 | 163 | def poweron(self): 164 | self.res.high() 165 | time.sleep_ms(1) 166 | self.res.low() 167 | time.sleep_ms(10) 168 | self.res.high() 169 | -------------------------------------------------------------------------------- /Source_Ch08/Pyboard/urtc.py: -------------------------------------------------------------------------------- 1 | import ucollections 2 | import utime 3 | 4 | 5 | DateTimeTuple = ucollections.namedtuple("DateTimeTuple", ["year", "month", 6 | "day", "weekday", "hour", "minute", "second", "millisecond"]) 7 | 8 | 9 | def datetime_tuple(year=None, month=None, day=None, weekday=None, hour=None, 10 | minute=None, second=None, millisecond=None): 11 | return DateTimeTuple(year, month, day, weekday, hour, minute, 12 | second, millisecond) 13 | 14 | 15 | def _bcd2bin(value): 16 | return (value or 0) - 6 * ((value or 0) >> 4) 17 | 18 | 19 | def _bin2bcd(value): 20 | return (value or 0) + 6 * ((value or 0) // 10) 21 | 22 | 23 | def tuple2seconds(datetime): 24 | return utime.mktime((datetime.year, datetime.month, datetime.day, 25 | datetime.hour, datetime.minute, datetime.second, datetime.weekday, 0)) 26 | 27 | 28 | def seconds2tuple(seconds): 29 | (year, month, day, hour, minute, 30 | second, weekday, _yday) = utime.localtime(seconds) 31 | return DateTimeTuple(year, month, day, weekday, hour, minute, second, 0) 32 | 33 | 34 | class _BaseRTC: 35 | _SWAP_DAY_WEEKDAY = False 36 | 37 | def __init__(self, i2c, address=0x68): 38 | self.i2c = i2c 39 | self.address = address 40 | 41 | def _register(self, register, buffer=None): 42 | if buffer is None: 43 | return self.i2c.mem_read(1, self.address, register)[0] 44 | self.i2c.mem_write(buffer, self.address, register) 45 | 46 | def _flag(self, register, mask, value=None): 47 | data = self._register(register) 48 | if value is None: 49 | return bool(data & mask) 50 | if value: 51 | data |= mask 52 | else: 53 | data &= ~mask 54 | self._register(register, bytearray((data,))) 55 | 56 | 57 | def datetime(self, datetime=None): 58 | if datetime is None: 59 | buffer = self.i2c.mem_read(7, self.address, 60 | self._DATETIME_REGISTER) 61 | if self._SWAP_DAY_WEEKDAY: 62 | day = buffer[3] 63 | weekday = buffer[4] 64 | else: 65 | day = buffer[4] 66 | weekday = buffer[3] 67 | return datetime_tuple( 68 | year=_bcd2bin(buffer[6]) + 2000, 69 | month=_bcd2bin(buffer[5]), 70 | day=_bcd2bin(day), 71 | weekday=_bcd2bin(weekday), 72 | hour=_bcd2bin(buffer[2]), 73 | minute=_bcd2bin(buffer[1]), 74 | second=_bcd2bin(buffer[0]), 75 | ) 76 | datetime = datetime_tuple(*datetime) 77 | buffer = bytearray(7) 78 | buffer[0] = _bin2bcd(datetime.second) 79 | buffer[1] = _bin2bcd(datetime.minute) 80 | buffer[2] = _bin2bcd(datetime.hour) 81 | if self._SWAP_DAY_WEEKDAY: 82 | buffer[4] = _bin2bcd(datetime.weekday) 83 | buffer[3] = _bin2bcd(datetime.day) 84 | else: 85 | buffer[3] = _bin2bcd(datetime.weekday) 86 | buffer[4] = _bin2bcd(datetime.day) 87 | buffer[5] = _bin2bcd(datetime.month) 88 | buffer[6] = _bin2bcd(datetime.year - 2000) 89 | self._register(self._DATETIME_REGISTER, buffer) 90 | 91 | 92 | class DS1307(_BaseRTC): 93 | _NVRAM_REGISTER = 0x08 94 | _DATETIME_REGISTER = 0x00 95 | _SQUARE_WAVE_REGISTER = 0x07 96 | 97 | def stop(self, value=None): 98 | return self._flag(0x00, 0b10000000, value) 99 | 100 | def memory(self, address, buffer=None): 101 | if buffer is not None and address + len(buffer) > 56: 102 | raise ValueError("address out of range") 103 | return self._register(self._NVRAM_REGISTER + address, buffer) 104 | 105 | 106 | class DS3231(_BaseRTC): 107 | _CONTROL_REGISTER = 0x0e 108 | _STATUS_REGISTER = 0x0f 109 | _DATETIME_REGISTER = 0x00 110 | _ALARM_REGISTERS = (0x08, 0x0b) 111 | _SQUARE_WAVE_REGISTER = 0x0e 112 | 113 | def lost_power(self): 114 | return self._flag(self._STATUS_REGISTER, 0b10000000) 115 | 116 | def alarm(self, value=None, alarm=0): 117 | return self._flag(self._STATUS_REGISTER, 118 | 0b00000011 & (1 << alarm), value) 119 | 120 | def stop(self, value=None): 121 | return self._flag(self._CONTROL_REGISTER, 0b10000000, value) 122 | 123 | def datetime(self, datetime=None): 124 | if datetime is not None: 125 | status = self._register(self._STATUS_REGISTER) & 0b01111111 126 | self._register(self._STATUS_REGISTER, bytearray((status,))) 127 | return super().datetime(datetime) 128 | 129 | def alarm_time(self, datetime=None, alarm=0): 130 | if datetime is None: 131 | buffer = self.i2c.mem_read(3, self.address, 132 | self._ALARM_REGISTERS[alarm]) 133 | day = None 134 | weekday = None 135 | second = None 136 | if buffer[2] & 0b10000000: 137 | pass 138 | elif buffer[2] & 0b01000000: 139 | day = _bcd2bin(buffer[2] & 0x3f) 140 | else: 141 | weekday = _bcd2bin(buffer[2] & 0x3f) 142 | minute = (_bcd2bin(buffer[0] & 0x7f) 143 | if not buffer[0] & 0x80 else None) 144 | hour = (_bcd2bin(buffer[1] & 0x7f) 145 | if not buffer[1] & 0x80 else None) 146 | if alarm == 0: 147 | # handle seconds 148 | buffer = self.i2c.mem_read(1, 149 | self.address, self._ALARM_REGISTERS[alarm] - 1) 150 | second = (_bcd2bin(buffer[0] & 0x7f) 151 | if not buffer[0] & 0x80 else None) 152 | return datetime_tuple( 153 | day=day, 154 | weekday=weekday, 155 | hour=hour, 156 | minute=minute, 157 | second=second, 158 | ) 159 | datetime = datetime_tuple(*datetime) 160 | buffer = bytearray(3) 161 | buffer[0] = (_bin2bcd(datetime.minute) 162 | if datetime.minute is not None else 0x80) 163 | buffer[1] = (_bin2bcd(datetime.hour) 164 | if datetime.hour is not None else 0x80) 165 | if datetime.day is not None: 166 | if datetime.weekday is not None: 167 | raise ValueError("can't specify both day and weekday") 168 | buffer[2] = _bin2bcd(datetime.day) | 0b01000000 169 | elif datetime.weekday is not None: 170 | buffer[2] = _bin2bcd(datetime.weekday) 171 | else: 172 | buffer[2] = 0x80 173 | self._register(self._ALARM_REGISTERS[alarm], buffer) 174 | if alarm == 0: 175 | # handle seconds 176 | buffer = bytearray([_bin2bcd(datetime.second) 177 | if datetime.second is not None else 0x80]) 178 | self._register(self._ALARM_REGISTERS[alarm] - 1, buffer) 179 | 180 | 181 | 182 | class PCF8523(_BaseRTC): 183 | _CONTROL1_REGISTER = 0x00 184 | _CONTROL2_REGISTER = 0x01 185 | _CONTROL3_REGISTER = 0x02 186 | _DATETIME_REGISTER = 0x03 187 | _ALARM_REGISTER = 0x0a 188 | _SQUARE_WAVE_REGISTER = 0x0f 189 | _SWAP_DAY_WEEKDAY = True 190 | 191 | def __init__(self, *args, **kwargs): 192 | super().__init__(*args, **kwargs) 193 | self.init() 194 | 195 | def init(self): 196 | # Enable battery switchover and low-battery detection. 197 | self._flag(self._CONTROL3_REGISTER, 0b11100000, False) 198 | 199 | def reset(self): 200 | self._flag(self._CONTROL1_REGISTER, 0x58, True) 201 | self.init() 202 | 203 | def lost_power(self, value=None): 204 | return self._flag(self._CONTROL3_REGISTER, 0b00010000, value) 205 | 206 | def stop(self, value=None): 207 | return self._flag(self._CONTROL1_REGISTER, 0b00010000, value) 208 | 209 | def battery_low(self): 210 | return self._flag(self._CONTROL3_REGISTER, 0b00000100) 211 | 212 | def alarm(self, value=None): 213 | return self._flag(self._CONTROL2_REGISTER, 0b00001000, value) 214 | 215 | def datetime(self, datetime=None): 216 | if datetime is not None: 217 | self.lost_power(False) # clear the battery switchover flag 218 | return super().datetime(datetime) 219 | 220 | def alarm_time(self, datetime=None): 221 | if datetime is None: 222 | buffer = self.i2c.mem_read(4, self.address, 223 | self._ALARM_REGISTER) 224 | return datetime_tuple( 225 | weekday=_bcd2bin(buffer[3] & 226 | 0x7f) if not buffer[3] & 0x80 else None, 227 | day=_bcd2bin(buffer[2] & 228 | 0x7f) if not buffer[2] & 0x80 else None, 229 | hour=_bcd2bin(buffer[1] & 230 | 0x7f) if not buffer[1] & 0x80 else None, 231 | minute=_bcd2bin(buffer[0] & 232 | 0x7f) if not buffer[0] & 0x80 else None, 233 | ) 234 | datetime = datetime_tuple(*datetime) 235 | buffer = bytearray(4) 236 | buffer[0] = (_bin2bcd(datetime.minute) 237 | if datetime.minute is not None else 0x80) 238 | buffer[1] = (_bin2bcd(datetime.hour) 239 | if datetime.hour is not None else 0x80) 240 | buffer[2] = (_bin2bcd(datetime.day) 241 | if datetime.day is not None else 0x80) 242 | buffer[3] = (_bin2bcd(datetime.weekday) | 0b01000000 243 | if datetime.weekday is not None else 0x80) 244 | self._register(self._ALARM_REGISTER, buffer) 245 | -------------------------------------------------------------------------------- /Source_Ch08/WiPy/clock.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 8 2 | # 3 | # Project 1: A MicroPython Clock! 4 | # 5 | # Required Components: 6 | # - Pyboard 7 | # - OLED SPI display 8 | # - RTC I2C module 9 | # 10 | # Note: this only runs on the WiPy. See chapter text 11 | # for how to modify this to run on the Pyboard. 12 | # 13 | 14 | # Imports for the project 15 | import urtc 16 | import utime 17 | from machine import SPI, I2C, RTC as rtc, Pin as pin 18 | from ssd1306 import SSD1306_SPI as ssd 19 | 20 | # Setup SPI and I2C 21 | spi = SPI(0, SPI.MASTER, baudrate=2000000, polarity=0, phase=0) 22 | i2c = I2C(0, I2C.MASTER, baudrate=100000,pins=("P9", "P8")) 23 | 24 | # Setup the OLED : D/C, RST, CS 25 | oled_module = ssd(128,32,spi,pin('P5'),pin('P6'),pin('P7')) 26 | 27 | # Setup the RTC 28 | rtc_module = urtc.DS1307(i2c) 29 | # 30 | # NOTE: We only need to set the datetime once. Uncomment these 31 | # lines only on the first run of a new RTC module or 32 | # whenever you change the battery. 33 | # (year, month, day, weekday, hour, minute, second, millisecond) 34 | #start_datetime = (2017,07,20,4,9,0,0,0) 35 | #rtc_module.datetime(start_datetime) 36 | 37 | # Return a string to print the day of the week 38 | def get_weekday(day): 39 | if day == 1: return "Sunday" 40 | elif day == 2: return "Monday" 41 | elif day == 3: return "Tuesday" 42 | elif day == 4: return "Wednesday" 43 | elif day == 5: return "Thursday" 44 | elif day == 6: return "Friday" 45 | else: return "Saturday" 46 | 47 | # Display the date and time 48 | def write_time(oled, rtc): 49 | # Get datetime 50 | dt = rtc.datetime() 51 | # Print the date 52 | oled.text("Date: {0:02}/{1:02}/{2:04}".format(dt[1], dt[2], dt[0]), 0, 0) 53 | # Print the time 54 | oled.text("Time: {0:02}:{1:02}:{2:02}".format(dt[4], dt[5], dt[6]), 0, 10) 55 | # Print the day of the week 56 | oled.text("Day: {0}".format(get_weekday(dt[3])), 0, 20) 57 | # Update the OLED 58 | oled.show() 59 | 60 | # Clear the screen 61 | def clear_screen(oled): 62 | oled.fill(0) 63 | oled.show() 64 | 65 | # Here, we make a "run" function to be used from the main.py 66 | # code module. This is preferable to direct "main" execution. 67 | def run(): 68 | # Display the deate and time every second 69 | while True: 70 | clear_screen(oled_module) 71 | write_time(oled_module, rtc_module) 72 | utime.sleep(1) 73 | -------------------------------------------------------------------------------- /Source_Ch08/WiPy/main.py: -------------------------------------------------------------------------------- 1 | # main.py -- put your code here! 2 | import clock 3 | clock.run() 4 | -------------------------------------------------------------------------------- /Source_Ch08/WiPy/ssd1306.py: -------------------------------------------------------------------------------- 1 | # MicroPython SSD1306 OLED driver, I2C and SPI interfaces 2 | 3 | import time 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 | # Note the subclass must initialize self.framebuf to a framebuffer. 34 | # This is necessary because the underlying data buffer is different 35 | # between I2C and SPI implementations (I2C needs an extra byte). 36 | self.poweron() 37 | self.init_display() 38 | 39 | def init_display(self): 40 | for cmd in ( 41 | SET_DISP | 0x00, # off 42 | # address setting 43 | SET_MEM_ADDR, 0x00, # horizontal 44 | # resolution and layout 45 | SET_DISP_START_LINE | 0x00, 46 | SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0 47 | SET_MUX_RATIO, self.height - 1, 48 | SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0 49 | SET_DISP_OFFSET, 0x00, 50 | SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12, 51 | # timing and driving scheme 52 | SET_DISP_CLK_DIV, 0x80, 53 | SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1, 54 | SET_VCOM_DESEL, 0x30, # 0.83*Vcc 55 | # display 56 | SET_CONTRAST, 0xff, # maximum 57 | SET_ENTIRE_ON, # output follows RAM contents 58 | SET_NORM_INV, # not inverted 59 | # charge pump 60 | SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14, 61 | SET_DISP | 0x01): # on 62 | self.write_cmd(cmd) 63 | self.fill(0) 64 | self.show() 65 | 66 | def poweroff(self): 67 | self.write_cmd(SET_DISP | 0x00) 68 | 69 | def contrast(self, contrast): 70 | self.write_cmd(SET_CONTRAST) 71 | self.write_cmd(contrast) 72 | 73 | def invert(self, invert): 74 | self.write_cmd(SET_NORM_INV | (invert & 1)) 75 | 76 | def show(self): 77 | x0 = 0 78 | x1 = self.width - 1 79 | if self.width == 64: 80 | # displays with width of 64 pixels are shifted by 32 81 | x0 += 32 82 | x1 += 32 83 | self.write_cmd(SET_COL_ADDR) 84 | self.write_cmd(x0) 85 | self.write_cmd(x1) 86 | self.write_cmd(SET_PAGE_ADDR) 87 | self.write_cmd(0) 88 | self.write_cmd(self.pages - 1) 89 | self.write_framebuf() 90 | 91 | def fill(self, col): 92 | self.framebuf.fill(col) 93 | 94 | def pixel(self, x, y, col): 95 | self.framebuf.pixel(x, y, col) 96 | 97 | def scroll(self, dx, dy): 98 | self.framebuf.scroll(dx, dy) 99 | 100 | def text(self, string, x, y, col=1): 101 | self.framebuf.text(string, x, y, col) 102 | 103 | 104 | class SSD1306_I2C(SSD1306): 105 | def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False): 106 | self.i2c = i2c 107 | self.addr = addr 108 | self.temp = bytearray(2) 109 | # Add an extra byte to the data buffer to hold an I2C data/command byte 110 | # to use hardware-compatible I2C transactions. A memoryview of the 111 | # buffer is used to mask this byte from the framebuffer operations 112 | # (without a major memory hit as memoryview doesn't copy to a separate 113 | # buffer). 114 | self.buffer = bytearray(((height // 8) * width) + 1) 115 | self.buffer[0] = 0x40 # Set first byte of data buffer to Co=0, D/C=1 116 | self.framebuf = framebuf.FrameBuffer1(memoryview(self.buffer)[1:], width, height) 117 | super().__init__(width, height, external_vcc) 118 | 119 | def write_cmd(self, cmd): 120 | self.temp[0] = 0x80 # Co=1, D/C#=0 121 | self.temp[1] = cmd 122 | self.i2c.writeto(self.addr, self.temp) 123 | 124 | def write_framebuf(self): 125 | # Blast out the frame buffer using a single I2C transaction to support 126 | # hardware I2C interfaces. 127 | self.i2c.writeto(self.addr, self.buffer) 128 | 129 | def poweron(self): 130 | pass 131 | 132 | 133 | class SSD1306_SPI(SSD1306): 134 | def __init__(self, width, height, spi, dc, res, cs, external_vcc=False): 135 | self.rate = 10 * 1024 * 1024 136 | dc.init(dc.OUT, value=0) 137 | res.init(res.OUT, value=0) 138 | cs.init(cs.OUT, value=1) 139 | self.spi = spi 140 | self.dc = dc 141 | self.res = res 142 | self.cs = cs 143 | self.buffer = bytearray((height // 8) * width) 144 | self.framebuf = framebuf.FrameBuffer1(self.buffer, width, height) 145 | super().__init__(width, height, external_vcc) 146 | 147 | def write_cmd(self, cmd): 148 | self.spi.init(baudrate=self.rate, polarity=0, phase=0) 149 | self.cs.value(1) 150 | self.dc.value(0) 151 | self.cs.value(0) 152 | self.spi.write(bytearray([cmd])) 153 | self.cs.value(1) 154 | 155 | def write_framebuf(self): 156 | self.spi.init(baudrate=self.rate, polarity=0, phase=0) 157 | self.cs.value(1) 158 | self.dc.value(1) 159 | self.cs.value(0) 160 | self.spi.write(self.buffer) 161 | self.cs.value(1) 162 | 163 | def poweron(self): 164 | self.res.value(1) 165 | time.sleep_ms(1) 166 | self.res.value(0) 167 | time.sleep_ms(10) 168 | self.res.value(1) 169 | -------------------------------------------------------------------------------- /Source_Ch08/WiPy/urtc.py: -------------------------------------------------------------------------------- 1 | import ucollections 2 | import utime 3 | 4 | 5 | DateTimeTuple = ucollections.namedtuple("DateTimeTuple", ["year", "month", 6 | "day", "weekday", "hour", "minute", "second", "millisecond"]) 7 | 8 | 9 | def datetime_tuple(year=None, month=None, day=None, weekday=None, hour=None, 10 | minute=None, second=None, millisecond=None): 11 | return DateTimeTuple(year, month, day, weekday, hour, minute, 12 | second, millisecond) 13 | 14 | 15 | def _bcd2bin(value): 16 | return (value or 0) - 6 * ((value or 0) >> 4) 17 | 18 | 19 | def _bin2bcd(value): 20 | return (value or 0) + 6 * ((value or 0) // 10) 21 | 22 | 23 | def tuple2seconds(datetime): 24 | return utime.mktime((datetime.year, datetime.month, datetime.day, 25 | datetime.hour, datetime.minute, datetime.second, datetime.weekday, 0)) 26 | 27 | 28 | def seconds2tuple(seconds): 29 | (year, month, day, hour, minute, 30 | second, weekday, _yday) = utime.localtime(seconds) 31 | return DateTimeTuple(year, month, day, weekday, hour, minute, second, 0) 32 | 33 | 34 | class _BaseRTC: 35 | _SWAP_DAY_WEEKDAY = False 36 | 37 | def __init__(self, i2c, address=0x68): 38 | self.i2c = i2c 39 | self.address = address 40 | 41 | def _register(self, register, buffer=None): 42 | if buffer is None: 43 | return self.i2c.readfrom_mem(self.address, register, 1)[0] 44 | self.i2c.writeto_mem(self.address, register, buffer) 45 | 46 | def _flag(self, register, mask, value=None): 47 | data = self._register(register) 48 | if value is None: 49 | return bool(data & mask) 50 | if value: 51 | data |= mask 52 | else: 53 | data &= ~mask 54 | self._register(register, bytearray((data,))) 55 | 56 | 57 | def datetime(self, datetime=None): 58 | if datetime is None: 59 | buffer = self.i2c.readfrom_mem(self.address, 60 | self._DATETIME_REGISTER, 7) 61 | if self._SWAP_DAY_WEEKDAY: 62 | day = buffer[3] 63 | weekday = buffer[4] 64 | else: 65 | day = buffer[4] 66 | weekday = buffer[3] 67 | return datetime_tuple( 68 | year=_bcd2bin(buffer[6]) + 2000, 69 | month=_bcd2bin(buffer[5]), 70 | day=_bcd2bin(day), 71 | weekday=_bcd2bin(weekday), 72 | hour=_bcd2bin(buffer[2]), 73 | minute=_bcd2bin(buffer[1]), 74 | second=_bcd2bin(buffer[0]), 75 | ) 76 | datetime = datetime_tuple(*datetime) 77 | buffer = bytearray(7) 78 | buffer[0] = _bin2bcd(datetime.second) 79 | buffer[1] = _bin2bcd(datetime.minute) 80 | buffer[2] = _bin2bcd(datetime.hour) 81 | if self._SWAP_DAY_WEEKDAY: 82 | buffer[4] = _bin2bcd(datetime.weekday) 83 | buffer[3] = _bin2bcd(datetime.day) 84 | else: 85 | buffer[3] = _bin2bcd(datetime.weekday) 86 | buffer[4] = _bin2bcd(datetime.day) 87 | buffer[5] = _bin2bcd(datetime.month) 88 | buffer[6] = _bin2bcd(datetime.year - 2000) 89 | self._register(self._DATETIME_REGISTER, buffer) 90 | 91 | 92 | class DS1307(_BaseRTC): 93 | _NVRAM_REGISTER = 0x08 94 | _DATETIME_REGISTER = 0x00 95 | _SQUARE_WAVE_REGISTER = 0x07 96 | 97 | def stop(self, value=None): 98 | return self._flag(0x00, 0b10000000, value) 99 | 100 | def memory(self, address, buffer=None): 101 | if buffer is not None and address + len(buffer) > 56: 102 | raise ValueError("address out of range") 103 | return self._register(self._NVRAM_REGISTER + address, buffer) 104 | 105 | 106 | class DS3231(_BaseRTC): 107 | _CONTROL_REGISTER = 0x0e 108 | _STATUS_REGISTER = 0x0f 109 | _DATETIME_REGISTER = 0x00 110 | _ALARM_REGISTERS = (0x08, 0x0b) 111 | _SQUARE_WAVE_REGISTER = 0x0e 112 | 113 | def lost_power(self): 114 | return self._flag(self._STATUS_REGISTER, 0b10000000) 115 | 116 | def alarm(self, value=None, alarm=0): 117 | return self._flag(self._STATUS_REGISTER, 118 | 0b00000011 & (1 << alarm), value) 119 | 120 | def stop(self, value=None): 121 | return self._flag(self._CONTROL_REGISTER, 0b10000000, value) 122 | 123 | def datetime(self, datetime=None): 124 | if datetime is not None: 125 | status = self._register(self._STATUS_REGISTER) & 0b01111111 126 | self._register(self._STATUS_REGISTER, bytearray((status,))) 127 | return super().datetime(datetime) 128 | 129 | def alarm_time(self, datetime=None, alarm=0): 130 | if datetime is None: 131 | buffer = self.i2c.readfrom_mem(self.address, 132 | self._ALARM_REGISTERS[alarm], 3) 133 | day = None 134 | weekday = None 135 | second = None 136 | if buffer[2] & 0b10000000: 137 | pass 138 | elif buffer[2] & 0b01000000: 139 | day = _bcd2bin(buffer[2] & 0x3f) 140 | else: 141 | weekday = _bcd2bin(buffer[2] & 0x3f) 142 | minute = (_bcd2bin(buffer[0] & 0x7f) 143 | if not buffer[0] & 0x80 else None) 144 | hour = (_bcd2bin(buffer[1] & 0x7f) 145 | if not buffer[1] & 0x80 else None) 146 | if alarm == 0: 147 | # handle seconds 148 | buffer = self.i2c.readfrom_mem( 149 | self.address, self._ALARM_REGISTERS[alarm] - 1, 1) 150 | second = (_bcd2bin(buffer[0] & 0x7f) 151 | if not buffer[0] & 0x80 else None) 152 | return datetime_tuple( 153 | day=day, 154 | weekday=weekday, 155 | hour=hour, 156 | minute=minute, 157 | second=second, 158 | ) 159 | datetime = datetime_tuple(*datetime) 160 | buffer = bytearray(3) 161 | buffer[0] = (_bin2bcd(datetime.minute) 162 | if datetime.minute is not None else 0x80) 163 | buffer[1] = (_bin2bcd(datetime.hour) 164 | if datetime.hour is not None else 0x80) 165 | if datetime.day is not None: 166 | if datetime.weekday is not None: 167 | raise ValueError("can't specify both day and weekday") 168 | buffer[2] = _bin2bcd(datetime.day) | 0b01000000 169 | elif datetime.weekday is not None: 170 | buffer[2] = _bin2bcd(datetime.weekday) 171 | else: 172 | buffer[2] = 0x80 173 | self._register(self._ALARM_REGISTERS[alarm], buffer) 174 | if alarm == 0: 175 | # handle seconds 176 | buffer = bytearray([_bin2bcd(datetime.second) 177 | if datetime.second is not None else 0x80]) 178 | self._register(self._ALARM_REGISTERS[alarm] - 1, buffer) 179 | 180 | 181 | 182 | class PCF8523(_BaseRTC): 183 | _CONTROL1_REGISTER = 0x00 184 | _CONTROL2_REGISTER = 0x01 185 | _CONTROL3_REGISTER = 0x02 186 | _DATETIME_REGISTER = 0x03 187 | _ALARM_REGISTER = 0x0a 188 | _SQUARE_WAVE_REGISTER = 0x0f 189 | _SWAP_DAY_WEEKDAY = True 190 | 191 | def __init__(self, *args, **kwargs): 192 | super().__init__(*args, **kwargs) 193 | self.init() 194 | 195 | def init(self): 196 | # Enable battery switchover and low-battery detection. 197 | self._flag(self._CONTROL3_REGISTER, 0b11100000, False) 198 | 199 | def reset(self): 200 | self._flag(self._CONTROL1_REGISTER, 0x58, True) 201 | self.init() 202 | 203 | def lost_power(self, value=None): 204 | return self._flag(self._CONTROL3_REGISTER, 0b00010000, value) 205 | 206 | def stop(self, value=None): 207 | return self._flag(self._CONTROL1_REGISTER, 0b00010000, value) 208 | 209 | def battery_low(self): 210 | return self._flag(self._CONTROL3_REGISTER, 0b00000100) 211 | 212 | def alarm(self, value=None): 213 | return self._flag(self._CONTROL2_REGISTER, 0b00001000, value) 214 | 215 | def datetime(self, datetime=None): 216 | if datetime is not None: 217 | self.lost_power(False) # clear the battery switchover flag 218 | return super().datetime(datetime) 219 | 220 | def alarm_time(self, datetime=None): 221 | if datetime is None: 222 | buffer = self.i2c.readfrom_mem(self.address, 223 | self._ALARM_REGISTER, 4) 224 | return datetime_tuple( 225 | weekday=_bcd2bin(buffer[3] & 226 | 0x7f) if not buffer[3] & 0x80 else None, 227 | day=_bcd2bin(buffer[2] & 228 | 0x7f) if not buffer[2] & 0x80 else None, 229 | hour=_bcd2bin(buffer[1] & 230 | 0x7f) if not buffer[1] & 0x80 else None, 231 | minute=_bcd2bin(buffer[0] & 232 | 0x7f) if not buffer[0] & 0x80 else None, 233 | ) 234 | datetime = datetime_tuple(*datetime) 235 | buffer = bytearray(4) 236 | buffer[0] = (_bin2bcd(datetime.minute) 237 | if datetime.minute is not None else 0x80) 238 | buffer[1] = (_bin2bcd(datetime.hour) 239 | if datetime.hour is not None else 0x80) 240 | buffer[2] = (_bin2bcd(datetime.day) 241 | if datetime.day is not None else 0x80) 242 | buffer[3] = (_bin2bcd(datetime.weekday) | 0b01000000 243 | if datetime.weekday is not None else 0x80) 244 | self._register(self._ALARM_REGISTER, buffer) 245 | -------------------------------------------------------------------------------- /Source_Ch08/clock_pyb_wipy.diff: -------------------------------------------------------------------------------- 1 | --- ./clock.py 2017-07-22 11:41:37.000000000 -0400 2 | +++ ./WiPy/clock.py 2017-07-22 11:41:10.000000000 -0400 3 | @@ -7,24 +7,22 @@ 4 | # - OLED SPI display 5 | # - RTC I2C module 6 | # 7 | -# Note: this only runs on the Pyboard. See chapter text 8 | -# for how to modify this to run on the WiPy 9 | +# Note: this only runs on the WiPy. See chapter text 10 | +# for how to modify this to run on the Pyboard 11 | # 12 | 13 | # Imports for the project 14 | -import pyb 15 | import urtc 16 | -from machine import SPI, Pin as pin 17 | -from pyb import I2C 18 | +import utime 19 | +from machine import SPI, I2C, RTC as rtc, Pin as pin 20 | from ssd1306 import SSD1306_SPI as ssd 21 | 22 | # Setup SPI and I2C 23 | -spi = SPI(2, baudrate=8000000, polarity=0, phase=0) 24 | -i2c = I2C(1, I2C.MASTER) 25 | -i2c.init(I2C.MASTER, baudrate=500000) 26 | +spi = SPI(0, SPI.MASTER, baudrate=2000000, polarity=0, phase=0) 27 | +i2c = I2C(0, I2C.MASTER, baudrate=100000,pins=("P9", "P8")) 28 | 29 | # Setup the OLED : D/C, RST, CS 30 | -oled_module = ssd(128,32,spi,pin("Y4"),pin("Y3"),pin("Y5")) 31 | +oled_module = ssd(128,32,spi,pin('P5'),pin('P6'),pin('P7')) 32 | 33 | # Setup the RTC 34 | rtc_module = urtc.DS1307(i2c) 35 | @@ -71,4 +69,4 @@ 36 | while True: 37 | clear_screen(oled_module) 38 | write_time(oled_module, rtc_module) 39 | - pyb.delay(1000) 40 | + utime.sleep(1) -------------------------------------------------------------------------------- /Source_Ch08/oled_test.py: -------------------------------------------------------------------------------- 1 | import machine 2 | from machine import Pin as pin 3 | from ssd1306 import SSD1306_SPI as ssd 4 | spi = machine.SPI(2, baudrate=8000000, polarity=0, phase=0) 5 | oled = ssd(128,32,spi,pin("Y4"),pin("Y3"),pin("Y5")) 6 | oled.fill(0) 7 | oled.show() 8 | oled.fill(1) 9 | oled.show() 10 | oled.fill(0) 11 | oled.show() 12 | oled.text("Hello, World!", 0, 0) 13 | oled.show() 14 | -------------------------------------------------------------------------------- /Source_Ch08/rtc_test.py: -------------------------------------------------------------------------------- 1 | import urtc 2 | from pyb import I2C 3 | from machine import Pin as pin 4 | i2c = I2C(1, I2C.MASTER) 5 | i2c.init(I2C.MASTER, baudrate=500000) 6 | i2c.scan() 7 | rtc = urtc.DS1307(i2c) 8 | # year, month, day, weekday, hour, minute, second, millisecond) 9 | start_datetime = (2017,07,20,4,7,25,0,0) 10 | rtc.datetime(start_datetime) 11 | print(rtc.datetime()) 12 | -------------------------------------------------------------------------------- /Source_Ch08/ssd1306_pyb.diff: -------------------------------------------------------------------------------- 1 | --- /Users/cbell/Downloads/Adafruit-uRTC-master_orig/urtc.py 2017-04-21 16:52:32.000000000 -0400 2 | +++ /Users/cbell/Downloads/Adafruit-uRTC-master/urtc.py 2017-07-20 19:20:38.000000000 -0400 3 | @@ -40,8 +40,8 @@ 4 | 5 | def _register(self, register, buffer=None): 6 | if buffer is None: 7 | - return self.i2c.readfrom_mem(self.address, register, 1)[0] 8 | - self.i2c.writeto_mem(self.address, register, buffer) 9 | + return self.i2c.mem_read(1, self.address, register)[0] 10 | + self.i2c.mem_write(buffer, self.address, register) 11 | 12 | def _flag(self, register, mask, value=None): 13 | data = self._register(register) 14 | @@ -56,8 +56,8 @@ 15 | 16 | def datetime(self, datetime=None): 17 | if datetime is None: 18 | - buffer = self.i2c.readfrom_mem(self.address, 19 | - self._DATETIME_REGISTER, 7) 20 | + buffer = self.i2c.mem_read(7, self.address, 21 | + self._DATETIME_REGISTER) 22 | if self._SWAP_DAY_WEEKDAY: 23 | day = buffer[3] 24 | weekday = buffer[4] 25 | @@ -128,8 +128,8 @@ 26 | 27 | def alarm_time(self, datetime=None, alarm=0): 28 | if datetime is None: 29 | - buffer = self.i2c.readfrom_mem(self.address, 30 | - self._ALARM_REGISTERS[alarm], 3) 31 | + buffer = self.i2c.mem_read(3, self.address, 32 | + self._ALARM_REGISTERS[alarm]) 33 | day = None 34 | weekday = None 35 | second = None 36 | @@ -145,8 +145,8 @@ 37 | if not buffer[1] & 0x80 else None) 38 | if alarm == 0: 39 | # handle seconds 40 | - buffer = self.i2c.readfrom_mem( 41 | - self.address, self._ALARM_REGISTERS[alarm] - 1, 1) 42 | + buffer = self.i2c.mem_read(1, 43 | + self.address, self._ALARM_REGISTERS[alarm] - 1) 44 | second = (_bcd2bin(buffer[0] & 0x7f) 45 | if not buffer[0] & 0x80 else None) 46 | return datetime_tuple( 47 | @@ -219,8 +219,8 @@ 48 | 49 | def alarm_time(self, datetime=None): 50 | if datetime is None: 51 | - buffer = self.i2c.readfrom_mem(self.address, 52 | - self._ALARM_REGISTER, 4) 53 | + buffer = self.i2c.mem_read(4, self.address, 54 | + self._ALARM_REGISTER) 55 | return datetime_tuple( 56 | weekday=_bcd2bin(buffer[3] & 57 | 0x7f) if not buffer[3] & 0x80 else None, 58 | -------------------------------------------------------------------------------- /Source_Ch08/ssd1306_wipy.diff: -------------------------------------------------------------------------------- 1 | --- ./Pyboard/ssd1306.py 2016-10-30 14:06:02.000000000 -0400 2 | +++ ./WiPy/ssd1306.py 2017-07-20 21:39:31.000000000 -0400 3 | @@ -146,23 +146,23 @@ 4 | 5 | def write_cmd(self, cmd): 6 | self.spi.init(baudrate=self.rate, polarity=0, phase=0) 7 | - self.cs.high() 8 | - self.dc.low() 9 | - self.cs.low() 10 | + self.cs.value(1) 11 | + self.dc.value(0) 12 | + self.cs.value(0) 13 | self.spi.write(bytearray([cmd])) 14 | - self.cs.high() 15 | + self.cs.value(1) 16 | 17 | def write_framebuf(self): 18 | self.spi.init(baudrate=self.rate, polarity=0, phase=0) 19 | - self.cs.high() 20 | - self.dc.high() 21 | - self.cs.low() 22 | + self.cs.value(1) 23 | + self.dc.value(1) 24 | + self.cs.value(0) 25 | self.spi.write(self.buffer) 26 | - self.cs.high() 27 | + self.cs.value(1) 28 | 29 | def poweron(self): 30 | - self.res.high() 31 | + self.res.value(1) 32 | time.sleep_ms(1) 33 | - self.res.low() 34 | + self.res.value(0) 35 | time.sleep_ms(10) 36 | - self.res.high() 37 | + self.res.value(1) 38 | -------------------------------------------------------------------------------- /Source_Ch08/urtc_pyboard.diff: -------------------------------------------------------------------------------- 1 | --- /Users/cbell/Downloads/Adafruit-uRTC-master 3/urtc.py 2017-04-21 16:52:32.000000000 -0400 2 | +++ /Users/cbell/Documents/Writing/MicroPython for the IOT/source/Ch08/Pyboard/urtc.py 2017-07-20 19:20:38.000000000 -0400 3 | @@ -40,8 +40,8 @@ 4 | 5 | def _register(self, register, buffer=None): 6 | if buffer is None: 7 | - return self.i2c.readfrom_mem(self.address, register, 1)[0] 8 | - self.i2c.writeto_mem(self.address, register, buffer) 9 | + return self.i2c.mem_read(1, self.address, register)[0] 10 | + self.i2c.mem_write(buffer, self.address, register) 11 | 12 | def _flag(self, register, mask, value=None): 13 | data = self._register(register) 14 | @@ -56,8 +56,8 @@ 15 | 16 | def datetime(self, datetime=None): 17 | if datetime is None: 18 | - buffer = self.i2c.readfrom_mem(self.address, 19 | - self._DATETIME_REGISTER, 7) 20 | + buffer = self.i2c.mem_read(7, self.address, 21 | + self._DATETIME_REGISTER) 22 | if self._SWAP_DAY_WEEKDAY: 23 | day = buffer[3] 24 | weekday = buffer[4] 25 | @@ -128,8 +128,8 @@ 26 | 27 | def alarm_time(self, datetime=None, alarm=0): 28 | if datetime is None: 29 | - buffer = self.i2c.readfrom_mem(self.address, 30 | - self._ALARM_REGISTERS[alarm], 3) 31 | + buffer = self.i2c.mem_read(3, self.address, 32 | + self._ALARM_REGISTERS[alarm]) 33 | day = None 34 | weekday = None 35 | second = None 36 | @@ -145,8 +145,8 @@ 37 | if not buffer[1] & 0x80 else None) 38 | if alarm == 0: 39 | # handle seconds 40 | - buffer = self.i2c.readfrom_mem( 41 | - self.address, self._ALARM_REGISTERS[alarm] - 1, 1) 42 | + buffer = self.i2c.mem_read(1, 43 | + self.address, self._ALARM_REGISTERS[alarm] - 1) 44 | second = (_bcd2bin(buffer[0] & 0x7f) 45 | if not buffer[0] & 0x80 else None) 46 | return datetime_tuple( 47 | @@ -219,8 +219,8 @@ 48 | 49 | def alarm_time(self, datetime=None): 50 | if datetime is None: 51 | - buffer = self.i2c.readfrom_mem(self.address, 52 | - self._ALARM_REGISTER, 4) 53 | + buffer = self.i2c.mem_read(4, self.address, 54 | + self._ALARM_REGISTER) 55 | return datetime_tuple( 56 | weekday=_bcd2bin(buffer[3] & 57 | 0x7f) if not buffer[3] & 0x80 else None, 58 | -------------------------------------------------------------------------------- /Source_Ch09/Pyboard/ped_part1_pyb.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 9 2 | # 3 | # Project 2: A MicroPython Pedestrian Crosswalk Simulator 4 | # Part 1 - controlling LEDs and button as input 5 | # 6 | # Required Components: 7 | # - Pyboard 8 | # - (2) Red LEDs 9 | # - (2) Yellow LEDs 10 | # - (1) Green LED 11 | # - (5) 220 Ohm resistors 12 | # - (1) breadboard friendly momentary button 13 | # 14 | # Note: this only runs on the Pyboard. 15 | # 16 | # Imports for the project 17 | from pyb import Pin, delay, ExtInt 18 | 19 | # Setup the button and LEDs 20 | button = Pin('X1', Pin.IN, Pin.PULL_UP) 21 | led1 = Pin('X7', Pin.OUT_PP) 22 | led2 = Pin('X6', Pin.OUT_PP) 23 | led3 = Pin('X5', Pin.OUT_PP) 24 | led4 = Pin('X4', Pin.OUT_PP) 25 | led5 = Pin('X3', Pin.OUT_PP) 26 | 27 | # Setup lists for the LEDs 28 | stoplight = [led1, led2, led3] 29 | walklight = [led4, led5] 30 | 31 | # Turn off the LEDs 32 | for led in stoplight: 33 | led.low() 34 | for led in walklight: 35 | led.low() 36 | 37 | # Start with green stoplight and red walklight 38 | stoplight[2].high() 39 | walklight[0].high() 40 | 41 | # We need a method to cycle the stoplight and walklight 42 | # 43 | # We toggle from green to yellow for 2 seconds 44 | # then red for 20 seconds. 45 | def cycle_lights(): 46 | # Go yellow. 47 | stoplight[2].low() 48 | stoplight[1].high() 49 | # Wait 2 seconds 50 | delay(2000) 51 | # Go red and turn on walk light 52 | stoplight[1].low() 53 | stoplight[0].high() 54 | delay(500) # Give the pedestrian a chance to see it 55 | walklight[0].low() 56 | walklight[1].high() 57 | # After 10 seconds, start blinking the walk light 58 | delay(10000) 59 | for i in range(0,10): 60 | walklight[1].low() 61 | delay(500) 62 | walklight[1].high() 63 | delay(500) 64 | 65 | # Stop=green, walk=red 66 | walklight[1].low() 67 | walklight[0].high() 68 | delay(500) # Give the pedestrian a chance to see it 69 | stoplight[0].low() 70 | stoplight[2].high() 71 | 72 | # Create callback for the button 73 | def button_pressed(line): 74 | cur_value = button.value() 75 | active = 0 76 | while (active < 50): 77 | if button.value() != cur_value: 78 | active += 1 79 | else: 80 | active = 0 81 | delay(1) 82 | print("") 83 | if active: 84 | cycle_lights() 85 | else: 86 | print("False press") 87 | 88 | # Create an interrupt for the button 89 | e = ExtInt('X1', ExtInt.IRQ_FALLING, Pin.PULL_UP, button_pressed) 90 | 91 | -------------------------------------------------------------------------------- /Source_Ch09/Pyboard/ped_part2_pyb.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 9 2 | # 3 | # Project 2: A MicroPython Pedestrian Crosswalk Simulator 4 | # Part 2 - Controlling the walklight remotely 5 | # 6 | # Required Components: 7 | # - Pyboard 8 | # - (2) Red LEDs 9 | # - (2) Yellow LEDs 10 | # - (1) Green LED 11 | # - (5) 220 Ohm resistors 12 | # - (1) breadboard friendly momentary button 13 | # - (1) CC3000 breakout board 14 | # 15 | # Note: this only runs on the Pyboard. 16 | # 17 | # Imports for the project 18 | from pyb import Pin, delay, ExtInt, SPI 19 | import network 20 | import usocket as socket 21 | 22 | # Setup network connection 23 | nic = network.CC3K(SPI(2), Pin.board.Y5, Pin.board.Y4, Pin.board.Y3) 24 | # Replace the following with yout SSID and password 25 | nic.connect("YOUR_SSID", "YOUR_PASSWORD") 26 | print("Connecting...") 27 | while not nic.isconnected(): 28 | delay(50) 29 | print("Connected!") 30 | print("My IP address is: {0}".format(nic.ifconfig()[0])) 31 | 32 | # HTML web page for the project 33 | HTML_RESPONSE = """ 34 | 35 | 36 | MicroPython for the IOT - Project 2 37 | 38 |

MicroPython for the IOT - Project 2


39 |
A simple project to demonstrate how to control hardware over the Internet.


40 |
41 |
42 | 43 |

44 | 45 |
46 |
47 | 48 | """ 49 | 50 | # Setup the LEDs 51 | led1 = Pin('X7', Pin.OUT_PP) 52 | led2 = Pin('X6', Pin.OUT_PP) 53 | led3 = Pin('X5', Pin.OUT_PP) 54 | led4 = Pin('X4', Pin.OUT_PP) 55 | led5 = Pin('X3', Pin.OUT_PP) 56 | 57 | # Setup lists for the LEDs 58 | stoplight = [led1, led2, led3] 59 | walklight = [led4, led5] 60 | 61 | # Turn off the LEDs 62 | for led in stoplight: 63 | led.low() 64 | for led in walklight: 65 | led.low() 66 | 67 | # Start with green stoplight and red walklight 68 | stoplight[2].high() 69 | walklight[0].high() 70 | 71 | # We need a method to cycle the stoplight and walklight 72 | # 73 | # We toggle from green to yellow for 2 seconds 74 | # then red for 20 seconds. 75 | def cycle_lights(): 76 | # Go yellow. 77 | stoplight[2].low() 78 | stoplight[1].high() 79 | # Wait 2 seconds 80 | delay(2000) 81 | # Go red and turn on walk light 82 | stoplight[1].low() 83 | stoplight[0].high() 84 | delay(500) # Give the pedestrian a chance to see it 85 | walklight[0].low() 86 | walklight[1].high() 87 | # After 10 seconds, start blinking the walk light 88 | delay(10000) 89 | for i in range(0,10): 90 | walklight[1].low() 91 | delay(500) 92 | walklight[1].high() 93 | delay(500) 94 | 95 | # Stop=green, walk=red 96 | walklight[1].low() 97 | walklight[0].high() 98 | delay(500) # Give the pedestrian a chance to see it 99 | stoplight[0].low() 100 | stoplight[2].high() 101 | 102 | # Setup the socket and respond to HTML requests 103 | def run(): 104 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 105 | sock.bind(('', 80)) 106 | sock.listen(5) 107 | server_on = True 108 | while server_on: 109 | client, address = sock.accept() 110 | print("Got a connection from a client at: %s" % str(address)) 111 | request = client.recv(1024) 112 | # Process the request from the client. Payload should appear in pos 6. 113 | # Check for walk button click 114 | walk_clicked = (request[:17] == b'GET /?WALK=PLEASE') 115 | # Check for stop server button click 116 | stop_clicked = (request[:18] == b'GET /?shutdown=now') 117 | if walk_clicked: 118 | print('Requesting walk now!') 119 | cycle_lights() 120 | elif stop_clicked: 121 | server_on = False 122 | print("Goodbye!") 123 | client.send(HTML_RESPONSE) 124 | client.close() 125 | sock.close() 126 | -------------------------------------------------------------------------------- /Source_Ch09/WiPy/ped_part1_wipy.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 9 2 | # 3 | # Project 2: A MicroPython Pedestrian Crosswalk Simulator 4 | # Part 1 - controlling LEDs and button as input 5 | # 6 | # Required Components: 7 | # - WiPy 8 | # - (2) Red LEDs 9 | # - (2) Yellow LEDs 10 | # - (1) Green LED 11 | # - (5) 220 Ohm resistors 12 | # - (1) breadboard friendly momentary button 13 | # 14 | # Note: this only runs on the WiPy. 15 | # 16 | # Imports for the project 17 | from machine import Pin 18 | import utime 19 | 20 | # Setup the button and LEDs 21 | button = Pin('P23', Pin.IN, Pin.PULL_UP) 22 | led1 = Pin('P3', Pin.OUT) 23 | led2 = Pin('P4', Pin.OUT) 24 | led3 = Pin('P5', Pin.OUT) 25 | led4 = Pin('P6', Pin.OUT) 26 | led5 = Pin('P7', Pin.OUT) 27 | 28 | # Setup lists for the LEDs 29 | stoplight = [led1, led2, led3] 30 | walklight = [led4, led5] 31 | 32 | # Turn off the LEDs 33 | for led in stoplight: 34 | led.value(0) 35 | for led in walklight: 36 | led.value(0) 37 | 38 | # Start with green stoplight and red walklight 39 | stoplight[2].value(1) 40 | walklight[0].value(1) 41 | 42 | # We need a method to cycle the stoplight and walklight 43 | # 44 | # We toggle from green to yellow for 2 seconds 45 | # then red for 20 seconds. 46 | def cycle_lights(): 47 | # Go yellow. 48 | stoplight[2].value(0) 49 | stoplight[1].value(1) 50 | # Wait 2 seconds 51 | utime.sleep(2) 52 | # Go red and turn on walk light 53 | stoplight[1].value(0) 54 | stoplight[0].value(1) 55 | utime.sleep_ms(500) # Give the pedestrian a chance to see it 56 | walklight[0].value(0) 57 | walklight[1].value(1) 58 | # After 10 seconds, start blinking the walk light 59 | utime.sleep(1) 60 | for i in range(0,10): 61 | walklight[1].value(0) 62 | utime.sleep_ms(500) 63 | walklight[1].value(1) 64 | utime.sleep_ms(500) 65 | 66 | # Stop=green, walk=red 67 | walklight[1].value(0) 68 | walklight[0].value(1) 69 | utime.sleep_ms(500) # Give the pedestrian a chance to see it 70 | stoplight[0].value(0) 71 | stoplight[2].value(1) 72 | 73 | # Create callback for the button 74 | def button_pressed(line): 75 | cur_value = button.value() 76 | active = 0 77 | while (active < 50): 78 | if button.value() != cur_value: 79 | active += 1 80 | else: 81 | active = 0 82 | utime.sleep_ms(1) 83 | print("") 84 | if active: 85 | cycle_lights() 86 | else: 87 | print("False press") 88 | 89 | # Create an interrupt for the button 90 | button.callback(Pin.IRQ_FALLING, button_pressed) 91 | 92 | -------------------------------------------------------------------------------- /Source_Ch09/WiPy/ped_part2_wipy.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 9 2 | # 3 | # Project 2: A MicroPython Pedestrian Crosswalk Simulator 4 | # Part 3 - controlling LEDs over the Internet 5 | # 6 | # Required Components: 7 | # - WiPy 8 | # - (2) Red LEDs 9 | # - (2) Yellow LEDs 10 | # - (1) Green LED 11 | # - (5) 220 Ohm resistors 12 | # 13 | # Note: this only runs on the WiPy. 14 | # 15 | # Imports for the project 16 | from machine import Pin 17 | import usocket as socket 18 | import utime 19 | import machine 20 | from network import WLAN 21 | 22 | # Setup the board to connect to our network. 23 | wlan = WLAN(mode=WLAN.STA) 24 | nets = wlan.scan() 25 | for net in nets: 26 | if net.ssid == 'YOUR_SSID': 27 | print('Network found!') 28 | wlan.connect(net.ssid, auth=(net.sec, 'YOUR_PASSWORD'), timeout=5000) 29 | while not wlan.isconnected(): 30 | machine.idle() # save power while waiting 31 | print('WLAN connection succeeded!') 32 | print("My IP address is: {0}".format(wlan.ifconfig()[0])) 33 | break 34 | 35 | # HTML web page for the project 36 | HTML_RESPONSE = """ 37 | 38 | 39 | MicroPython for the IOT - Project 2 40 | 41 |

MicroPython for the IOT - Project 2


42 |
A simple project to demonstrate how to control hardware over the Internet.


43 |
44 |
45 | 46 |

47 | 48 |
49 |
50 | 51 | """ 52 | 53 | # Setup the LEDs (button no longer needed) 54 | led1 = Pin('P3', Pin.OUT) 55 | led2 = Pin('P4', Pin.OUT) 56 | led3 = Pin('P5', Pin.OUT) 57 | led4 = Pin('P6', Pin.OUT) 58 | led5 = Pin('P7', Pin.OUT) 59 | 60 | # Setup lists for the LEDs 61 | stoplight = [led1, led2, led3] 62 | walklight = [led4, led5] 63 | 64 | # Turn off the LEDs 65 | for led in stoplight: 66 | led.value(0) 67 | for led in walklight: 68 | led.value(0) 69 | 70 | # Start with green stoplight and red walklight 71 | stoplight[2].value(1) 72 | walklight[0].value(1) 73 | 74 | # We need a method to cycle the stoplight and walklight 75 | # 76 | # We toggle from green to yellow for 2 seconds 77 | # then red for 20 seconds. 78 | def cycle_lights(): 79 | # Go yellow. 80 | stoplight[2].value(0) 81 | stoplight[1].value(1) 82 | # Wait 2 seconds 83 | utime.sleep(2) 84 | # Go red and turn on walk light 85 | stoplight[1].value(0) 86 | stoplight[0].value(1) 87 | utime.sleep_ms(500) # Give the pedestrian a chance to see it 88 | walklight[0].value(0) 89 | walklight[1].value(1) 90 | # After 10 seconds, start blinking the walk light 91 | utime.sleep(1) 92 | for i in range(0,10): 93 | walklight[1].value(0) 94 | utime.sleep_ms(500) 95 | walklight[1].value(1) 96 | utime.sleep_ms(500) 97 | 98 | # Stop=green, walk=red 99 | walklight[1].value(0) 100 | walklight[0].value(1) 101 | utime.sleep_ms(500) # Give the pedestrian a chance to see it 102 | stoplight[0].value(0) 103 | stoplight[2].value(1) 104 | 105 | # Setup the socket and respond to HTML requests 106 | def run(): 107 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 108 | sock.bind(('', 80)) 109 | sock.listen(5) 110 | server_on = True 111 | while server_on: 112 | client, address = sock.accept() 113 | print("Got a connection from a client at: %s" % str(address)) 114 | request = client.recv(1024) 115 | # Process the request from the client. Payload should appear in pos 6. 116 | # Check for walk button click 117 | walk_clicked = (request[:17] == b'GET /?WALK=PLEASE') 118 | # Check for stop server button click 119 | stop_clicked = (request[:18] == b'GET /?shutdown=now') 120 | if walk_clicked: 121 | print('Requesting walk now!') 122 | cycle_lights() 123 | elif stop_clicked: 124 | server_on = False 125 | print("Goodbye!") 126 | client.send(HTML_RESPONSE) 127 | client.close() 128 | sock.close() 129 | -------------------------------------------------------------------------------- /Source_Ch09/response.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | MicroPython for the IOT - Project 2 4 | 5 | 6 | 7 |

Pedestrian Stoplight Simulation

8 |
9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /Source_Ch10/Pyboard/plant_monitor.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 10 2 | # 3 | # Project 3: MicroPython Plant Monitoring 4 | # 5 | # Plant monitor class 6 | # 7 | # Note: this only runs on the Pyboard. 8 | from pyb import ADC, delay, Pin, SD 9 | import os 10 | import pyb 11 | 12 | # Thresholds for the sensors 13 | LOWER_THRESHOLD = 500 14 | UPPER_THRESHOLD = 2500 15 | UPDATE_FREQ = 120 # seconds 16 | 17 | # File name for the data 18 | SENSOR_DATA = "plant_data.csv" 19 | 20 | # Format the time (epoch) for a better view 21 | def _format_time(tm_data): 22 | # Use a special shortcut to unpack tuple: *tm_data 23 | return "{0}-{1:0>2}-{2:0>2} {3:0>2}:{4:0>2}:{5:0>2}".format(*tm_data) 24 | 25 | class PlantMonitor: 26 | """ 27 | This class reads soil moisture from one or more sensors and writes the 28 | data to a comma-separated value (csv) file as specified in the constructor. 29 | """ 30 | 31 | # Initialization for the class (the constructor) 32 | def __init__(self, rtc): 33 | self.rtc = rtc 34 | 35 | # Try to access the SD card and make the new path 36 | try: 37 | self.sensor_file = "/sd/{0}".format(filename) 38 | f = open(self.sensor_file, 'r') 39 | f.close() 40 | print("INFO: Using SD card for data.") 41 | except: 42 | print("ERROR: cannot mount SD card, reverting to flash!") 43 | self.sensor_file = SENSOR_DATA 44 | print("Data filename = {0}".format(self.sensor_file)) 45 | 46 | # Setup the dictionary for each soil moisture sensor 47 | soil_moisture1 = { 48 | 'sensor': ADC(Pin('X19')), 49 | 'power': Pin('X20', Pin.OUT_PP), 50 | 'location': 'Green ceramic pot on top shelf', 51 | 'num': 1, 52 | } 53 | soil_moisture2 = { 54 | 'sensor': ADC(Pin('X20')), 55 | 'power': Pin('X21', Pin.OUT_PP), 56 | 'location': 'Fern on bottom shelf', 57 | 'num': 2, 58 | } 59 | # Setup a list for each sensor dictionary 60 | self.sensors = [soil_moisture1, soil_moisture2] 61 | # Setup the alarm to read the sensors 62 | self.alarm = pyb.millis() 63 | print("Plant Monitor class is ready...") 64 | 65 | # Clear the log 66 | def clear_log(self): 67 | log_file = open(self.sensor_file, 'w') 68 | log_file.close() 69 | 70 | # Get the filename we're using after the check for SD card 71 | def get_filename(self): 72 | return self.sensor_file 73 | 74 | # Read the sensor 10 times and average the values read 75 | def _get_value(self, sensor, power): 76 | total = 0 77 | # Turn power on 78 | power.high() 79 | for i in range (0,10): 80 | # Wait for sensor to power on and settle 81 | delay(5000) 82 | # Read the value 83 | value = sensor.read() 84 | total += value 85 | # Turn sensor off 86 | power.low() 87 | return int(total/10) 88 | 89 | # Monitor the sensors, read the values and save them 90 | def read_sensors(self): 91 | if pyb.elapsed_millis(self.alarm) < (UPDATE_FREQ * 1000): 92 | return 93 | self.alarm = pyb.millis() 94 | log_file = open(self.sensor_file, 'a') 95 | for sensor in self.sensors: 96 | # Read the data from the sensor and convert the value 97 | value = self._get_value(sensor['sensor'], sensor['power']) 98 | print("Value read: {0}".format(value)) 99 | time_data = self.rtc.datetime() 100 | # datetime,num,value,enum,location 101 | log_file.write( 102 | "{0},{1},{2},{3},{4}\n".format(_format_time(time_data), 103 | sensor['num'], value, 104 | self._convert_value(value), 105 | sensor['location'])) 106 | log_file.close() 107 | 108 | # Convert the raw sensor value to an enumeration 109 | def _convert_value(self, value): 110 | # If value is less than lower threshold, soil is dry else if it 111 | # is greater than upper threshold, it is wet, else all is well. 112 | if (value <= LOWER_THRESHOLD): 113 | return "dry" 114 | elif (value >= UPPER_THRESHOLD): 115 | return "wet" 116 | return "ok" 117 | -------------------------------------------------------------------------------- /Source_Ch10/Pyboard/plant_pyboard.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 10 2 | # 3 | # Project 3: MicroPython Plant Monitoring 4 | # 5 | # Required Components: 6 | # - Pyboard 7 | # - (N) Soil moisture sensors (one for each plant) 8 | # 9 | # Note: this only runs on the Pyboard. 10 | # 11 | # Imports for the project 12 | from pyb import delay, SPI 13 | from pyb import I2C 14 | import network 15 | import urtc 16 | import usocket as socket 17 | from plant_monitor import PlantMonitor 18 | 19 | # Setup the I2C interface for the rtc 20 | i2c = I2C(1, I2C.MASTER) 21 | i2c.init(I2C.MASTER, baudrate=500000) 22 | 23 | # HTML web page for the project 24 | HTML_TABLE_ROW = "{0}{1}{2}{3}{4}" 25 | 26 | # Setup the board to connect to our network. 27 | def connect(): 28 | nic = network.CC3K(SPI(2), Pin.board.Y5, Pin.board.Y4, Pin.board.Y3) 29 | # Replace the following with yout SSID and password 30 | print("Connecting...") 31 | nic.connect("YOUR_SSID", "YOUR_PASSWORD") 32 | while not nic.isconnected(): 33 | delay(50) 34 | print("Connected!") 35 | print("My IP address is: {0}".format(nic.ifconfig()[0])) 36 | return True 37 | 38 | # Read HTML from file and send to client a row at a time. 39 | def send_html_file(filename, client): 40 | html = open(filename, 'r') 41 | for row in html: 42 | client.send(row) 43 | html.close() 44 | 45 | # Send the sensor data to the client. 46 | def send_sensor_data(client, filename): 47 | send_html_file("part1.html", client) 48 | log_file = open(filename, 'r') 49 | for row in log_file: 50 | cols = row.strip("\n").split(",") # split row by commas 51 | # build the table string if all parts are there 52 | if len(cols) >= 5: 53 | html = HTML_TABLE_ROW.format(cols[0], cols[1], cols[2], 54 | cols[3], cols[4]) 55 | # send the row to the client 56 | client.send(html) 57 | delay(50) 58 | log_file.close() 59 | send_html_file("part2.html", client) 60 | 61 | # Setup the socket and respond to HTML requests 62 | def run(): 63 | # Connect to the network 64 | if not connect(): 65 | sys.exit(-1) 66 | 67 | # Setup the real time clock 68 | rtc = urtc.DS1307(i2c) 69 | # 70 | # NOTE: We only need to set the datetime once. Uncomment these 71 | # lines only on the first run of a new RTC module or 72 | # whenever you change the battery. 73 | # (year, month, day, weekday, hour, minute, second, millisecond) 74 | #start_datetime = (2017,07,20,4,9,0,0,0) 75 | #rtc.datetime(start_datetime) 76 | 77 | # Setup the plant monitoring object instance from the plant_monitoring class 78 | plants = PlantMonitor(rtc) 79 | filename = plants.get_filename() 80 | 81 | # Setup the HTML server 82 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 83 | sock.bind(('', 80)) 84 | sock.setblocking(False) # We must use polling for Pyboard. 85 | sock.listen(5) 86 | print("Ready for connections...") 87 | server_on = True 88 | while server_on: 89 | try: 90 | client, address = sock.accept() 91 | except OSError as err: 92 | # Do check for reading sensors here 93 | plants.read_sensors() 94 | delay(50) 95 | continue 96 | print("Got a connection from a client at: %s" % str(address)) 97 | request = client.recv(1024) 98 | 99 | # Allow for clearing of the log, but be careful! The auto refresh 100 | # will resend this command if you do not clear it from your URL 101 | # line. 102 | 103 | if (request[:14] == b'GET /CLEAR_LOG'): 104 | print('Requesting clear log.') 105 | plants.clear_log() 106 | send_sensor_data(client, filename) 107 | client.close() 108 | sock.close() 109 | -------------------------------------------------------------------------------- /Source_Ch10/WiPy/plant_monitor.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 10 2 | # 3 | # Project 3: MicroPython Plant Monitoring 4 | # 5 | # Plant monitor class 6 | # 7 | # Note: this only runs on the WiPy. 8 | from machine import ADC, Pin, SD, Timer 9 | import os 10 | import utime 11 | 12 | # Thresholds for the sensors 13 | LOWER_THRESHOLD = 500 14 | UPPER_THRESHOLD = 2500 15 | UPDATE_FREQ = 120 # seconds 16 | 17 | # File name for the data 18 | SENSOR_DATA = "plant_data.csv" 19 | 20 | # Format the time (epoch) for a better view 21 | def _format_time(tm_data): 22 | # Use a special shortcut to unpack tuple: *tm_data 23 | return "{0}-{1:0>2}-{2:0>2} {3:0>2}:{4:0>2}:{5:0>2}".format(*tm_data) 24 | 25 | class PlantMonitor: 26 | """ 27 | This class reads soil moisture from one or more sensors and writes the 28 | data to a comma-separated value (csv) file as specified in the constructor. 29 | """ 30 | 31 | # Initialization for the class (the constructor) 32 | def __init__(self, rtc): 33 | self.rtc = rtc 34 | 35 | # Try to access the SD card and make the new path 36 | try: 37 | sd = SD() 38 | os.mount(sd, '/sd') 39 | self.sensor_file = "/sd/{0}".format(SENSOR_DATA) 40 | print("INFO: Using SD card for data.") 41 | except: 42 | print("ERROR: cannot mount SD card, reverting to flash!") 43 | self.sensor_file = SENSOR_DATA 44 | print("Data filename = {0}".format(self.sensor_file)) 45 | 46 | # Setup the dictionary for each soil moisture sensor 47 | adc = ADC(0) 48 | soil_moisture1 = { 49 | 'sensor': adc.channel(pin='P13', attn=3), 50 | 'power': Pin('P19', Pin.OUT), 51 | 'location': 'Green ceramic pot on top shelf', 52 | 'num': 1, 53 | } 54 | soil_moisture2 = { 55 | 'sensor': adc.channel(pin='P14', attn=3), 56 | 'power': Pin('P20', Pin.OUT), 57 | 'location': 'Fern on bottom shelf', 58 | 'num': 2, 59 | } 60 | # Setup a list for each sensor dictionary 61 | self.sensors = [soil_moisture1, soil_moisture2] 62 | # Setup the alarm to read the sensors 63 | a = Timer.Alarm(handler=self._read_sensors, s=UPDATE_FREQ, 64 | periodic=True) 65 | print("Plant Monitor class is ready...") 66 | 67 | # Clear the log 68 | def clear_log(self): 69 | log_file = open(self.sensor_file, 'w') 70 | log_file.close() 71 | 72 | # Get the filename we're using after the check for SD card 73 | def get_filename(self): 74 | return self.sensor_file 75 | 76 | # Read the sensor 10 times and average the values read 77 | def _get_value(self, sensor, power): 78 | total = 0 79 | # Turn power on 80 | power.value(1) 81 | for i in range (0,10): 82 | # Wait for sensor to power on and settle 83 | utime.sleep(5) 84 | # Read the value 85 | value = sensor.value() 86 | total += value 87 | # Turn sensor off 88 | power.value(0) 89 | return int(total/10) 90 | 91 | # Monitor the sensors, read the values and save them 92 | def _read_sensors(self, line): 93 | log_file = open(self.sensor_file, 'a') 94 | for sensor in self.sensors: 95 | # Read the data from the sensor and convert the value 96 | value = self._get_value(sensor['sensor'], sensor['power']) 97 | print("Value read: {0}".format(value)) 98 | time_data = self.rtc.now() 99 | # datetime,num,value,enum,location 100 | log_file.write( 101 | "{0},{1},{2},{3},{4}\n".format(_format_time(time_data), 102 | sensor['num'], value, 103 | self._convert_value(value), 104 | sensor['location'])) 105 | log_file.close() 106 | 107 | # Convert the raw sensor value to an enumeration 108 | def _convert_value(self, value): 109 | # If value is less than lower threshold, soil is dry else if it 110 | # is greater than upper threshold, it is wet, else all is well. 111 | if (value <= LOWER_THRESHOLD): 112 | return "dry" 113 | elif (value >= UPPER_THRESHOLD): 114 | return "wet" 115 | return "ok" 116 | -------------------------------------------------------------------------------- /Source_Ch10/WiPy/plant_wipy.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 10 2 | # 3 | # Project 3: MicroPython Plant Monitoring 4 | # 5 | # Required Components: 6 | # - WiPy 7 | # - (N) Soil moisture sensors (one for each plant) 8 | # 9 | # Note: this only runs on the WiPy. 10 | # 11 | # Imports for the project 12 | from machine import RTC 13 | import sys 14 | import usocket as socket 15 | import utime 16 | import machine 17 | from network import WLAN 18 | from plant_monitor import PlantMonitor 19 | 20 | # HTML web page for the project 21 | HTML_TABLE_ROW = "{0}{1}{2}{3}{4}" 22 | 23 | # Setup the board to connect to our network. 24 | def connect(): 25 | wlan = WLAN(mode=WLAN.STA) 26 | nets = wlan.scan() 27 | for net in nets: 28 | if net.ssid == 'YOUR_SSID': 29 | print('Network found!') 30 | wlan.connect(net.ssid, auth=(net.sec, 'YOUR_PASSWORD'), timeout=5000) 31 | while not wlan.isconnected(): 32 | machine.idle() # save power while waiting 33 | print('WLAN connection succeeded!') 34 | print("My IP address is: {0}".format(wlan.ifconfig()[0])) 35 | return True 36 | return False 37 | 38 | # Setup the real time clock with the NTP service 39 | def get_ntp(): 40 | rtc = RTC() 41 | print("Time before sync:", rtc.now()) 42 | rtc.ntp_sync("pool.ntp.org") 43 | while not rtc.synced(): 44 | utime.sleep(3) 45 | print("Waiting for NTP server...") 46 | print("Time after sync:", rtc.now()) 47 | return rtc 48 | 49 | # Read HTML from file and send to client a row at a time. 50 | def send_html_file(filename, client): 51 | html = open(filename, 'r') 52 | for row in html: 53 | client.send(row) 54 | html.close() 55 | 56 | # Send the sensor data to the client. 57 | def send_sensor_data(client, filename): 58 | send_html_file("part1.html", client) 59 | log_file = open(filename, 'r') 60 | for row in log_file: 61 | cols = row.strip("\n").split(",") # split row by commas 62 | # build the table string if all parts are there 63 | if len(cols) >= 5: 64 | html = HTML_TABLE_ROW.format(cols[0], cols[1], cols[2], 65 | cols[3], cols[4]) 66 | # send the row to the client 67 | client.send(html) 68 | log_file.close() 69 | send_html_file("part2.html", client) 70 | 71 | # Setup the socket and respond to HTML requests 72 | def run(): 73 | # Connect to the network 74 | if not connect(): 75 | sys.exit(-1) 76 | 77 | # Setup the real time clock 78 | rtc = get_ntp() 79 | 80 | # Setup the plant monitoring object instance from the plant_monitoring class 81 | plants = PlantMonitor(rtc) 82 | filename = plants.get_filename() 83 | 84 | # Setup the HTML server 85 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 86 | sock.bind(('', 80)) 87 | sock.listen(5) 88 | print("Ready for connections...") 89 | server_on = True 90 | while server_on: 91 | client, address = sock.accept() 92 | print("Got a connection from a client at: %s" % str(address)) 93 | request = client.recv(1024) 94 | 95 | # Allow for clearing of the log, but be careful! The auto refresh 96 | # will resend this command if you do not clear it from your URL 97 | # line. 98 | 99 | if (request[:14] == b'GET /CLEAR_LOG'): 100 | print('Requesting clear log.') 101 | plants.clear_log() 102 | send_sensor_data(client, filename) 103 | client.close() 104 | sock.close() 105 | -------------------------------------------------------------------------------- /Source_Ch10/part1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MicroPython for the IOT - Project 3 5 | 6 | 18 | 19 |

MicroPython for the IOT - Project 3


20 |
A simple project to demonstrate how to retrieve sensor data over the Internet.
21 |

Plant Monitoring Data

22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Source_Ch10/part2.html: -------------------------------------------------------------------------------- 1 |
DatetimeSensor NumberRaw ValueMoistureLocation
2 |
3 | 4 | -------------------------------------------------------------------------------- /Source_Ch10/plant_data.csv: -------------------------------------------------------------------------------- 1 | 2017-08-08 20:26:17,1,78,dry,Small fern on bottom shelf on porch 2 | 2017-08-08 20:26:32,2,136,dry,Green pot creeper thing on floor in living room 3 | 2017-08-08 20:26:47,1,128,dry,Small fern on bottom shelf on porch 4 | 2017-08-08 20:27:02,2,112,dry,Green pot creeper thing on floor in living room 5 | -------------------------------------------------------------------------------- /Source_Ch10/sample.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MicroPython for the IOT - Project 3 5 | 6 | 18 | 19 |

MicroPython for the IOT - Project 3


20 |
A simple project to demonstrate how to retrieve sensor data over the Internet.
21 |

Plant Monitoring Data

22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
DatetimeSensor NumberRaw ValueMoistureLocation
13:41:10 05/15/20171220OkSmall fern on bottom shelf on porch
13:41:15 05/15/20172299OkGreen thing on bottom shelf on porch
13:41:22 05/15/20173118DryFlowers on top shelf on porch
13:41:31 05/15/20174500WetCreeper on middle shelf on porch
33 |
34 | 35 | -------------------------------------------------------------------------------- /Source_Ch10/threshold.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 10 2 | # 3 | # Project 3: MicroPython Plant Monitoring 4 | # 5 | # Threshold calibration for soil moisture sensors 6 | # 7 | # Note: this only runs on the WiPy. 8 | from machine import ADC, Pin 9 | import utime 10 | 11 | # Setup the ADC channel for the read (signal) 12 | # Here we choose pin P13 and set attn to 3 to stabilize voltage 13 | adc = ADC(0) 14 | sensor = adc.channel(pin='P13', attn=3) 15 | 16 | # Setup the GPIO pin for powering the sensor. We use Pin 19 17 | power = Pin('P19', Pin.OUT) 18 | # Turn sensor off 19 | power.value(0) 20 | 21 | # Loop 10 times and average the values read 22 | print("Reading 10 values.") 23 | total = 0 24 | for i in range (0,10): 25 | # Turn power on 26 | power.value(1) 27 | # Wait for sensor to power on and settle 28 | utime.sleep(5) 29 | # Read the value 30 | value = sensor.value() 31 | print("Value read: {0}".format(value)) 32 | total += value 33 | # Turn sensor off 34 | power.value(0) 35 | 36 | # Now average the values 37 | print("The average value read is: {0}".format(total/10)) 38 | 39 | 40 | -------------------------------------------------------------------------------- /Source_Ch11/weather.csv: -------------------------------------------------------------------------------- 1 | 23.09,41.92773,101964.1 2 | 23.09,42.0166,101972.2 3 | 23.08,41.93848,101974.9 4 | 23.07,41.90332,101974.9 5 | 23.07,41.92578,101980.3 6 | 23.07,41.94824,101974.9 7 | 23.06,41.92481,101972.1 8 | 23.06,41.92481,101974.8 9 | 23.05,41.95801,101974.8 10 | 23.05,41.93555,101982.9 11 | 23.55,41.91308,101977.5 12 | 24.81,41.91308,101985.6 13 | 24.99,41.92773,101964.1 14 | 25.19,42.0166,101972.2 15 | 26.11,41.93848,101974.9 16 | 28.70,41.90332,101974.9 17 | 29.22,41.92578,101980.3 18 | 28.07,41.94824,101974.9 19 | 26.62,41.92481,101972.1 20 | 25.44,41.92481,101974.8 21 | 24.15,41.95801,101974.8 22 | 23.95,41.93555,101982.9 23 | 22.50,41.91308,101977.5 24 | 21.25,41.91308,101985.6 25 | 20.11,41.92773,101964.1 26 | 22.09,42.0166,101972.2 27 | 21.99,41.93848,101974.9 28 | 21.07,41.90332,101974.9 29 | 20.97,41.92578,101980.3 30 | 20.07,40.13824,101974.9 31 | 19.96,39.32481,101972.1 32 | 20.06,40.62481,101974.8 33 | 21.05,40.95801,101974.8 34 | 22.05,41.93555,101982.9 35 | 23.05,41.91308,101977.5 36 | 23.05,41.91308,101985.6 37 | 23.09,41.92773,101964.1 38 | 23.09,42.0166,101972.2 39 | 23.08,41.93848,101974.9 40 | 23.07,41.90332,101974.9 41 | 23.07,41.92578,101980.3 42 | 23.07,41.94824,101974.9 43 | 23.06,41.92481,101972.1 44 | 23.06,41.92481,101974.8 45 | 23.05,41.95801,101974.8 46 | 23.05,41.93555,101982.9 47 | 23.05,41.91308,101977.5 48 | 23.05,41.91308,101985.6 49 | -------------------------------------------------------------------------------- /Source_Ch11/weather.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 11 2 | # 3 | # Project 4: MicroPython Weather Node 4 | # 5 | # Required Components: 6 | # - WiPy 7 | # - (1) BME280 Weather Sensor 8 | # 9 | # Note: this only runs on the WiPy. 10 | # 11 | # Imports for the project 12 | from network import WLAN 13 | from weather_node import WeatherNode 14 | import machine 15 | 16 | # Define out user id and key 17 | _IO_ID = "YOUR_DEVICE_ID" 18 | _IO_USERNAME ="YOUR_USER_ID" 19 | _IO_KEY = "YOUR_AIO_KEY" 20 | _FREQUENCY = 5 # seconds 21 | 22 | # Setup the board to connect to our network. 23 | def connect(): 24 | wlan = WLAN(mode=WLAN.STA) 25 | nets = wlan.scan() 26 | for net in nets: 27 | if net.ssid == 'YOUR_SSID': 28 | print('Network found!') 29 | wlan.connect(net.ssid, auth=(net.sec, 'YOUR_SSID_PASSWORD'), timeout=5000) 30 | while not wlan.isconnected(): 31 | machine.idle() # save power while waiting 32 | print('WLAN connection succeeded!') 33 | print("My IP address is: {0}".format(wlan.ifconfig()[0])) 34 | return True 35 | return False 36 | 37 | def run(): 38 | # Setup our Internet connection 39 | connect() 40 | 41 | # Run the weather MQTT client 42 | weather_mqtt_client = WeatherNode(_IO_ID, _IO_USERNAME, _IO_KEY, _FREQUENCY) 43 | weather_mqtt_client.run() 44 | -------------------------------------------------------------------------------- /Source_Ch11/weather_node.py: -------------------------------------------------------------------------------- 1 | # MicroPython for the IOT - Chapter 11 2 | # 3 | # Project 4: MicroPython Weather Node - BME280 MQTT Client class 4 | # 5 | # Note: this only runs on the WiPy. 6 | # 7 | # Imports for the project 8 | from machine import I2C 9 | from mqtt import MQTTClient 10 | import bme280 11 | import utime 12 | 13 | class WeatherNode: 14 | """Sensor node using a BME280 sensor to send temperature, humidity, and 15 | barometric pressure to io.adafruit.com MQTT broker.""" 16 | 17 | # Constructor 18 | def __init__(self, io_id, io_user, io_key, frequency, port=1883): 19 | # Turn sensors on/off 20 | self.sensor_on = False 21 | 22 | # Save variables passed for use in other methods 23 | self.io_id = io_id 24 | self.io_user = io_user 25 | self.io_key = io_key 26 | self.update_frequency = frequency 27 | self.port = port 28 | 29 | # Now, setup the sensor 30 | i2c = I2C(0, I2C.MASTER, baudrate=100000) 31 | self.sensor = bme280.BME280(i2c=i2c) 32 | utime.sleep_ms(100) 33 | print("Weather MQTT client is ready.") 34 | 35 | # Reads the sensor. Returns a tuple of (temperature, humidity, pressure) 36 | def read_data(self): 37 | utime.sleep_ms(50) 38 | temperature = self.sensor.read_temperature() / 100.00 39 | humidity = self.sensor.read_humidity() / 1024.00 40 | pressure = self.sensor.read_pressure() / 256.00 41 | return (temperature, humidity, pressure) 42 | 43 | # A test to read data from a file and publish it. 44 | def read_data_test(self, client): 45 | data_file = open("weather.csv", "r") 46 | for row in data_file: 47 | data = row.strip("\n").split(",") 48 | print(" >", data) 49 | client.publish(topic="{0}/feeds/temperature".format(self.io_user), 50 | msg=str(data[0])) 51 | client.publish(topic="{0}/feeds/humidity".format(self.io_user), 52 | msg=str(data[1])) 53 | client.publish(topic="{0}/feeds/pressure".format(self.io_user), 54 | msg=str(data[2])) 55 | utime.sleep(self.update_frequency) 56 | data_file.close() 57 | 58 | # A simple callback to print the message from the server 59 | def message_callback(self, topic, msg): 60 | print("[{0}]: {1}".format(topic, msg)) 61 | self.sensor_on = (msg == b'ON') 62 | 63 | def run(self): 64 | # Now we setup our MQTT client 65 | client = MQTTClient(self.io_id, "io.adafruit.com", user=self.io_user, 66 | password=self.io_key, port=self.port) 67 | client.set_callback(self.message_callback) 68 | client.connect() 69 | client.subscribe(topic="{0}/feeds/sensors".format(self.io_user)) 70 | 71 | while True: 72 | if self.sensor_on: 73 | data = self.read_data() 74 | print(" >", data) 75 | client.publish(topic="{0}/feeds/temperature".format(self.io_user), 76 | msg=str(data[0])) 77 | client.publish(topic="{0}/feeds/humidity".format(self.io_user), 78 | msg=str(data[1])) 79 | client.publish(topic="{0}/feeds/pressure".format(self.io_user), 80 | msg=str(data[2])) 81 | utime.sleep(self.update_frequency) 82 | client.check_msg() 83 | utime.sleep(1) # Check messages only once per second 84 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to Apress Source Code 2 | 3 | Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers. 4 | 5 | ## How to Contribute 6 | 7 | 1. Make sure you have a GitHub account. 8 | 2. Fork the repository for the relevant book. 9 | 3. Create a new branch on which to make your change, e.g. 10 | `git checkout -b my_code_contribution` 11 | 4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted. 12 | 5. Submit a pull request. 13 | 14 | Thank you for your contribution! --------------------------------------------------------------------------------