1

I am attempting to set up a websocket with server running on my raspberry pi. The following code is slightly modified from an example I found here.

I have built a whole webpage around this example allowing me to control gpio and send messages to a serial device plugged into the pi. Both that site and this example work perfectly from my laptop (windows 10 using Chrome or Firefox).

However when I connect from my phone (Android 5.0.1 using Chrome for android). It appears to never open the socket. In the example code it just displays "messages go here.

My first thought was the chrome on android didn't support websockets but I was able to connect and echo messages on this site http://www.websocket.org/echo.html. So it appears the functionality is there.

What else would prevent the socket from opening ?

pysocket.py

import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web

class WSHandler(tornado.websocket.WebSocketHandler):

  def check_origin(self, origin):
    return True

  def open(self):
    print 'New connection was opened'
    self.write_message("Welcome to my websocket!")

  def on_message(self, message):
    print 'Incoming message:', message
    self.write_message("You said: " + message)

  def on_close(self):
    print 'Connection was closed...'

application = tornado.web.Application([
  (r'/ws', WSHandler),
])

if __name__ == "__main__":
  http_server = tornado.httpserver.HTTPServer(application)
  http_server.listen(8888)
  tornado.ioloop.IOLoop.instance().start()

pysocket.php

<!doctype html>
<html>
  <head>
    <title>WebSockets with Python & Tornado</title>
    <meta charset="utf-8" />
    <style type="text/css">
      body {
        text-align: center;
        min-width: 500px;
      }
    </style>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
    <script>
      $(function(){
        var ws;
        var logger = function(msg){
          var now = new Date();
          var sec = now.getSeconds();
          var min = now.getMinutes();
          var hr = now.getHours();
          $("#log").html($("#log").html() + "<br/>" + hr + ":" + min + ":" + sec + " ___ " +  msg);
          //$("#log").animate({ scrollTop: $('#log')[0].scrollHeight}, 100);
          $('#log').scrollTop($('#log')[0].scrollHeight);
        }

        var sender = function() {
          var msg = $("#msg").val();
          if (msg.length > 0)
            ws.send(msg);
          $("#msg").val(msg);
        }

        ws = new WebSocket("ws://raspberrypi-mike:8888/ws");
        ws.onmessage = function(evt) {

          logger(evt.data);
        };
        ws.onclose = function(evt) { 
          $("#log").text("Connection was closed..."); 
          $("#thebutton #msg").prop('disabled', true);
        };
        ws.onopen = function(evt) { $("#log").text("Opening socket..."); };

        $("#msg").keypress(function(event) {
          if (event.which == 13) {
             sender();
           }
        });

        $("#thebutton").click(function(){
          sender();
        });
      });
    </script>
  </head>

  <body>
    <h1>WebSockets with Python & Tornado</h1>
    <div id="log" style="overflow:scroll;width:500px; height:200px;background-color:#ffeeaa; margin:auto; text-align:left">Messages go here</div>

    <div style="margin:10px">
      <input type="text" id="msg" style="background:#fff;width:200px"/>
      <input type="button" id="thebutton" value="Send" />
    </div>

    <a href="http://lowpowerlab.com/blog/2013/01/17/raspberrypi-websockets-with-python-tornado/">www.LowPowerLab.com</a>
  </body>
</html>
  • Did you try using raspberry pi's IP instead of `raspberrypi-mike`? – gre_gor Dec 09 '16 at 13:21
  • I have not. I will try that tonight. I figured since my laptop could connect the hostname was working. Is there any particular reason my phone would need the IP rather then the hostname? – CrimsonKnights Dec 09 '16 at 15:44
  • Replacing the hostname with the ip did fix the problem. Now I am quite curious why my phones browser could not resolve the hostname. – CrimsonKnights Dec 12 '16 at 16:39
  • I have added an answer with and explanation, for what I think is going on. – gre_gor Dec 12 '16 at 17:03

2 Answers2

1

ADDED SOME EXTRA CODE

I would recommend using node.js for that:

var express = require("express");
var app = express();
var http = require("http").Server(app);
var path = require("path");
var io = require('socket.io')(http);
var SerialPort = require('serialport');
var gpio = require('rpio');

http.listen(3000);

var serialPort = new SerialPort.SerialPort("/dev/ttyAMA0", {
    baudrate: 115200,
    dataBits: 8,
    parity: "none",
    stopBits: 1,
    flowControl: false
});

