|
| 1 | +### mb-soil-moisture.py v1.0 |
| 2 | +### Show soil moisture on micro:bit display using resistive and capacitive sensors |
| 3 | + |
| 4 | +### Tested with BBC micro:bit v1.5 and MicroPython v1.9.2-34-gd64154c73 |
| 5 | + |
| 6 | +### MIT License |
| 7 | + |
| 8 | +### Copyright (c) 2021 Kevin J. Walters |
| 9 | + |
| 10 | +### Permission is hereby granted, free of charge, to any person obtaining a copy |
| 11 | +### of this software and associated documentation files (the "Software"), to deal |
| 12 | +### in the Software without restriction, including without limitation the rights |
| 13 | +### to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 14 | +### copies of the Software, and to permit persons to whom the Software is |
| 15 | +### furnished to do so, subject to the following conditions: |
| 16 | + |
| 17 | +### The above copyright notice and this permission notice shall be included in all |
| 18 | +### copies or substantial portions of the Software. |
| 19 | + |
| 20 | +### THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 21 | +### IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 22 | +### FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 23 | +### AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 24 | +### LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 25 | +### OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 26 | +### SOFTWARE. |
| 27 | + |
| 28 | + |
| 29 | +### This is a micro:bit version of |
| 30 | +### https://github.com/kevinjwalters/circuitpython-examples/blob/master/pico/soil-moisture.py |
| 31 | +### featured in |
| 32 | +### https://www.instructables.com/Soil-Moisture-Sensing-With-the-Maker-Pi-Pico/ |
| 33 | + |
| 34 | +import utime |
| 35 | +from microbit import display, pin0, pin1, sleep |
| 36 | + |
| 37 | +RES_PIN = pin0 |
| 38 | +CAP_PIN = pin1 |
| 39 | + |
| 40 | +### Values for (dry, wet) based on values from CircuitPython version |
| 41 | +RES_RANGE = (1023, 156) |
| 42 | +CAP_RANGE = (593, 343) |
| 43 | + |
| 44 | +### The BBC micro:bit 5x5 LED display can show ten |
| 45 | +### brightness levels in MicroPython 0-9 |
| 46 | +### (The underlying DAL supports a wider range 0-255) |
| 47 | +MAX_BRIGHTNESS = 9 |
| 48 | + |
| 49 | + |
| 50 | +def bar_chart(value, |
| 51 | + columns=(0, 1, 2, 3, 4), |
| 52 | + *, max_value=100): |
| 53 | + """Draw a vertical bar chart based on percentage value |
| 54 | + using variable brightness levels on display.""" |
| 55 | + |
| 56 | + ### Nine brightness levels on 5x5 LED matrix equates |
| 57 | + ### to 45 pixel steps - start at bottow row (no 4) and |
| 58 | + ### light pixels until value px_steps value is reached |
| 59 | + px_steps = round(value * 45 / max_value) |
| 60 | + for y in range(4, -1, -1): |
| 61 | + for x in columns: |
| 62 | + ### The min/max here limit values from 0 to 9 |
| 63 | + display.set_pixel(x, y, |
| 64 | + min(MAX_BRIGHTNESS, max(0, px_steps))) |
| 65 | + px_steps -= MAX_BRIGHTNESS |
| 66 | + |
| 67 | + |
| 68 | +def adc_to_moisture(raw_adc, arid_value, sodden_value): |
| 69 | + """Convert a micro:bit 0-1024 ADC value into a moisture percentage |
| 70 | + using crude linear model.""" |
| 71 | + |
| 72 | + a_lower = min(arid_value, sodden_value) |
| 73 | + a_range = abs(sodden_value - arid_value) |
| 74 | + inverted = arid_value > sodden_value |
| 75 | + |
| 76 | + fraction = (raw_adc - a_lower) / a_range |
| 77 | + if inverted: |
| 78 | + fraction = 1.0 - fraction |
| 79 | + |
| 80 | + return min(100.0, max(0.0, fraction * 100.0)) |
| 81 | + |
| 82 | + |
| 83 | +def get_res_moisture(): |
| 84 | + return adc_to_moisture(RES_PIN.read_analog(), *RES_RANGE) |
| 85 | + |
| 86 | +def get_cap_moisture(): |
| 87 | + return adc_to_moisture(CAP_PIN.read_analog(), *CAP_RANGE) |
| 88 | + |
| 89 | + |
| 90 | +scroll_delay=250 ### default is 150ms |
| 91 | + |
| 92 | +while True: |
| 93 | + ### Read both values and display them slowly |
| 94 | + res_perc = get_res_moisture() |
| 95 | + cap_perc = get_cap_moisture() |
| 96 | + display.scroll("R " + str(round(res_perc)), |
| 97 | + delay=scroll_delay) |
| 98 | + sleep(2000) |
| 99 | + display.scroll("C " + str(round(cap_perc)), |
| 100 | + delay=scroll_delay) |
| 101 | + sleep(2000) |
| 102 | + |
| 103 | + ### Now show both values on bar charts for 20 seconds |
| 104 | + ### but only read fresh capacitive values |
| 105 | + start_time_ms = utime.ticks_ms() |
| 106 | + while True: |
| 107 | + cap_perc = get_cap_moisture() |
| 108 | + bar_chart(res_perc, (0, 1)) ### Left two columns |
| 109 | + bar_chart(cap_perc, (3, 4)) ### Right two columns |
| 110 | + now_time_ms = utime.ticks_ms() |
| 111 | + if utime.ticks_diff(now_time_ms, start_time_ms) > 20000: |
| 112 | + break |
| 113 | + sleep(500) |
0 commit comments