# cpb_visual_inclinometer.py

# Use the CPB as a visual inclinometer by lighting the onboard NeoPixel LEDS to
# indicate the highest point as it tilts.

# This program uses only onboard hardware: accelerometer, NeoPixel LEDs.

# The sensor data is printed to the serial console in a format suitable for
# plotting.

# This example also demonstrates signal smoothing using a first-order filter.

# ----------------------------------------------------------------
# Import the standard Python math library and time functions.
import math, time

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

# 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

# ----------------------------------------------------------------
# Initialize global variables for the main loop.
plot_timer = 0.0
last_time = time.monotonic()
x = 0
y = 0

# ----------------------------------------------------------------
# Begin the main processing loop.  Empirically, this loop runs about 200 Hz.
while True:
    # Read the accelerometer, which functions primarily as a tilt sensor.
    raw_x, raw_y, z = cp.acceleration

    # Apply first-order low-pass smoothing filters to the acceleration signals.
    x += 0.1 * (raw_x - x)
    y += 0.1 * (raw_y - y)
        
    # Convert the x,y acceleration components to an angle in degrees with
    # respect to the USB connector, increasing clockwise.
    acc_angle = math.atan2(x, y) * 180/math.pi
    
    # Project the tilt angle onto the NeoPixels. The list assumes the Neopixels
    # are uniformly spaced at 30 degree intervals, with the top and bottom
    # positions absent.  The angles are measured analogous to a clock with zero
    # at the top.  Up to three LEDs are lit in proportion to the relative
    # position of the tilt vector and their position.
    for i, led_angle in enumerate((-30, -60, -90, -120, -150, 150, 120, 90, 60, 30)):
        diff = abs(acc_angle - led_angle)
        if diff < 32:
            c = 255 - 8*diff
            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()

    # Periodically generate data in a format suitable for plotting within the Mu Editor.
    now = time.monotonic()
    interval = now - last_time
    last_time = now
    plot_timer -= interval
    if plot_timer < 0.0:
        plot_timer += 0.05  # 20 Hz data
        # scale the angle to radians so it can be plotted on the same scale as X and Y
        radians = acc_angle * (math.pi / 180.0)
        print(f"({x:6.3f},{y:6.3f},{radians:6.3f})")
