The sine wave calculation (ghp's code) provides a sinusoidal movement to the servos, controlling the speed of the rotation. The code is working, and the effect is very pleasing. For anyone that's been paying attention the finished product is below. Can't make any guarantees for the elegance of my bits of code, and I'm fairly sure that a few things in there could be tightened up.
Essentially, the code reads total daily rainfall data captured at my nearest station (Bureau of Meteorology data) from an HTML table on their site and uses this data to set the speed of the cycle. ghp's code translates the movement into the pleasing sine wave across 16 servos, which lower and raise weights on a nylon thread to create a rippling sculpture which changes based on the amount of rainfall.
Code: Select all
import math
import time
import requests
import pandas as pd
from adafruit_servokit import ServoKit
print("""
----------------------------
Rainfall - Kinetic Sculpture
----------------------------
""")
print("Gathering rainfall data")
# Get the rainfall data from BOM
# state the URL
url = 'http://www.bom.gov.au/products/IDV60801/IDV60801.94829.shtml'
# pretend to be a browser to avoid error 403 Forbidden
header = {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.75 Safari/537.36",
"X-Requested-With": "XMLHttpRequest"
}
# open the url
r = requests.get(url, headers=header)
# convert the HTML tables to a Pandas DataFrame
df = pd.read_html(r.text)
# we only want today's data
# today's data is found in the 2nd table on the page
df = df[1]
# pull out the maximum rain measurment
# for some reason either Pandas or the BOM website is confused...
# the rainfall data is in the windspeed column
max = df['Spdkts'].max()
print("Rainfall: "+str(max)+"mm")
# setup adafruit servo HAT
snumber = 16
kit = ServoKit(channels=snumber)
# calculate the sweep cycle time based on rainfall
if max == 0:
total_time = 10
elif max > 0 and max <= 1:
total_time = 7
elif max > 1 and max <= 3:
total_time = 4
elif max > 3 and max <= 6:
total_time = 1
elif max > 3 and max <= 10:
total_time = 0.5
print("Sweep cycle time is: "+str(total_time)+"s")
# there will not be too frequent updates, the update cycle is
wait_time = 0.05
# the number of updates in a cycle will be approximate
N = int( total_time / wait_time)
# build a data array which contains the precalculated sine values.
data = []
for n in range(N):
radian = 2 * math.pi / N * n
data.append ( math.sin(radian) )
def convert_sine_to_0_180(sin_value):
""" you need a conversion from
sine values in range [-1.. +1] to the adafruit servo values [0..180]"""
out = (sin_value + 1.0) * 90.
return round(out, 2)
tick = 0
print("Running servos")
try:
while True:
serv = 0
while serv <= 15:
angle = data[( tick + N // snumber * serv ) % N]
angle = convert_sine_to_0_180(angle)
# uncomment the following line to print all servo angles
#print (serv, tick, s)
# uncomment the following line to run servos
kit.servo[serv].angle = angle
# the following if statement prints a sine wave from servo 0 to terminal
#if serv == 0:
# print ((int(angle)/5) * "*")
serv += 1
tick += 1
time.sleep(wait_time)
except KeyboardInterrupt:
print("Resetting servos")
serv = 0
for serv in range (0, 15):
angle = 0
serv += 1
kit.servo[serv].angle = 0
print("""
----------------------------""")