|
| 1 | +import time |
| 2 | +import struct |
| 3 | +import displayio |
| 4 | +import terminalio |
| 5 | +from microcontroller import nvm |
| 6 | +from adafruit_display_text import label |
| 7 | +from adafruit_bitmap_font import bitmap_font |
| 8 | +import adafruit_imageload |
| 9 | +from adafruit_clue import clue |
| 10 | + |
| 11 | +# ==| USER CONFIG |===================== |
| 12 | +USE_METRIC = False |
| 13 | +DISPLAY_UPDATE = 1 |
| 14 | +HOLD_TO_SET = 1 |
| 15 | +FONT = terminalio.FONT |
| 16 | +BLUE = 0x53E4FF |
| 17 | +ORANGE = 0xFCDF03 |
| 18 | +RED = 0xFA0000 |
| 19 | +DEBOUNCE = 0.05 |
| 20 | +SAMPLES = 10 |
| 21 | +DELAY = 0.05 |
| 22 | +STD_SLP = 1013.25 |
| 23 | +# ==| USER CONFIG |===================== |
| 24 | + |
| 25 | +# configure pressure sensor (see Table 15 in datasheet) |
| 26 | +clue._pressure.mode = 0x03 # normal |
| 27 | +clue._pressure.overscan_pressure = 0x05 # x16 |
| 28 | +clue._pressure.overscan_temperature = 0x02 # x2 |
| 29 | +clue._pressure.iir_filter = 0x02 # 4 |
| 30 | +clue._pressure.standby_period = 0x01 # 62.5 ms |
| 31 | + |
| 32 | +# restore saved sea level pressure from NVM |
| 33 | +clue.sea_level_pressure = struct.unpack("f", nvm[0:4])[0] |
| 34 | + |
| 35 | +# -------------------------------------------------------------------- |
| 36 | +# D I S P L A Y S E T U P |
| 37 | +# -------------------------------------------------------------------- |
| 38 | + |
| 39 | +# create main display group |
| 40 | +splash = displayio.Group(max_size=4) |
| 41 | +clue.display.show(splash) |
| 42 | + |
| 43 | +# background |
| 44 | +bg_bmp, bg_pal = adafruit_imageload.load( |
| 45 | + "/network23.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette |
| 46 | +) |
| 47 | +for i, color in enumerate(bg_pal): |
| 48 | + if color == 0xFF0000: |
| 49 | + bg_pal.make_transparent(i) |
| 50 | + break |
| 51 | +background = displayio.TileGrid(bg_bmp, pixel_shader=bg_pal) |
| 52 | + |
| 53 | +# a group for both altitude readouts |
| 54 | +alti_readouts = displayio.Group(max_size=2, scale=6) |
| 55 | + |
| 56 | +# altitude (corrected) |
| 57 | +alti_disp = label.Label(FONT, text="12345", color=ORANGE) |
| 58 | +alti_disp.anchor_point = (0, 0) |
| 59 | +alti_disp.anchored_position = (7, 2) |
| 60 | + |
| 61 | +# altitude (uncorrected) |
| 62 | +alt2_disp = label.Label(FONT, text="12345", color=ORANGE) |
| 63 | +alt2_disp.anchor_point = (0, 0) |
| 64 | +alt2_disp.anchored_position = (7, 15) |
| 65 | + |
| 66 | +# add both alti's to group |
| 67 | +alti_readouts.append(alti_disp) |
| 68 | +alti_readouts.append(alt2_disp) |
| 69 | + |
| 70 | +# barometric pressure and temperature |
| 71 | +aux_data = label.Label(FONT, text="P: 1234.56 T: 123.4", color=BLUE) |
| 72 | +aux_data.anchor_point = (0, 0) |
| 73 | +aux_data.anchored_position = (16, 212) |
| 74 | + |
| 75 | +# calibration mode indicator |
| 76 | +cal_mode = label.Label(FONT, text=" ", color=RED, scale=4, x=150, y=200) |
| 77 | + |
| 78 | +# add everything to splash |
| 79 | +splash.append(background) |
| 80 | +splash.append(alti_readouts) |
| 81 | +splash.append(aux_data) |
| 82 | +splash.append(cal_mode) |
| 83 | + |
| 84 | +# -------------------------------------------------------------------- |
| 85 | +# H E L P E R F U N C T I O N S |
| 86 | +# -------------------------------------------------------------------- |
| 87 | +def compute_altitude(barometric_pressure, sea_level_pressure): |
| 88 | + """Compute altitude (m) from barometric pressure (hPa) and sea level pressure (hPa).""" |
| 89 | + # https://www.weather.gov/media/epz/wxcalc/pressureAltitude.pdf |
| 90 | + return 44307.69396 * (1 - pow((barometric_pressure / sea_level_pressure), 0.190284)) |
| 91 | + |
| 92 | + |
| 93 | +def compute_sea_level_pressure(barometric_pressure, altitude): |
| 94 | + """Compute sea level pressure (hPa) from barometric pressure (hPa) and altitude (m).""" |
| 95 | + return barometric_pressure * pow((1 - (altitude / 44307.69396)), -5.2553) |
| 96 | + |
| 97 | + |
| 98 | +def average_readings(samples=10, delay=0.05): |
| 99 | + """Return averaged readings for pressure and temperature.""" |
| 100 | + pressure = 0 |
| 101 | + temperature = 0 |
| 102 | + for _ in range(samples): |
| 103 | + pressure += clue.pressure |
| 104 | + temperature += clue.temperature |
| 105 | + time.sleep(delay) |
| 106 | + return pressure / samples, temperature / samples |
| 107 | + |
| 108 | + |
| 109 | +def recalibrate(current_sea_level_pressure=None): |
| 110 | + """Enter current altitude.""" |
| 111 | + cal_mode.text = "CAL" |
| 112 | + alt2_disp.text = "-----" |
| 113 | + # wait for release if still being held |
| 114 | + while clue.button_a and clue.button_b: |
| 115 | + pass |
| 116 | + # get current value |
| 117 | + altitude = int(alti_disp.text) |
| 118 | + done = False |
| 119 | + while not done: |
| 120 | + now = time.monotonic() |
| 121 | + # increase |
| 122 | + if clue.button_a and not clue.button_b: |
| 123 | + altitude -= 1 |
| 124 | + time.sleep(DEBOUNCE) |
| 125 | + # decrease |
| 126 | + elif clue.button_b and not clue.button_a: |
| 127 | + altitude += 1 |
| 128 | + time.sleep(DEBOUNCE) |
| 129 | + # hold both to set |
| 130 | + elif clue.button_a and clue.button_b: |
| 131 | + while clue.button_a and clue.button_b: |
| 132 | + if time.monotonic() - now > HOLD_TO_SET: |
| 133 | + print("done") |
| 134 | + done = True |
| 135 | + break |
| 136 | + alti_disp.text = "{:5d}".format(altitude) |
| 137 | + cal_mode.text = " " |
| 138 | + # change clue settings |
| 139 | + if not USE_METRIC: |
| 140 | + altitude *= 0.3048 |
| 141 | + # get current local pressure |
| 142 | + barometric_pressure, _ = average_readings(SAMPLES, DELAY) |
| 143 | + # compute sea level pressure and set |
| 144 | + clue.sea_level_pressure = compute_sea_level_pressure(barometric_pressure, altitude) |
| 145 | + # store in NVM for later use |
| 146 | + nvm[0:4] = struct.pack("f", clue.sea_level_pressure) |
| 147 | + |
| 148 | + |
| 149 | +def update_display(): |
| 150 | + """Update the display with latest info.""" |
| 151 | + barometric_pressure, temperature = average_readings(SAMPLES, DELAY) |
| 152 | + altitude = compute_altitude(barometric_pressure, clue.sea_level_pressure) |
| 153 | + alt2tude = compute_altitude(barometric_pressure, STD_SLP) |
| 154 | + if not USE_METRIC: |
| 155 | + altitude *= 3.28084 # ft |
| 156 | + alt2tude *= 3.28084 |
| 157 | + # barometric_pressure *= 0.0145038 # psi |
| 158 | + temperature = 32 + 1.8 * temperature # deg F |
| 159 | + alti_disp.text = "{:5d}".format(int(altitude)) |
| 160 | + alt2_disp.text = "{:5d}".format(int(alt2tude)) |
| 161 | + aux_data.text = "P: {:7.2f} T: {:5.1f}".format(barometric_pressure, temperature) |
| 162 | + |
| 163 | + |
| 164 | +# -------------------------------------------------------------------- |
| 165 | +# M A I N L O O P |
| 166 | +# -------------------------------------------------------------------- |
| 167 | +last_update = time.monotonic() |
| 168 | + |
| 169 | +while True: |
| 170 | + |
| 171 | + now = time.monotonic() |
| 172 | + |
| 173 | + # update display with latest info |
| 174 | + if now - last_update > DISPLAY_UPDATE: |
| 175 | + update_display() |
| 176 | + last_update = now |
| 177 | + |
| 178 | + # hold both to recalibrate |
| 179 | + if clue.button_a and clue.button_b: |
| 180 | + # accumulate hold time |
| 181 | + while clue.button_a and clue.button_b: |
| 182 | + if time.monotonic() - now > HOLD_TO_SET: |
| 183 | + print("set") |
| 184 | + recalibrate(clue.sea_level_pressure) |
| 185 | + break |
| 186 | + # wait for release if still being held |
| 187 | + while clue.button_a and clue.button_b: |
| 188 | + pass |
0 commit comments