# cpb_soft_sensing.py

# Demonstrate measurement of several soft sensor inputs in both analog and digital modes.
# The values are indicated using the NeoPixel LEDs and speaker beeps.

# This example uses the A3 pad as a pullup for a resistive sensor connected
# between A2 and GND.  The wiring for this is as follows:
#
#  1. One half of resistive pressure sensor connected to A2 (either half).
#  2. Other half of sensor connected to GND.
#  3. Jumper wire connects A3 to A2.

# The A4 pad is used as a binary digital input with an internal pullup.  The
# wiring for this is follows:
#
#  1. One half of a soft switch sensor is connected to A4 (either half).
#  2. Other half of switch connected to GND.

# ----------------------------------------------------------------
# Import any needed standard Python modules.
import time

# Import the board-specific input/output library.
from adafruit_circuitplayground import cp

# Import the low-level hardware libraries.
import board
import digitalio
import analogio

# ----------------------------------------------------------------
# Initialize hardware.

# Configure the digital input pin used for its pullup resistor bias voltage.
bias = digitalio.DigitalInOut(board.D10)        # pad A3
bias.switch_to_input(pull=digitalio.Pull.UP)

# Configure the analog input pin used to measure the sensor voltage.
sensor = analogio.AnalogIn(board.A2)
scaling = sensor.reference_voltage / (2**16)

# Configure the digital input pin used as a soft switch.
switch = digitalio.DigitalInOut(board.D3)       # pad SCL A4
switch.switch_to_input(pull=digitalio.Pull.UP)

# Configure the NeoPixel LED array for bulk update using show().
cp.pixels.auto_write = False

# Turn down the NeoPixel brightness, otherwise it is somewhat blinding.
cp.pixels.brightness = 0.5

# ----------------------------------------------------------------
# Define functions used by the main loop.

# Use the CPB NeoPixel LEDS as a dial gauge.  A normalized value between zero
# and one is shown as a spot of light like a dial needle.  The USB connector is
# considered to be the top; the LED to its right is zero, with values increasing
# clockwise around to the LED to the left of the USB.  The spot is anti-aliased
# to increase precision, i.e. it cross-fades between LEDs as the value changes.

def update_LED_dial(value):
    # Clamp the input to the range limits.
    clamped = min(max(value, 0.0), 1.0)

    # Convert the normalized value to an indicator angle in degrees with respect to the top.
    indicator = (clamped * 300) + 30

    # Project the angle onto the NeoPixels. The list assumes the Neopixels are
    # uniformly spaced at 30 degree intervals, with the bottom position absent
    # (e.g. at the battery connector).
    for i, led_angle in enumerate((330, 300, 270, 240, 210, 150, 120, 90, 60, 30)):
        diff = abs(indicator - led_angle)
        if diff < 30:
            c = min(max(255 - int(10*diff), 0), 255)
            cp.pixels[i] = (c, c, c) # shade of gray
        else:
            cp.pixels[i] = (0,0,0)   # no light

    # Send new data to the physical LED chain.
    cp.pixels.show()

# ----------------------------------------------------------------
# Initialize global variables for the main loop.

# Integer time stamp for the next console output.
next_print_time = time.monotonic_ns()

# Previous touch value, used to detect changes.
last_touch = 0

# ----------------------------------------------------------------
# Begin the main processing loop.
while True:

    # Read the current integer clock.
    now = time.monotonic_ns()

    # Read the integer sensor value and scale it to a value in Volts.
    volts = sensor.value * scaling

    # Normalize the soft sensor reading.  Typically you'll need to adjust these values to your device.
    pressure = (1.4 - volts) / (1.4 - 0.1)

    # Update the LED dial gauge display.
    update_LED_dial(pressure)

    # Read and invert the soft switch input.
    touched = 0 if switch.value else 1

    # Emit a brief sound when the soft switch is pressed or released
    if touched != last_touch:
        last_touch = touched

        if touched == 1:
            cp.play_tone(392, 0.1)
        else:
            cp.play_tone(262, 0.1)

    # Poll the time stamp to decide whether to emit console output.
    if now >= next_print_time:

        # Advance the time stamp to the next event time.
        next_print_time += 100000000 # 0.1 seconds in nanosecond units

        # Periodically print the readings on the console.
        print(f"({volts}, {pressure}, {touched})")
