Multiple Http requests before response locks up server

CircuitPython on hardware including Adafruit's boards, and CircuitPython libraries using Blinka on host computers.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
jbailey969
 
Posts: 32
Joined: Sat May 13, 2023 2:06 pm

Multiple Http requests before response locks up server

Post by jbailey969 »

I am serving a webpage using the raspberry pi pico W and used https://learn.adafruit.com/pico-w-http- ... n/overview as guide.

If I hit two buttons quickly so that the response from the first button press is not fully complete everything locks up. If I wait it seems to work fine. Below are some code snippets.

Not sure why I can't do two http requests back to back. I thought they would just queue up. I realize they are calling the same method so I assume it has something to do with that.

Code: Select all

# serve webpage
@server.route("/")
def base(request: HTTPRequest):
    with HTTPResponse(request, content_type=MIMEType.TYPE_HTML) as response: response.send_file("index.html")

# if a button is pressed on the site
@server.route("/", method=HTTPMethod.POST)
def buttonpress(request: HTTPRequest):
    global config
    raw_text = request.raw_request.decode("utf8")
    if "random" in raw_text: 
        config["option_selected"] = "random"
        animateFeller()
    if "forth_of_july" in raw_text: 
        config["option_selected"] = "forth_of_july"
        animateFeller()
    if "christmas" in raw_text: 
        config["option_selected"] = "christmas"
        animateFeller()
    if "halloween" in raw_text: 
        config["option_selected"] = "halloween"
        animateFeller()
    if "birds_dogs" in raw_text: 
        config["option_selected"] = "birds_dogs"
        animateFeller()
    if "birds_dogs_short_version" in raw_text: 
        config["option_selected"] = "birds_dogs_short_version"
        animateFeller()
    if "just_birds" in raw_text: 
        config["option_selected"] = "just_birds"
        animateFeller()
    if "machines" in raw_text: 
        config["option_selected"] = "machines"
        animateFeller()
    if "no_sounds" in raw_text: 
        config["option_selected"] = "no_sounds"
        animateFeller()
    if "owl" in raw_text: 
        config["option_selected"] = "owl"
        animateFeller() 
    if "feller_rest_pos" in raw_text:
        moveFellerToPositionGently(config["feller_rest_pos"])
    if "feller_chop_pos" in raw_text:
        moveFellerToPositionGently(config["feller_chop_pos"])
    if "tree_up_pos" in raw_text:
        moveTreeToPositionGently(config["tree_up_pos"])
    if "tree_down_pos" in raw_text:
        moveTreeToPositionGently(config["tree_down_pos"])
    #  reload site
    with HTTPResponse(request, content_type=MIMEType.TYPE_HTML) as response: response.send_file("index.html")
    
    ....
    
while True:
    pretty_state_machine.update()
    try:
        server.poll()
    except Exception as e:
        print(e)
        continue
    

User avatar
danhalbert
 
Posts: 4651
Joined: Tue Aug 08, 2017 12:37 pm

Re: Multiple Http requests before response locks up server

Post by danhalbert »

