Python tornado stop function
Question: I've got a tornado web server running on my Raspberry and a ultrasonic sensor
connected to it. I've got a html page with a start and stop button, when I
click start the script is sending a message "start" to the serwer and it runs
a function that prints the distance.

Now i'm trying to stop the function when clicking stop button. But when te
function is printing the distances and I click the stop button on my website
the server doesnt recive the "stop" message.

![enter image description here](http://i.stack.imgur.com/AFuZZ.png)

The code is here: <http://pastebin.com/SVRZNXgH>

    
    
    import tornado.httpserver
    import tornado.ioloop
    import tornado.options
    import tornado.web
    import tornado.websocket
    import time
    import RPi.GPIO as GPIO
    
    GPIO.setmode(GPIO.BOARD)
    
    # Ta funkcja dokonuje pomiaru odleglosci
    def pomiar():
      # Ustawienie pinow czujnika odleglosci
      GPIO_TRIGGER = 16
      GPIO_ECHO    = 18
    
      # Ustawienie pinu buzzera
      GPIO_BUZZER = 22
    
      # Ustawienie pinu serwomechanizmu
      GPIO_SERVO = 13
    
      # Ustawienie pinow
      GPIO.setup(GPIO_TRIGGER,GPIO.OUT)
      GPIO.setup(GPIO_ECHO,GPIO.IN)
      GPIO.setup(GPIO_SERVO,GPIO.OUT)
    
      # Ustawienie Trigget jako false (stan niski)
      GPIO.output(GPIO_TRIGGER, False)
    
      GPIO.setup(GPIO_TRIGGER,GPIO.OUT)
      GPIO.output(GPIO_TRIGGER, True)
    
      time.sleep(0.00001)
      GPIO.output(GPIO_TRIGGER, False)
      start = time.time()
    
      while GPIO.input(GPIO_ECHO)==0:
        start = time.time()
    
      while GPIO.input(GPIO_ECHO)==1:
        stop = time.time()
    
      elapsed = stop-start
      distance = (elapsed * 34300)/2
      print "odleglosc :  %.1f" % distance
      time.sleep(2)
    
      return distance
    
    # Funkcja dokonujaca sprawdzenia kata
    def sprawdz_kat():
      GPIO_SERVO = 13
      GPIO.setup(GPIO_SERVO,GPIO.OUT)
      p = GPIO.PWM(GPIO_SERVO,50)
    
      katy = [10,12.5,5,2.5];
      start = 20.0;
      for x in katy:
          p.ChangeDutyCycle(x)
          time.sleep(0.5)
          odleglosc = pomiar()
          print "odleglosc :  %.1f" % odleglosc
          if(odleglosc>start):
              start=odleglosc
              kat = x
      if(kat==10):        
          return float(-45)
      if(kat==12.5):
          return float(-90)
      if(kat==5):
          return float(45)
      if(kat==2.5):
          return float(90)
    
    # Funkcja do obrotu robota o zadany kat   
    def obrot(kat):
      obrot=abs(kat/100)
      print(obrot)
      if(kat<0):
          GPIO.output(M1_EN, True) # True - do przodu, False - do tylu
          GPIO.output(M2_EN, True) # False - do przodu, True - do tylu
      else:
          GPIO.output(M1_EN, False) # True - do przodu, False - do tylu
          GPIO.output(M2_EN, False) # False - do przodu, True - do tylu
      LEWA_PWM.ChangeDutyCycle(25)
      PRAWA_PWM.ChangeDutyCycle(25)
      time.sleep(obrot)
      LEWA_PWM.ChangeDutyCycle(0)
      PRAWA_PWM.ChangeDutyCycle(0)
      GPIO.output(M1_EN, True)
      GPIO.output(M2_EN, False)   
    
    # Program labiryntu
    def labirynt():
      distance = pomiar()
      p.start(7.5)
      while True:
        distance = pomiar()
        GPIO.setup(GPIO_BUZZER,GPIO.OUT)
        GPIO.output(GPIO_BUZZER, False)
        time.sleep(0.08)
        GPIO.setup(GPIO_BUZZER,GPIO.IN)
        time.sleep(distance/500)
        LEWA_PWM.ChangeDutyCycle(10)
        PRAWA_PWM.ChangeDutyCycle(10)
        if(distance<50):
          LEWA_PWM.ChangeDutyCycle(0)
          PRAWA_PWM.ChangeDutyCycle(0)
          kat = sprawdz_kat()
          print kat
          obrot(kat)
          p.ChangeDutyCycle(7.5)
          time.sleep(0.5)
        GPIO.setup(GPIO_SERVO,GPIO.IN)
      GPIO.cleanup  
    
    def stop_all():
      GPIO.output(GPIO_TRIGGER, False)
      GPIO.output(GPIO_ECHO, False)
      GPIO.cleanup()
    
    # Ustawienie pinow czujnika odleglosci
    GPIO_TRIGGER = 16
    GPIO_ECHO    = 18
    
    # Ustawienie pinu buzzera
    GPIO_BUZZER = 22
    
    # Ustawienie pinu serwomechanizmu
    GPIO_SERVO = 13
    
    # Ustawienie pinow
    GPIO.setup(GPIO_TRIGGER,GPIO.OUT)
    GPIO.setup(GPIO_ECHO,GPIO.IN)
    GPIO.setup(GPIO_SERVO,GPIO.OUT)
    
    # Ustawienie Trigget jako false (stan niski)
    GPIO.output(GPIO_TRIGGER, False)
    
    # Ustawienia dla serwa
    p = GPIO.PWM(GPIO_SERVO,50) #Ustawienie czestotliwosci na 50Hz
     #poczatkowy kat serwa 90 stopni (neutralny)
    
    # Ustawienia buzzera
    GPIO.setup(GPIO_BUZZER,GPIO.IN)  
    
    # -----------------------
    # Ustawienia silnikow
    # -----------------------
    
    # Prawa strona
    M1_PWM = 26
    M1_EN = 24
    
    # Lewa strona
    M2_PWM = 23
    M2_EN = 21
    
    # Prawa strona ustawienia jako output
    GPIO.setup(M1_PWM,GPIO.OUT)
    GPIO.setup(M1_EN,GPIO.OUT)
    
    # Lewa strona ustawienia jako output
    GPIO.setup(M2_PWM,GPIO.OUT)
    GPIO.setup(M2_EN,GPIO.OUT)
    
    # Prawa strona ustawienie stanow niskich
    GPIO.output(M1_PWM, False)
    GPIO.output(M1_EN, True) # True - do przodu, False - do tylu
    
    # Lewa strona ustawienie stanow niskich
    GPIO.output(M2_PWM, False)
    GPIO.output(M2_EN, False) # False - do przodu, True - do tylu
    
    # Ustawienie PWM dla prawej strony
    PRAWA_PWM = GPIO.PWM(26,50)
    
    # Ustawienie PWM dla lewej strony
    LEWA_PWM = GPIO.PWM(23,50)
    
    # Poczatkowe ustawienia PWM silnikow
    PRAWA_PWM.start(0)
    LEWA_PWM.start(0)
    
    
    
    from tornado.options import define, options
    define("port", default=8080, help="run on the given port", type=int)
    
    class IndexHandler(tornado.web.RequestHandler):
        def get(self):
            self.render('index.html')
    
    class WebSocketHandler(tornado.websocket.WebSocketHandler):
        def open(self):
            print 'new connection'
            self.write_message("connected")
    
        def on_message(self, message):
            print 'message received %s' % message
            self.write_message('message received %s' % message)
            aaa = message
            if message == "gora":
              GPIO.output(M1_EN, True)
              GPIO.output(M2_EN, False)
              LEWA_PWM.ChangeDutyCycle(8)
              PRAWA_PWM.ChangeDutyCycle(8)
            if message == "dol":
              GPIO.output(M1_EN, False)
              GPIO.output(M2_EN, True)
              LEWA_PWM.ChangeDutyCycle(8)
              PRAWA_PWM.ChangeDutyCycle(8)
            if message == "stop":
              LEWA_PWM.ChangeDutyCycle(0)
              PRAWA_PWM.ChangeDutyCycle(0)
            if message == "lewo":
              GPIO.output(M1_EN, True)
              GPIO.output(M2_EN, True)
              LEWA_PWM.ChangeDutyCycle(20)
              PRAWA_PWM.ChangeDutyCycle(20)
            if message == "prawo":
              GPIO.output(M1_EN, False)
              GPIO.output(M2_EN, False)
              LEWA_PWM.ChangeDutyCycle(20)
              PRAWA_PWM.ChangeDutyCycle(20)
            while (aaa == "start_maze"):
              pomiar()
        def on_close(self):
            print 'connection closed'
    
    if __name__ == "__main__":
        tornado.options.parse_command_line()
        app = tornado.web.Application(
            handlers=[
                (r"/", IndexHandler),
                (r"/ws", WebSocketHandler),
                (r'/js/(.*)', tornado.web.StaticFileHandler, {'path': "/home/pi/js"}),
                (r'/css/(.*)', tornado.web.StaticFileHandler, {'path': "/home/pi/css"}),
                (r'/fonts/(.*)', tornado.web.StaticFileHandler, {'path': "/home/pi/fonts"}),
            ]
        )
        httpServer = tornado.httpserver.HTTPServer(app)
        httpServer.listen(options.port)
        print "Listening on port:", options.port
        tornado.ioloop.IOLoop.instance().start()
    


Answer: The reason is that you got an endless loop in line 227.

One possible solution:

  1. Make your `pomiar` function a method of the websocket handler
  2. Use a variable on your websocket handler `self.running` and on start set it to true. And call `self.pomiar()`
  3. On stop just set `self.running` to `false`
  4. At the end of `pomiar` remove the `time.sleep` it does block the complete tornado server. Instead:

...

    
    
    if self.running:
        tornado.ioloop.IOLoop.instance().add_timeout(time.time() + 2, self.pomiar)
    

The add_timeout makes tornado call your pomiar function again in two seconds.
Checking first if `self.running` is still `true` makes sure your `pomiar`
function is only called as long as stop is not called.

There are a lot of other ways to solve this. You might want to read more about
tornado and the asynchronous functions it provides, for example:
<http://tornado.readthedocs.org/en/latest/gen.html>