io.on('connection', function(socket){
    console.log('Connected');
    socket.on("WriteSerial:get",function(data){
            var hex = new Buffer (data, "hex"); //be careful passing data
            writeSerial(serialPort, hex);
            io.emit("WriteSerial:response", "Data writen!");
    });
    socket.on("ReadGPIO:get",function(data){
            var input = readPin(data.pin); 
            io.emit("ReadGPIO:response", input); 
    }); 
    socket.on("WriteGPIO:get",function(data){
            writePin(data.pin, data.time); 
            io.emit("WriteGPIO:response", "Set!"); 
    }); 
    socket.on("unWriteGPIO:get",function(data){
            unwritePin(data); 
            io.emit("unWriteGPIO:response", "Set!"); 
    });        
}
app.use(express.static(path.join(__dirname, '/')));
app.get("/home",function(req,res,next){
    res.sendFile(path.join(__dirname + "/index.html"));
});

function writeSerial (port, data)   {
    port.write(data, function(err) {
        if (err) {
            return console.log('Error on write: ', err.message);
        } else {
            console.log('Data written: ' + data);
        }
    });
}

function readPin(pin){
    rpio.open(pin, rpio.INPUT);
    var read = rpio.read(pin) ? 'high' : 'low';
    return read;
}

function writePin(pin, timeInMs){
    rpio.open(pin, rpio.OUTPUT, rpio.LOW);
    rpio.write(pin, rpio.HIGH);
    if (timeInMs > 0) {
        setTimeout(function(){
            rpio.write(pin, rpio.LOW); 
        }, timeInMs);
    } //You can put 0 if You want it to be high until You shut it down 
}

function unWritePin(pin){
    if(readPin(pin) === 'high') {
        rpio.write(pin, rpio.LOW);
    } else {
        console.log("Pin already low!");
    }
}

Be sure You have instaled the node.js with right version. If not do this in terminal:

sudo apt-get remove nodered && sudo apt-get remove nodejs nodejs-legacy && curl -sL https://deb.nodesource.com/setup_4.x | sudo bash - && sudo apt-get install -y nodejs

Make a folder 'server' in '/home/pi/', add server.js to it. Add code I provided to server.js. Open that folder with terminal:

cd /home/pi/server/

Afther that install all modules used on server:

sudo npm install express && sudo npm install http && sudo npm install path && sudo npm install socket.io && sudo npm install serialport --unsafe-perm && sudo npm install rpio --unsafe-perm

Now all we have to do is create client side part. In folder '/home/pi/server' create index.html file and add folder called 'js'. In folder 'js' add socket.io.js for client side which You can find in folder '/home/pi/server/node_modules/socket.io/node_modules/socket.io-client/'.

Include socket.io.js for client side into Your index.html like this:

<script type="text/javascript" src="js/socket.io.js" /></script>

Also add main.js file to 'js' folder where You will put Your javascript code in and include it to index.html:

<script type="text/javascript" src="js/main.js" /></script>
<script type="text/javascript" src="js/jquery.js" /></script>

I will not make any graphics for but some main.js code is here:

$(document).ready(function() {
    var socket = io.connect('http://your_ip_address_rpi:3000');

    $( "#myButton" ).click(function(){
        io.emit("WriteSerial:get", $("#myTextHolder").val()); //"FAAF531C" this is string of hex, should be added some filter to pass error when char is not part of HEX!
    });
    $( "#myButton2" ).click(function(){
        io.emit("WriteGPIO:get", {"pin" : $("#myPinHolder").val(), "time" : $("#myTimeHolder").val()})

}

To run server on RPI startup add 'sudo node /home/pi/server/server.js &' to '/etc/rc.local' before 'exit 0' with 'sudo nano' editor.

It will work really good on any device.

Avoid
  • 392
  • 1
  • 3
  • 16
  • Thanks. I'v never used node.js but I can give it a try. can you elaborate on what it would do differently such that it would work on my phone while my original code does not? – CrimsonKnights Dec 09 '16 at 15:48
  • I have tested it and works on every mobile device I have tried. Node has better performance, has built-in libraries to handle web requests and responses so you don’t need a separate web server or other dependencies. As we are talking about Raspberry Pi node will preserve stability of RPI. PHP is "easy" to use compared to node but I would still recommend node at this state. – Avoid Dec 09 '16 at 19:45
  • Made many node servers myself and I personally prefer node over PHP. – Avoid Dec 09 '16 at 19:51
  • Also some features You can add to node server is playing mp3 file so You can make something like sound station (search for node module play sound), adding picture taking on RPI to make time laps or capturing many mjpeg pictures in 1 sec and post it to client like video. This is if You like to have fun with RPI. – Avoid Dec 09 '16 at 20:18
0

For the hostnames of the devices to work across network, a device has to advertise its own hostname or just responding to DNS queries for its own hostname.

Whatever implementation the Raspberry Pi is using, your laptop is supporting it, but your phone isn't.

So, to be able to connect, you need to change your hostname raspberrypi-mike to your Raspberry Pi's IP address, inside your JavaScript code.

gre_gor
  • 6,669
  • 9
  • 47
  • 52