{"id":5092,"date":"2021-10-24T18:59:49","date_gmt":"2021-10-24T22:59:49","guid":{"rendered":"https:\/\/courses.ideate.cmu.edu\/16-223\/f2021\/work\/?p=5092"},"modified":"2021-10-24T19:03:11","modified_gmt":"2021-10-24T23:03:11","slug":"rotary-marble-jam","status":"publish","type":"post","link":"https:\/\/courses.ideate.cmu.edu\/16-223\/f2021\/work\/2021\/10\/24\/rotary-marble-jam\/","title":{"rendered":"Rotary Marble Jam"},"content":{"rendered":"\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"Rotary Marble Jam\" width=\"620\" height=\"349\" src=\"https:\/\/www.youtube.com\/embed\/tBRcT62wock?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<p>Karen Abruzzo and Jason Perez<\/p>\n\n\n\n<p><strong>Statement<\/strong><\/p>\n\n\n\n<p>Our initial intent was to create music by having marbles drop on differently shaped wooden pieces. The user would have control over the rate at which the marbles would drop through a spinning gate. The final outcome was realized through a spinning gate controlled by the user with a set of instructions. At the console, the user can define a sequence of six values that determine the speed and direction of the gate. These controls allow the user to create a rhythmic pattern with the motor and sound effects with the falling marbles. <\/p>\n\n\n\n<p><strong>Files<\/strong><\/p>\n\n\n\n<p><a href=\"https:\/\/drive.google.com\/file\/d\/1uyl0Dj6D769WWmYuC_1VhlOsYWAuSpW-\/view?usp=sharing\">https:\/\/drive.google.com\/file\/d\/1uyl0Dj6D769WWmYuC_1VhlOsYWAuSpW-\/view?usp=sharing<\/a><\/p>\n\n\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\n# musical_rotator.py\n#\n# This assumes a Pololu DRV8833 dual motor driver has been wired up to the Pico as follows:\n#   Pico pin 24, GPIO18   -&amp;gt; AIN1\n#   Pico pin 25, GPIO19   -&amp;gt; AIN2\n#   any Pico GND          -&amp;gt; GND\n\n# DRV8833 carrier board: https:\/\/www.pololu.com\/product\/2130\n\n################################################################\n# CircuitPython module documentation:\n# time    https:\/\/circuitpython.readthedocs.io\/en\/latest\/shared-bindings\/time\/index.html\n# math    https:\/\/circuitpython.readthedocs.io\/en\/latest\/shared-bindings\/math\/index.html\n# board   https:\/\/circuitpython.readthedocs.io\/en\/latest\/shared-bindings\/board\/index.html\n# pwmio   https:\/\/circuitpython.readthedocs.io\/en\/latest\/shared-bindings\/pwmio\/index.html\n\n################################################################################\n\n# load standard Python modules\nimport math, time\n\n# load the CircuitPython hardware definition module for pin definitions\nimport board\n\n# load the CircuitPython pulse-width-modulation module for driving hardware\nimport pwmio\n\n#--------------------------------------------------------------------------------\n# Class to represent a single dual H-bridge driver.\n\nclass DRV8833():\n    def __init__(self, AIN1=board.GP18, AIN2=board.GP19, BIN2=board.GP20, BIN1=board.GP21, pwm_rate=20000):\n        # Create a pair of PWMOut objects for each motor channel.\n        self.ain1 = pwmio.PWMOut(AIN1, duty_cycle=0, frequency=pwm_rate)\n        self.ain2 = pwmio.PWMOut(AIN2, duty_cycle=0, frequency=pwm_rate)\n\n        self.bin1 = pwmio.PWMOut(BIN1, duty_cycle=0, frequency=pwm_rate)\n        self.bin2 = pwmio.PWMOut(BIN2, duty_cycle=0, frequency=pwm_rate)\n\n    def write(self, channel, rate):\n        &quot;&quot;&quot;Set the speed and direction on a single motor channel.\n\n        :param channel:  0 for motor A, 1 for motor B\n        :param rate: modulation value between -1.0 and 1.0, full reverse to full forward.&quot;&quot;&quot;\n\n        # convert the rate into a 16-bit fixed point integer\n        pwm = min(max(int(2**16 * abs(rate)), 0), 65535)\n\n        if channel == 0:\n            if rate &lt; 0:\n                self.ain1.duty_cycle = pwm\n                self.ain2.duty_cycle = 0\n            else:\n                self.ain1.duty_cycle = 0\n                self.ain2.duty_cycle = pwm\n        else:\n            if rate &lt; 0:\n                self.bin1.duty_cycle = pwm\n                self.bin2.duty_cycle = 0\n            else:\n                self.bin1.duty_cycle = 0\n                self.bin2.duty_cycle = pwm\n\n\n#--------------------------------------------------------------------------------\n# Create an object to represent a dual motor driver.\nprint(&quot;Creating driver object.&quot;)\ndriver = DRV8833()\n\n#--------------------------------------------------------------------------------\n# Begin the main processing loop.  This is structured as a looping script, since\n# each movement primitive 'blocks', i.e. doesn't return until the action is\n# finished.\n#---------------------------------------------------------------\nclass Logic:\n    def __init__(self):\n        &quot;&quot;&quot;Application state machine.&quot;&quot;&quot;\n        self.update_interval = 100000000           # period of 2 Hz in nanoseconds\n        self.update_timer    = 0\n\n        # initialize the state machine\n        self.state = 'init'\n        self.state_changed = False\n        self.state_timer = 0\n        self.intList  = []\n\n    def poll(self, elapsed):\n        &quot;&quot;&quot;Polling function to be called as frequently as possible from the event loop\n        with the nanoseconds elapsed since the last cycle.&quot;&quot;&quot;\n        self.update_timer -= elapsed\n        if self.update_timer &lt; 0:\n            self.update_timer += self.update_interval\n            self.tick()            # evaluate the state machine\n\n    def transition(self, new_state):\n        &quot;&quot;&quot;Set the state machine to enter a new state on the next tick.&quot;&quot;&quot;\n        self.state         = new_state\n        self.state_changed = True\n        self.state_timer   = 0\n        print(&quot;Entering state:&quot;, new_state)\n\n    def tick(self):\n        &quot;&quot;&quot;Evaluate the state machine rules.&quot;&quot;&quot;\n\n        # advance elapsed time in seconds\n        self.state_timer += 1e-9 * self.update_interval\n        #print(self.state_timer)\n\n\n        # set up a flag to process transitions within a state clause\n        entering_state = self.state_changed\n        self.state_changed = False\n\n        # select the state clause to evaluate\n        if self.state == 'init':\n            self.transition('idle')\n\n        elif self.state == 'idle':\n            if entering_state:\n                self.intList = []\n                driver.write(0, .7)\n                print(&quot;Please enter 6 numbers 0-5, negative numbers reverse motor&quot;)\n                print(&quot;Example: 0 1 2 -3 -4 -5&quot;)\n                userInput = input(&quot;Enter: &quot;)\n                listTemp = list(userInput.split())\n                self.intList = list(map(int,listTemp))\n                #print(self.intList)\n                self.transition('userState')\n\n        elif self.state == 'userState':\n            if self.intList[0] &amp;gt; 0:\n                driver.write(0, .55+.05*self.intList[0])\n            else:\n                driver.write(0, -.55+.05*self.intList[0])\n            time.sleep(2.0)\n            if self.intList[1] &amp;gt; 0:\n                driver.write(0, .55+.05*self.intList[1])\n            else:\n                driver.write(0, -.55+.05*self.intList[1])\n            time.sleep(2.0)\n            if self.intList[2] &amp;gt; 0:\n                driver.write(0, .55+.05*self.intList[2])\n            else:\n                driver.write(0, -.55+.05*self.intList[2])\n            time.sleep(2.0)\n            if self.intList[3] &amp;gt; 0:\n                driver.write(0, .55+.05*self.intList[3])\n            else:\n                driver.write(0, -.55+.05*self.intList[3])\n            time.sleep(2.0)\n            if self.intList[4] &amp;gt; 0:\n                driver.write(0, .55+.05*self.intList[4])\n            else:\n                driver.write(0, -.55+.05*self.intList[4])\n            time.sleep(2.0)\n            if self.intList[5] &amp;gt; 0:\n                driver.write(0, .55+.05*self.intList[5])\n            else:\n                driver.write(0, -.55+.05*self.intList[5])\n            time.sleep(2.0)\n            self.transition('idle')\n\n#---------------------------------------------------------------\nprint(&quot;Starting main script.&quot;)\nlast_clock = time.monotonic_ns()\nlogic = Logic()\nwhile True:\n    now = time.monotonic_ns()\n    elapsed = now - last_clock\n    last_clock = now\n    logic.poll(elapsed)\n<\/pre>","protected":false},"excerpt":{"rendered":"<p>Karen Abruzzo and Jason Perez Statement Our initial intent was to create music by having marbles drop on differently shaped wooden pieces. The user would have control over&#8230;<\/p>\n","protected":false},"author":36,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/courses.ideate.cmu.edu\/16-223\/f2021\/work\/wp-json\/wp\/v2\/posts\/5092"}],"collection":[{"href":"https:\/\/courses.ideate.cmu.edu\/16-223\/f2021\/work\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/courses.ideate.cmu.edu\/16-223\/f2021\/work\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/16-223\/f2021\/work\/wp-json\/wp\/v2\/users\/36"}],"replies":[{"embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/16-223\/f2021\/work\/wp-json\/wp\/v2\/comments?post=5092"}],"version-history":[{"count":2,"href":"https:\/\/courses.ideate.cmu.edu\/16-223\/f2021\/work\/wp-json\/wp\/v2\/posts\/5092\/revisions"}],"predecessor-version":[{"id":5096,"href":"https:\/\/courses.ideate.cmu.edu\/16-223\/f2021\/work\/wp-json\/wp\/v2\/posts\/5092\/revisions\/5096"}],"wp:attachment":[{"href":"https:\/\/courses.ideate.cmu.edu\/16-223\/f2021\/work\/wp-json\/wp\/v2\/media?parent=5092"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/16-223\/f2021\/work\/wp-json\/wp\/v2\/categories?post=5092"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/courses.ideate.cmu.edu\/16-223\/f2021\/work\/wp-json\/wp\/v2\/tags?post=5092"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}