Could you show more of the code (maybe upload the whole program as an attachment if it's large)?

When you say "if I wait", do you mean that you wait to push the button, or you are adding a delay internally?

Are you rewriting index.html when the buttons are pushed? So perhaps the previous response is still reading the file?

When it locks up, is there anything printed on the serial console (REPL)?

User avatar
jbailey969
 
Posts: 32
Joined: Sat May 13, 2023 2:06 pm

Re: Multiple Http requests before response locks up server

Post by jbailey969 »

On the web page you can click the button really fast. If you click the button and wait until the routine returns the index.html it works. I believe because it has had the chance to complete the http request and return the index.html. If I inspect in chrome on the network tab I can see the requests being sent out. If the requests are sent out rapidly the previous ones turn red and the code gets stuck.

The controller is the raspberry pi pico w.

here is the code.py

Code: Select all

import os
import time
import gc

import ssl
import wifi
import ipaddress
import socketpool
import adafruit_requests
from adafruit_httpserver.server import HTTPServer
from adafruit_httpserver.request import HTTPRequest
from adafruit_httpserver.response import HTTPResponse
from adafruit_httpserver.methods import HTTPMethod
from adafruit_httpserver.mime_type import MIMEType

import audiomp3
import audiocore
import audiomixer
import audiobusio

import sdcardio
import storage

import board
import microcontroller
import busio
import pwmio
import digitalio

import random
import rtc

from analogio import AnalogIn
from adafruit_motor import servo
from adafruit_debouncer import Debouncer
from analogio import AnalogIn

import files
import animate_feller

def garbage_collect(collection_point):
    gc.collect()
    start_mem = gc.mem_free()
    print( "Point " + collection_point + " Available memory: {} bytes".format(start_mem) )

def reset_pico():
    microcontroller.on_next_reset(microcontroller.RunMode.NORMAL)
    microcontroller.reset()
    
garbage_collect("imports")
        
################################################################################
# Setup hardware

# Setup and analog pin to be used for volume control
# the the volume control is digital by setting mixer voice levels
analog_in = AnalogIn(board.A0)

def get_voltage(pin):
    return (pin.value) / 65536

# Setup the servo, this animation has two the feller and tree
# also get the programmed values for position which is stored on the sdCard
feller_pwm = pwmio.PWMOut(board.GP10, duty_cycle=2 ** 15, frequency=50)
tree_pwm = pwmio.PWMOut(board.GP11, duty_cycle=2 ** 15, frequency=50)

feller_servo = servo.Servo(feller_pwm)
tree_servo = servo.Servo(tree_pwm)

# Setup the switches, there are two the Left and Right or Black and Red
SWITCH_1_PIN = board.GP6 #S1 on animator board
SWITCH_2_PIN = board.GP7 #S2 on animator board

switch_io_1 = digitalio.DigitalInOut(SWITCH_1_PIN)
switch_io_1.direction = digitalio.Direction.INPUT
switch_io_1.pull = digitalio.Pull.UP
left_switch = Debouncer(switch_io_1)

switch_io_2 = digitalio.DigitalInOut(SWITCH_2_PIN)
switch_io_2.direction = digitalio.Direction.INPUT
switch_io_2.pull = digitalio.Pull.UP
right_switch = Debouncer(switch_io_2)

# setup audio on the i2s bus, the animator uses the MAX98357A
# the animator can have one or two MAX98357As. one for mono two for stereo
# both MAX98357As share the same bus
# for mono the MAX98357A defaults to combine channels
# for stereo the MAX98357A SD pin is connected to VCC for right and a resistor to VCC for left
# the audio mixer is used so that volume can be control digitally it is set to stereo
# the sample_rate of the audio mixer is set to 22050 hz.  This is the max the raspberry pi pico can handle
# all files with be in the wave format instead of mp3.  This eliminates the need for decoding
i2s_bclk = board.GP18   # BCLK on MAX98357A
i2s_lrc = board.GP19  # LRC on MAX98357A
i2s_din = board.GP20  # DIN on MAX98357A

audio = audiobusio.I2SOut(bit_clock=i2s_bclk, word_select=i2s_lrc, data=i2s_din)

# Setup sdCard
# the sdCard holds all the media and calibration files
# if the card is missing a voice command is spoken
# the user inserts the card a presses the left button to move forward
sck = board.GP2
si = board.GP3
so = board.GP4
cs = board.GP5
spi = busio.SPI(sck, si, so)
try:
  sdcard = sdcardio.SDCard(spi, cs)
  vfs = storage.VfsFat(sdcard)
  storage.mount(vfs, "/sd")
except:
  wave0 = audiomp3.MP3Decoder(open("wav/micro_sd_card_not_inserted.mp3", "rb"))
  audio.play(wave0)
  while audio.playing:
    pass
  cardInserted = False
  while not cardInserted:
    left_switch.update()
    if left_switch.fell:
        reset_pico()

# Setup the mixer it can play higher quality audio wav using larger wave files
# wave files are less cpu intensive since they are not compressed
num_voices = 2
mixer = audiomixer.Mixer(voice_count=num_voices, sample_rate=22050, channel_count=2,
                         bits_per_sample=16, samples_signed=True, buffer_size=8192)
audio.play(mixer)

garbage_collect("hardware setup")

################################################################################
# Global Variables

# get the calibration settings from various json files which are stored on the sdCard
config = files.read_json_file("/sd/config_feller.json")

tree_last_pos = config["tree_up_pos"]
tree_min = 90
tree_max = 180
if config["tree_down_pos"] < tree_min or config["tree_down_pos"] > tree_max: config["tree_down_pos"] = tree_min
if config["tree_up_pos"] < tree_min or config["tree_up_pos"] > tree_max: config["tree_up_pos"] = tree_max

feller_last_pos = config["feller_rest_pos"]
feller_min = 0
feller_max = 170
if config["feller_rest_pos"] < feller_min or config["feller_rest_pos"] > feller_max: config["feller_rest_pos"] = feller_min
if config["feller_chop_pos"] > feller_max or config["feller_chop_pos"] < feller_min: config["feller_chop_pos"] = feller_max

config_main_menu = files.read_json_file("/sd/feller_menu/main_menu.json")
main_menu = config_main_menu["main_menu"]

config_choose_sounds = files.read_json_file("/sd/feller_menu/choose_sounds.json")
feller_sound_options = config_choose_sounds["choose_sounds"]

config_feller_dialog = files.read_json_file("/sd/feller_dialog/feller_dialog.json")

feller_dialog_positive = config_feller_dialog["feller_dialog_positive"]
feller_dialog_negative = config_feller_dialog["feller_dialog_negative"]
feller_dialog_advice = config_feller_dialog["feller_dialog_advice"]

config_adjust_feller_and_tree = files.read_json_file("/sd/feller_menu/adjust_feller_and_tree.json")
adjust_feller_and_tree = config_adjust_feller_and_tree["adjust_feller_and_tree"]

config_move_feller_and_tree = files.read_json_file("/sd/feller_menu/move_feller_and_tree.json")
move_feller_and_tree = config_move_feller_and_tree["move_feller_and_tree"]

################################################################################
# Global Methods

def reset_to_defaults():
    global config
    config["tree_up_pos"] = 165
    config["tree_down_pos"] = 100
    config["feller_rest_pos"] = 0
    config["feller_chop_pos"] = 150

def setVolume():
    volume = get_voltage(analog_in)
    mixer.voice[0].level = volume
    mixer.voice[1].level = volume
    
def sleepAndUpdateVolume(seconds):
    setVolume()
    time.sleep(seconds)
    
garbage_collect("global variable and methods")

################################################################################
# Dialog
    
def fellerCalAnnouncement():
    global left_switch
    wave0 = audiocore.WaveFile(open("/sd/feller_menu/now_we_can_adjust_the_feller_position.wav", "rb"))
    mixer.voice[0].play( wave0, loop=False )
    while mixer.voice[0].playing:
        pass
    wave0 = audiocore.WaveFile(open("/sd/feller_menu/to_exit_press_and_hold_button_down.wav", "rb"))
    mixer.voice[0].play( wave0, loop=False )
    while mixer.voice[0].playing:
        pass
    
def treeCalAnnouncement():
    wave0 = audiocore.WaveFile(open("/sd/feller_menu/now_we_can_adjust_the_tree_position.wav", "rb"))
    mixer.voice[0].play( wave0, loop=False )
    while mixer.voice[0].playing:
        pass
    wave0 = audiocore.WaveFile(open("/sd/feller_menu/to_exit_press_and_hold_button_down.wav", "rb"))
    mixer.voice[0].play( wave0, loop=False )
    while mixer.voice[0].playing:
        pass
    
def mainMenuAnnouncement():
    if mixer.voice[0].playing:
        mixer.voice[0].stop()
        while mixer.voice[0].playing:
            pass
    else:
        wave0 = audiocore.WaveFile(open("/sd/feller_menu/main_menu.wav", "rb"))
        mixer.voice[0].play( wave0, loop=False )
        while mixer.voice[0].playing:
            pass
        wave0 = audiocore.WaveFile(open("/sd/feller_menu/press_left_button_right_button.wav", "rb"))
        mixer.voice[0].play( wave0, loop=False )
        while mixer.voice[0].playing:
            pass
    
def selectSoundMenuAnnouncement():
    if mixer.voice[0].playing:
        mixer.voice[0].stop()
        while mixer.voice[0].playing:
            pass
    else:
        wave0 = audiocore.WaveFile(open("/sd/feller_menu/sound_selection_menu.wav", "rb"))
        mixer.voice[0].play( wave0, loop=False )
        while mixer.voice[0].playing:
            pass
        wave0 = audiocore.WaveFile(open("/sd/feller_menu/press_left_button_right_button.wav", "rb"))
        mixer.voice[0].play( wave0, loop=False )
        while mixer.voice[0].playing:
            pass
    
def adjustFellerAndTreeMenuAnnouncement():
    if mixer.voice[0].playing:
        mixer.voice[0].stop()
        while mixer.voice[0].playing:
            pass
    else:
        wave0 = audiocore.WaveFile(open("/sd/feller_menu/adjust_feller_and_tree_menu.wav", "rb"))
        mixer.voice[0].play( wave0, loop=False )
        while mixer.voice[0].playing:
            pass
        wave0 = audiocore.WaveFile(open("/sd/feller_menu/press_left_button_right_button.wav", "rb"))
        mixer.voice[0].play( wave0, loop=False )
        while mixer.voice[0].playing:
            pass
        
def moveFellerAndTreeMenuAnnouncement():
    if mixer.voice[0].playing:
        mixer.voice[0].stop()
        while mixer.voice[0].playing:
            pass
    else:
        wave0 = audiocore.WaveFile(open("/sd/feller_menu/move_feller_and_tree_menu.wav", "rb"))
        mixer.voice[0].play( wave0, loop=False )
        while mixer.voice[0].playing:
            pass
        wave0 = audiocore.WaveFile(open("/sd/feller_menu/press_left_button_right_button.wav", "rb"))
        mixer.voice[0].play( wave0, loop=False )
        while mixer.voice[0].playing:
            pass
        
def checkLimits(min_servo_pos, max_servo_pos, servo_pos):
    if servo_pos < min_servo_pos:
        wave0 = audiocore.WaveFile(open("/sd/feller_menu/limit_reached.wav", "rb"))
        mixer.voice[0].play( wave0, loop=False )
        while mixer.voice[0].playing:
            pass 
        return False
    if servo_pos > max_servo_pos:
        wave0 = audiocore.WaveFile(open("/sd/feller_menu/limit_reached.wav", "rb"))
        mixer.voice[0].play( wave0, loop=False )
        while mixer.voice[0].playing:
            pass 
        return False
    return True

def shortCircuitDialog():
    sleepAndUpdateVolume(0.02)
    left_switch.update()
    if left_switch.fell:
        mixer.voice[0].stop()
        
garbage_collect("dialog methods")

#############################################################################################
# Servo helpers
    
def calibratePosition(servo, movement_type):
    global config
    config[movement_type] = config[movement_type]
    if movement_type == "feller_rest_pos" or movement_type == "feller_chop_pos" :
        min_servo_pos = feller_min
        max_servo_pos = feller_max
        sign = 1
    else:
        min_servo_pos = tree_min
        max_servo_pos = tree_max
        sign = -1
    calibrations_complete = False
    while not calibrations_complete:
        servo.angle = config[movement_type]
        left_switch.update()
        right_switch.update()
        if left_switch.fell:
            config[movement_type] -= 1 * sign
            if checkLimits(min_servo_pos, max_servo_pos, config[movement_type]):
                servo.angle = config[movement_type]
            else:
                config[movement_type] += 1 * sign
        if right_switch.fell:
            button_check = True
            number_cycles = 0  
            while button_check:
                sleepAndUpdateVolume(.1)
                right_switch.update()
                number_cycles += 1
                if number_cycles > 30:
                    wave0 = audiocore.WaveFile(open("/sd/feller_menu/all_changes_complete.wav", "rb"))
                    mixer.voice[0].play( wave0, loop=False )
                    while mixer.voice[0].playing:
                        pass
                    global config
                    config[movement_type] = config[movement_type]
                    files.write_json_file("/sd/config_feller.json",config)
                    button_check = False
                    calibrations_complete = True 
                if right_switch.rose:
                    button_check = False           
            if not calibrations_complete:
                config[movement_type] += 1 * sign
                if checkLimits(min_servo_pos, max_servo_pos, config[movement_type]):
                    servo.angle = config[movement_type]
                else:
                    config[movement_type] -= 1 * sign
    if movement_type == "feller_rest_pos" or movement_type == "feller_chop_pos" :
        global feller_last_pos
        feller_last_pos = config[movement_type]
    else:
        global tree_last_pos
        tree_last_pos = config[movement_type]

def moveFellerToPositionGently (new_position):
    global feller_last_pos
    print("feller angle: " + str(tree_last_pos) + "  " + str(new_position))
    sign = 1
    if feller_last_pos > new_position: sign = - 1
    for feller_angle in range( feller_last_pos, new_position, sign):
        feller_servo.angle = feller_angle
        sleepAndUpdateVolume(0.01)
    feller_servo.angle = new_position 
    feller_last_pos = new_position
    
def moveTreeToPositionGently (new_position):
    global tree_last_pos
    print("tree angle: " + str(tree_last_pos) + "  " + str(new_position))
    sign = 1
    if tree_last_pos > new_position: sign = - 1
    for tree_angle in range( tree_last_pos, new_position, sign): 
        tree_servo.angle = tree_angle
        sleepAndUpdateVolume(0.01)
    tree_servo.angle = new_position
    tree_last_pos = new_position

def moveFellerServo (servo_pos):
    feller_servo.angle = servo_pos
    global feller_last_pos
    feller_last_pos = servo_pos

def moveTreeServo (servo_pos):
    tree_servo.angle = servo_pos
    global tree_last_pos
    tree_last_pos = servo_pos
    
def animateFeller ():
    animate_feller.animation_one(
        sleepAndUpdateVolume, 
        audiocore, 
        mixer, 
        feller_servo, 
        tree_servo, 
        config,
        feller_sound_options, 
        feller_dialog_positive,
        feller_dialog_negative,
        feller_dialog_advice,
        moveFellerServo,
        moveTreeServo,
        moveFellerToPositionGently,
        moveTreeToPositionGently,
        left_switch)

garbage_collect("servo helpers")

################################################################################
# Setup wifi and web server

print("Connecting to WiFi")
#  set static IP address
ipv4 =  ipaddress.IPv4Address("192.168.1.42")
netmask =  ipaddress.IPv4Address("255.255.255.0")
gateway =  ipaddress.IPv4Address("192.168.1.1")
wifi.radio.set_ipv4_address(ipv4=ipv4,netmask=netmask,gateway=gateway)

# make settings.toml file as follows.  Do not save this to github an make sure you added *.toml to your gitignore
# Comments are supported
# CIRCUITPY_WIFI_SSID="YOURSSID"
# CIRCUITPY_WIFI_PASSWORD="YOURPASSWORD"
# CIRCUITPY_WEB_API_PORT=80
# CIRCUITPY_WEB_API_USERNAME=""
# CIRCUITPY_WEB_API_PASSWORD="somepassword"

#  connect to your SSID
wifi.radio.connect(os.getenv('CIRCUITPY_WIFI_SSID'), os.getenv('CIRCUITPY_WIFI_PASSWORD'))

#  prints MAC address to REPL
mystring = [hex(i) for i in wifi.radio.mac_address]
print("My MAC addr:", mystring)

#  prints IP address to REPL
print("My IP address is", wifi.radio.ipv4_address)
print("Connected to WiFi")

# set up server
pool = socketpool.SocketPool(wifi.radio)
server = HTTPServer(pool)

def getTime(): 
    get_time_url = "https://worldtimeapi.org/api/timezone/America/New_York"
    requests = adafruit_requests.Session(pool, ssl.create_default_context())
    try:
        print("Fetching time from %s" % get_time_url)
        response = requests.get(get_time_url)  
        responseObject = files.json_parse(response.text)
        print(responseObject["timezone"])
        print(responseObject["datetime"])
        response.close()
        time.sleep(1)
        return responseObject["datetime"]
    except Exception as e:
        print("Error:\n", str(e))
        print("Resetting pico in 4 seconds")
        time.sleep(4)
        reset_pico()
    
################################################################################
# Setup routes

# serve webpage
@server.route("/")
def base(request: HTTPRequest):
    with HTTPResponse(request, content_type=MIMEType.TYPE_HTML) as response: response.send_file("index.html")

# if a button is pressed on the site
@server.route("/", method=HTTPMethod.POST)
def buttonpress(request: HTTPRequest):
    global config
    raw_text = request.raw_request.decode("utf8")
    if "random" in raw_text: 
        config["option_selected"] = "random"
        animateFeller()
    if "forth_of_july" in raw_text: 
        config["option_selected"] = "forth_of_july"
        animateFeller()
    if "christmas" in raw_text: 
        config["option_selected"] = "christmas"
        animateFeller()
    if "halloween" in raw_text: 
        config["option_selected"] = "halloween"
        animateFeller()
    if "birds_dogs" in raw_text: 
        config["option_selected"] = "birds_dogs"
        animateFeller()
    if "birds_dogs_short_version" in raw_text: 
        config["option_selected"] = "birds_dogs_short_version"
        animateFeller()
    if "just_birds" in raw_text: 
        config["option_selected"] = "just_birds"
        animateFeller()
    if "machines" in raw_text: 
        config["option_selected"] = "machines"
        animateFeller()
    if "no_sounds" in raw_text: 
        config["option_selected"] = "no_sounds"
        animateFeller()
    if "owl" in raw_text: 
        config["option_selected"] = "owl"
        animateFeller() 
    if "feller_rest_pos" in raw_text:
        moveFellerToPositionGently(config["feller_rest_pos"])
    if "feller_chop_pos" in raw_text:
        moveFellerToPositionGently(config["feller_chop_pos"])
    if "tree_up_pos" in raw_text:
        moveTreeToPositionGently(config["tree_up_pos"])
    if "tree_down_pos" in raw_text:
        moveTreeToPositionGently(config["tree_down_pos"])
    #  reload site
    with HTTPResponse(request, content_type=MIMEType.TYPE_HTML) as response: response.send_file("index.html")
    
garbage_collect("web server")

################################################################################
# State Machine

class StateMachine(object):

    def __init__(self):
        self.state = None
        self.states = {}
        self.paused_state = None

    def add_state(self, state):
        self.states[state.name] = state

    def go_to_state(self, state_name):
        if self.state:
            self.state.exit(self)
        self.state = self.states[state_name]
        self.state.enter(self)

    def update(self):
        if self.state:
            self.state.update(self)

    # When pausing, don't exit the state
    def pause(self):
        self.state = self.states['paused']
        self.state.enter(self)

    # When resuming, don't re-enter the state
    def resume_state(self, state_name):
        if self.state:
            self.state.exit(self)
        self.state = self.states[state_name]

    def reset(self):
        reset_pico()
        

################################################################################
# States

# Abstract parent state class.
class State(object):

    def __init__(self):
        pass

    @property
    def name(self):
        return ''

    def enter(self, machine):
        pass

    def exit(self, machine):
        pass

    def update(self, machine):
        if left_switch.fell:
            machine.paused_state = machine.state.name
            machine.pause()
            return False
        return True

class BaseState(State):

    def __init__(self):      
        pass

    @property
    def name(self):
        return 'base_state'

    def enter(self, machine):
        sleepAndUpdateVolume(.1)
        if mixer.voice[0].playing:
            mixer.voice[0].stop()
            while mixer.voice[0].playing:
                pass
        else:
            # set servos to starting position
            moveFellerToPositionGently(config["feller_rest_pos"])
            sleepAndUpdateVolume(0.02)
            moveTreeToPositionGently(config["tree_up_pos"])
            wave0 = audiocore.WaveFile(open("/sd/feller_menu/animations_are_now_active.wav", "rb"))
            mixer.voice[0].play( wave0, loop=False )
            while mixer.voice[0].playing:
                pass
        State.enter(self, machine)

    def exit(self, machine):
        State.exit(self, machine)

    def update(self, machine):
        left_switch.update()
        right_switch.update()
        if left_switch.fell:
            animateFeller()
        if right_switch.fell:
            machine.go_to_state('main_menu')

class MoveFellerAndTree(State):

    def __init__(self):
        self.menuIndex = 0
        self.selectedMenuIndex = 0

    @property
    def name(self):
        return 'move_feller_and_tree'

    def enter(self, machine):
        print('Select a program option')
        moveFellerAndTreeMenuAnnouncement()
        State.enter(self, machine)

    def exit(self, machine):
        State.exit(self, machine)

    def update(self, machine):
        left_switch.update()
        right_switch.update()
        if left_switch.fell:
            if mixer.voice[0].playing:
                mixer.voice[0].stop()
                while mixer.voice[0].playing:
                    pass
            else:
                wave0 = audiocore.WaveFile(open("/sd/feller_menu/" + move_feller_and_tree[self.menuIndex] + ".wav" , "rb"))
                mixer.voice[0].play( wave0, loop=False )
                self.selectedMenuIndex = self.menuIndex
                self.menuIndex +=1
                if self.menuIndex > len(move_feller_and_tree)-1:
                    self.menuIndex = 0
                while mixer.voice[0].playing:
                    shortCircuitDialog()
        if right_switch.fell:
                selected_menu_item = move_feller_and_tree[self.selectedMenuIndex]
                if selected_menu_item == "move_feller_to_rest_position":
                    moveFellerToPositionGently(config["feller_rest_pos"])
                elif selected_menu_item == "move_feller_to_chop_position":
                    moveFellerToPositionGently(config["feller_chop_pos"])
                elif selected_menu_item == "move_tree_to_upright_position":
                    moveTreeToPositionGently(config["tree_up_pos"])
                elif selected_menu_item == "move_tree_to_fallen_position":
                    moveTreeToPositionGently(config["tree_down_pos"])
                else:
                    wave0 = audiocore.WaveFile(open("/sd/feller_menu/all_changes_complete.wav", "rb"))
                    mixer.voice[0].play( wave0, loop=False )
                    while mixer.voice[0].playing:
                        pass
                    machine.go_to_state('base_state')
                     
class AdjustFellerAndTree(State):

    def __init__(self):
        self.menuIndex = 0
        self.selectedMenuIndex = 0

    @property
    def name(self):
        return 'adjust_feller_and_tree'

    def enter(self, machine):
        print('Select a program option')
        adjustFellerAndTreeMenuAnnouncement()
        State.enter(self, machine)

    def exit(self, machine):
        State.exit(self, machine)

    def update(self, machine):
        left_switch.update()
        right_switch.update()
        if left_switch.fell:
            if mixer.voice[0].playing:
                mixer.voice[0].stop()
                while mixer.voice[0].playing:
                    pass
            else:
                wave0 = audiocore.WaveFile(open("/sd/feller_menu/" + adjust_feller_and_tree[self.menuIndex] + ".wav" , "rb"))
                mixer.voice[0].play( wave0, loop=False )
                self.selectedMenuIndex = self.menuIndex
                self.menuIndex +=1
                if self.menuIndex > len(adjust_feller_and_tree)-1:
                    self.menuIndex = 0
                while mixer.voice[0].playing:
                    shortCircuitDialog()
        if right_switch.fell:
                selected_menu_item = adjust_feller_and_tree[self.selectedMenuIndex]
                if selected_menu_item == "move_feller_to_rest_position":
                    moveFellerToPositionGently(config["feller_rest_pos"])
                    fellerCalAnnouncement()
                    calibratePosition(feller_servo, "feller_rest_pos")
                    machine.go_to_state('base_state')
                elif selected_menu_item == "move_feller_to_chop_position":
                    moveFellerToPositionGently(config["feller_chop_pos"])
                    fellerCalAnnouncement()
                    calibratePosition(feller_servo, "feller_chop_pos")
                    machine.go_to_state('base_state')
                elif selected_menu_item == "move_tree_to_upright_position":
                    moveTreeToPositionGently(config["tree_up_pos"])
                    treeCalAnnouncement()
                    calibratePosition(tree_servo, "tree_up_pos")
                    machine.go_to_state('base_state')
                elif selected_menu_item == "move_tree_to_fallen_position":
                    moveTreeToPositionGently(config["tree_down_pos"])
                    treeCalAnnouncement()
                    calibratePosition(tree_servo, "tree_down_pos")
                    machine.go_to_state('base_state')
                else:
                    wave0 = audiocore.WaveFile(open("/sd/feller_menu/all_changes_complete.wav", "rb"))
                    mixer.voice[0].play( wave0, loop=False )
                    while mixer.voice[0].playing:
                        pass
                    machine.go_to_state('base_state')

class ChooseSounds(State):

    def __init__(self):
        self.menuIndex = 0
        self.selectedMenuIndex = 0

    @property
    def name(self):
        return 'choose_sounds'

    def enter(self, machine):
        print('Select a program option')
        selectSoundMenuAnnouncement()
        State.enter(self, machine)

    def exit(self, machine):
        State.exit(self, machine)

    def update(self, machine):
        left_switch.update()
        right_switch.update()
        if left_switch.fell:
            if mixer.voice[0].playing:
                mixer.voice[0].stop()
                while mixer.voice[0].playing:
                    pass
            else:
                wave0 = audiocore.WaveFile(open("/sd/feller_menu/option_" + feller_sound_options[self.menuIndex] + ".wav" , "rb"))
                mixer.voice[0].play( wave0, loop=False )
                self.selectedMenuIndex = self.menuIndex
                self.menuIndex +=1
                if self.menuIndex > len(feller_sound_options)-1:
                    self.menuIndex = 0
                while mixer.voice[0].playing:
                    shortCircuitDialog()
        if right_switch.fell:
            if mixer.voice[0].playing:
                mixer.voice[0].stop()
                while mixer.voice[0].playing:
                    pass
            config["option_selected"] = feller_sound_options[self.selectedMenuIndex]
            print ("Selected index: " + str(self.selectedMenuIndex) + " Saved option: " + config["option_selected"])
            files.write_json_file("/sd/config_feller.json",config)
            wave0 = audiocore.WaveFile(open("/sd/feller_menu/option_selected.wav", "rb"))
            mixer.voice[0].play( wave0, loop=False )
            while mixer.voice[0].playing:
                pass
            machine.go_to_state('base_state')

class MainMenu(State):

    def __init__(self):
        self.menuIndex = 0
        self.selectedMenuIndex = 0

    @property
    def name(self):
        return 'main_menu'

    def enter(self, machine):
        print('Select main menu')
        mainMenuAnnouncement()
        State.enter(self, machine)

    def exit(self, machine):
        State.exit(self, machine)

    def update(self, machine):
        left_switch.update()
        right_switch.update()
        if left_switch.fell:
            if mixer.voice[0].playing:
                mixer.voice[0].stop()
                while mixer.voice[0].playing:
                    pass
            else:
                wave0 = audiocore.WaveFile(open("/sd/feller_menu/" + main_menu[self.menuIndex] + ".wav" , "rb"))
                mixer.voice[0].play( wave0, loop=False )
                self.selectedMenuIndex = self.menuIndex
                self.menuIndex +=1
                if self.menuIndex > len(main_menu)-1:
                    self.menuIndex = 0
                while mixer.voice[0].playing:
                    shortCircuitDialog()
        if right_switch.fell:
            if mixer.voice[0].playing:
                mixer.voice[0].stop()
                while mixer.voice[0].playing:
                    pass
            else:
                selected_menu_item = main_menu[self.selectedMenuIndex]
                if selected_menu_item == "choose_sounds":
                    machine.go_to_state('choose_sounds')
                elif selected_menu_item == "adjust_feller_and_tree":
                    machine.go_to_state('adjust_feller_and_tree')
                elif selected_menu_item == "move_feller_and_tree":
                    machine.go_to_state('move_feller_and_tree')
                else:
                    wave0 = audiocore.WaveFile(open("/sd/feller_menu/all_changes_complete.wav", "rb"))
                    mixer.voice[0].play( wave0, loop=False )
                    while mixer.voice[0].playing:
                        pass
                    machine.go_to_state('base_state')
                
# StateTemplate copy and add functionality
class StateTemplate(State):

    def __init__(self):
        super().__init__()

    @property
    def name(self):
        return 'example'

    def enter(self, machine):
        State.enter(self, machine)

    def exit(self, machine):
        State.exit(self, machine)

    def update(self, machine):
        State.update(self, machine)
        
garbage_collect("state machine")

###############################################################################
# Create the state machine

pretty_state_machine = StateMachine()
pretty_state_machine.add_state(BaseState())
pretty_state_machine.add_state(MainMenu())
pretty_state_machine.add_state(ChooseSounds())
pretty_state_machine.add_state(AdjustFellerAndTree())
pretty_state_machine.add_state(MoveFellerAndTree())

pretty_state_machine.go_to_state('base_state')

print("starting server...")
# startup the server
try:
    server.start(str(wifi.radio.ipv4_address))
    print("Listening on http://%s:80" % wifi.radio.ipv4_address)
# if the server fails to begin, restart the pico w
except OSError:
    time.sleep(5)
    print("restarting...")
    reset_pico()
    
print("animator has started...")

while True:
    pretty_state_machine.update()
    sleepAndUpdateVolume(.1)
    try:
        server.poll()
    except Exception as e:
        print(e)
        continue
Here is the index.html

Code: Select all

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-type" content="text/html;charset=utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
        html {
            font-family: monospace;
            background-color: lightgrey;
            display: inline-block;
            margin: 0px auto;
            text-align: center;
        }

        h1 {
            color: deeppink;
            width: 200;
            word-wrap: break-word;
            padding: 2vh;
            font-size: 35px;
        }

        p {
            font-size: 1.5rem;
            width: 200;
            word-wrap: break-word;
        }

        .button {
            font-family: monospace;
            display: inline-block;
            background-color: black;
            border: none;
            border-radius: 4px;
            color: white;
            padding: 16px 40px;
            text-decoration: none;
            font-size: 30px;
            margin: 2px;
            cursor: pointer;
        }

        p.dotted {
            margin: auto;
            width: 75%;
            font-size: 25px;
            text-align: center;
        }
    </style>
</head>

<body>
    <title>Animator - Feller</title>
    <h1>Animator - Feller</h1>
    <br>
    <p class="dotted">Use this web app to control and calibrate your animator - feller product.  Have fun animating.</p>
    <br>
        
    <h1>Feller Animations:</h1><br>
    
    <form accept-charset="utf-8" method="POST">
        <button class="button" name="RUN ANIMATION" value="random" type="submit">Random</button>
        <button class="button" name="RUN ANIMATION FORTH" value="forth_of_july" type="submit">Forth of July</button>
        <button class="button" name="RUN ANIMATION CHRISTMAS" value="christmas" type="submit">Christmas</button>
        <button class="button" name="RUN ANIMATION HALLOWEEN" value="halloween" type="submit">Halloween</button>
        <button class="button" name="RUN ANIMATION BIRD DOG" value="birds_dogs" type="submit">Birds and Dogs</button>
        <button class="button" name="RUN ANIMATION BIRD DOG SHORT" value="birds_dogs_short_version" type="submit">Birds and Dogs Short</button>
        <button class="button" name="RUN ANIMATION JUST BIRDS" value="just_birds" type="submit">Just Birds</button>
        <button class="button" name="RUN ANIMATION MACHINE" value="machines" type="submit">Machine</button>
        <button class="button" name="RUN ANIMATION OWLS" value="owl" type="submit">Owls</button>
        <button class="button" name="RUN ANIMATION NO SOUNDS" value="no_sounds" type="submit">No other sounds</button>
    </form>  

    <h1>Feller positions:</h1><br>

    <form accept-charset="utf-8" method="POST">
        <button class="button" name="FELLER REST" value="feller_rest_pos" type="submit">Rest</button>
        <button class="button" name="FELLER CHOP" value="feller_chop_pos" type="submit">Chop</button>
        <button class="button" name="FELLER CALIBRATE" value="feller_calibrate" type="submit">Calibrate</button>
    </form>

    <h1>Tree positions:</h1><br>

    <form accept-charset="utf-8" method="POST">
        <button class="button" name="TREE UP" value="tree_up_pos" type="submit">Upright</button>
        <button class="button" name="TREE DOWN" value="tree_down_pos" type="submit">Fallen</button>
        <button class="button" name="TREE CALIBRATE" value="tree_calibrate" type="submit">Calibrate</button>
    </form>
    
</body>

</html>
here is the animate_feller.py

Code: Select all

import random

def animation_one(
        sleepAndUpdateVolume, 
        audiocore, 
        mixer, 
        feller_servo, 
        tree_servo, 
        config,
        feller_sound_options, 
        feller_dialog_positive,
        feller_dialog_negative,
        feller_dialog_advice,
        moveFellerServo,
        moveTreeServo,
        moveFellerToPositionGently,
        moveTreeToPositionGently,
        left_switch):
    sleepAndUpdateVolume(0.05)
    chopNum = 1
    chopNumber = random.randint(2, 7)
    what_to_speak = random.randint(1, 3)
    when_to_speak = random.randint(2, chopNumber)
    print("chop total: " + str(chopNumber) + " what to speak: " + str(what_to_speak) + " when to speak: " + str(when_to_speak))
    spoken = False
    tree_chop_pos = config["tree_up_pos"] - 3
    speak_rotation = 7
    speak_cadence = 0.2
    while chopNum <= chopNumber:
        if what_to_speak == 1 and when_to_speak == chopNum and not spoken:
            spoken = True
            highest_index = len(feller_dialog_positive) - 1
            soundNumber = random.randint(0, highest_index)
            soundFile = "/sd/feller_dialog/" + feller_dialog_positive[soundNumber] + ".wav"
            wave0 = audiocore.WaveFile(open(soundFile, "rb"))
            mixer.voice[0].play( wave0, loop=False )
            while mixer.voice[0].playing:
                feller_servo.angle = speak_rotation + config["feller_rest_pos"]
                sleepAndUpdateVolume(speak_cadence)
                feller_servo.angle = config["feller_rest_pos"]
                sleepAndUpdateVolume(speak_cadence)
        if what_to_speak == 2 and when_to_speak == chopNum and not spoken:
            spoken = True
            highest_index = len(feller_dialog_negative) - 1
            soundNumber = random.randint(0, highest_index)
            soundFile = "/sd/feller_dialog/" + feller_dialog_negative[soundNumber] + ".wav"
            wave0 = audiocore.WaveFile(open(soundFile, "rb"))
            mixer.voice[0].play( wave0, loop=False )
            while mixer.voice[0].playing:
                feller_servo.angle = speak_rotation + config["feller_rest_pos"]
                sleepAndUpdateVolume(speak_cadence)
                feller_servo.angle = config["feller_rest_pos"]
                sleepAndUpdateVolume(speak_cadence)
        if what_to_speak == 3 and when_to_speak == chopNum and not spoken:
            spoken = True
            highest_index = len(feller_dialog_advice) - 1
            soundNumber = random.randint(0, highest_index)
            soundFile = "/sd/feller_dialog/" + feller_dialog_advice[soundNumber] + ".wav"
            wave0 = audiocore.WaveFile(open(soundFile, "rb"))
            mixer.voice[0].play( wave0, loop=False )
            while mixer.voice[0].playing:
                feller_servo.angle = speak_rotation + config["feller_rest_pos"]
                sleepAndUpdateVolume(speak_cadence)
                feller_servo.angle = config["feller_rest_pos"]
                sleepAndUpdateVolume(speak_cadence)
        wave0 = audiocore.WaveFile(open("/sd/feller_chops/chop" + str(chopNum) + ".wav", "rb"))
        chopNum += 1
        chopActive = True
        for feller_angle in range(config["feller_rest_pos"], config["feller_chop_pos"] + 5, 10):  # 0 - 180 degrees, 10 degrees at a time.
            moveFellerServo(feller_angle)                                
            if feller_angle >= (config["feller_chop_pos"] - 10) and chopActive:
                mixer.voice[0].play( wave0, loop=False )
                chopActive = False
            if feller_angle >= config["feller_chop_pos"]:
                chopActive = True
                shake = 2
                for _ in range(shake):
                    moveTreeServo(tree_chop_pos)
                    sleepAndUpdateVolume(0.1)
                    moveTreeServo(config["tree_up_pos"])
                    sleepAndUpdateVolume(0.1)
            sleepAndUpdateVolume(0.02)
        if chopNum <= chopNumber: 
            for feller_angle in range(config["feller_chop_pos"], config["feller_rest_pos"], -5): # 180 - 0 degrees, 5 degrees at a time.
                moveFellerServo( feller_angle )
                sleepAndUpdateVolume(0.02)
        pass
    sleepAndUpdateVolume(0.02)
    if config["option_selected"] == "random":
        feller_sound_options_highest_index = len(feller_sound_options) - 2 #subtract -2 to avoid choosing "random" for a file
        soundNumber = random.randint(0, feller_sound_options_highest_index)
        soundFile = "/sd/feller_sounds/sounds_" + feller_sound_options[soundNumber] + ".wav"
    else:
        soundFile = "/sd/feller_sounds/sounds_" + config["option_selected"] + ".wav"
    wave0 = audiocore.WaveFile(open(soundFile, "rb"))
    mixer.voice[0].play( wave0, loop=False )
    for tree_angle in range(config["tree_up_pos"], config["tree_down_pos"], -5): # 180 - 0 degrees, 5 degrees at a time.
        moveTreeServo(tree_angle)
        sleepAndUpdateVolume(0.06)
    shake = 8
    for _ in range(shake):
        moveTreeServo(config["tree_down_pos"])
        sleepAndUpdateVolume(0.1)
        moveTreeServo(7 + config["tree_down_pos"])
        sleepAndUpdateVolume(0.1)
    moveTreeServo(config["tree_down_pos"])
    #soundFile2 = "/sd/feller_dialog/i_am_exhausted.wav"
    #wave1 = audiocore.WaveFile(open(soundFile2, "rb"))
    #mixer.voice[1].play( wave1, loop=True )
    while mixer.voice[0].playing:
        sleepAndUpdateVolume(0.02)
        left_switch.update()
        if left_switch.fell:
            mixer.voice[0].stop()
    moveFellerToPositionGently(config["feller_rest_pos"])
    sleepAndUpdateVolume(0.02)
    moveTreeToPositionGently(config["tree_up_pos"])
here is the files.py

Code: Select all

import os
import json

def print_directory(switch_0, path, tabs=0):
    print("Files on filesystem:")
    print("====================")
    dude = switch_0.fell
    print(dude)
    for file in os.listdir(path):
        stats = os.stat(path + "/" + file)
        filesize = stats[6]
        isdir = stats[0] & 0x4000

        if filesize < 1000:
            sizestr = str(filesize) + " by"
        elif filesize < 1000000:
            sizestr = "%0.1f KB" % (filesize / 1000)
        else:
            sizestr = "%0.1f MB" % (filesize / 1000000)

        prettyprintname = ""
        for _ in range(tabs):
            prettyprintname += "   "
        prettyprintname += file
        if isdir:
            prettyprintname += "/"
        print('{0:<40} Size: {1:>10}'.format(prettyprintname, sizestr))

        # recursively print directory contents
        if isdir:
            print_directory(path + "/" + file, tabs + 1)
            
def write_file_lines(file_name, lines):
    with open(file_name, "w") as f:
        for line in lines:
            f.write(line + "\n")

def read_file_lines(file_name):
    with open(file_name, "r") as f:
        lines = f.readlines()
        output_lines = [] 
        for line in lines:
            output_lines.append(line.strip())
        return output_lines
    
def write_file_line(file_name, line):
    with open(file_name, "w") as f:
        f.write(line + "\n")

def read_file_line(file_name):
    with open(file_name, "r") as f:
        line = f.read()
        output_line=line.strip()
        return output_line
    
def json_stringify(python_dictionary):
    json_string = json.dumps(python_dictionary)
    return json_string

def json_parse(my_object):
    python_dictionary = json.loads(my_object)
    return python_dictionary

def write_json_file(file_name, python_dictionary):
    json_string=json_stringify(python_dictionary)
    write_file_line(file_name, json_string)
    
def read_json_file(file_name):
    json_string=read_file_line(file_name)
    python_dictionary=json_parse(json_string)
    return python_dictionary

User avatar
jbailey969
 
Posts: 32
Joined: Sat May 13, 2023 2:06 pm

Re: Multiple Http requests before response locks up server

Post by jbailey969 »

I updated to the latest Circuit Python 8.10 but the problem still persists. If you send a post request before the other one has returned the code get lost in some state. If you stop the process you get some messaging like below. I don't know if that gives any insight onto whats is going on but thought it might be interesting.

Traceback (most recent call last):
File "<stdin>", line 875, in <module>
File "adafruit_httpserver/server.py", line 167, in poll
File "adafruit_httpserver/route.py", line 126, in wrapped_handler
File "<stdin>", line 496, in buttonpress
File "adafruit_httpserver/response.py", line 196, in send_file
File "adafruit_httpserver/response.py", line 141, in _send_headers
File "adafruit_httpserver/response.py", line 244, in _send_bytes
KeyboardInterrupt:

User avatar
danhalbert
 
Posts: 4651
Joined: Tue Aug 08, 2017 12:37 pm

Re: Multiple Http requests before response locks up server

Post by danhalbert »

I have opened a library issue about this: https://github.com/adafruit/Adafruit_Ci ... /issues/55

User avatar
danhalbert
 
Posts: 4651
Joined: Tue Aug 08, 2017 12:37 pm

Re: Multiple Http requests before response locks up server

Post by danhalbert »

Could you check out the latest version and see if this has been fixed for you? See https://github.com/adafruit/Adafruit_Ci ... er/pull/45

User avatar
jbailey969
 
Posts: 32
Joined: Sat May 13, 2023 2:06 pm

Re: Multiple Http requests before response locks up server

Post by jbailey969 »

Sure I can check it out and try it.

How dow I compile it into a library. I assume these instructions are the way to go? I have not done this before so total noob with this...

https://learn.adafruit.com/building-cir ... cuitpython

User avatar
jbailey969
 
Posts: 32
Joined: Sat May 13, 2023 2:06 pm

Re: Multiple Http requests before response locks up server

Post by jbailey969 »

After some searching I realized the libraries are built everyday. So I did not need to compile them.

So I used the library from https://circuitpython.org/libraries adafruit-circuitpython-bundle-8.x-mpy-20230527

Found the "adafruit_httpserver" folder and put it in my lib folder. I get the following error that it can't find them.

Traceback (most recent call last):
File "<stdin>", line 33, in <module>
ImportError: cannot import name HTTPServer

my imports are as follows I also commented out HTTPServer and same errors occurs for HTTPRequest...

from adafruit_httpserver.server import HTTPServer
from adafruit_httpserver.request import HTTPRequest
from adafruit_httpserver.response import HTTPResponse
from adafruit_httpserver.methods import HTTPMethod
from adafruit_httpserver.mime_type import MIMEType

User avatar
danhalbert
 
Posts: 4651
Joined: Tue Aug 08, 2017 12:37 pm

Re: Multiple Http requests before response locks up server

Post by danhalbert »

Version 4.0.0 of the library reorganized the API. See the documentation for the new API here: https://docs.circuitpython.org/projects ... en/latest/, and also see the examples: https://github.com/adafruit/Adafruit_Ci ... 1/examples

User avatar
jbailey969
 
Posts: 32
Joined: Sat May 13, 2023 2:06 pm

Re: Multiple Http requests before response locks up server

Post by jbailey969 »

Everything is working now. I can hit a button more than once.

User avatar
danhalbert
 
Posts: 4651
Joined: Tue Aug 08, 2017 12:37 pm

Re: Multiple Http requests before response locks up server

Post by danhalbert »

Thanks for testing! I will close the issue.

Locked
Please be positive and constructive with your questions and comments.

Return to “Adafruit CircuitPython”