Piader meets ESP and events

Some time ago we made a WiFi keyboard on ESP 8266. Long time ago we made a Piader game. From their combined powers comes Piader controlled by WiFi keyboard 😀

We need to change Piader a little, to be precise, event listener and keyboard server needs to use JSON packet not string. Why ? Because wifi keyboard is using it.

Piader will also move from TCP to UDP and this allows us to use broadcast.

What about keyboard changes ? Yey! No changes!

Piader source code

Local keyboard control

To control a player Piader uses two things.  First is a keyboard reader, it reads key and send it via TCP to game.
Second is an event server, it reads string from connection and add it to game queue.

As we want to control game via other method we need to switch to UDP protocol. Lets start with keyboard reader. Its located in local_key.py file.

We have two variables: TCP_IP and TCP_PORT, they wont be needed any more so remove them. In their place add ADDRESS = (‘<broadcast>’, 5053)

We are changing port from 5005 to 5053 because our wifi keyboard is using it and it is much easier to change it here 🙂

Next find socket.SOCK_STREAM and change it to socket.SOCK_DGRAM. Remove connect(..) – it is not needed in UDP.
In place of removed connect(..) put self.conn_socket.setsockopt(socket.SOL_SOCKET,  socket.SO_BROADCAST, 1). This will tell socket to listen to broadcast messages.

Last line to replace is send(..). Change it to self.conn_socket.sendto(event.encode(‘UTF-8’), ADDRESS).

Event server

This one require huge refactor. Dropping TCP usage removes whole ListenerThread class. Now we will listen, receive and add to queue.

Start with changing variables TCP_IP nad TCP_PORT to single ADDRESS = (‘<broadcast>’, 5053)

Next remove whole ListenerThread class. Modify __init__ in EventServerThread to use UDP socket.  Change the way we read and parse data, now it will read directly and put event into queue. Finally remove thread.join() loop.

The result we are left is:


#!/usr/bin/python
# -*- coding: utf-8 -*-
#pylint: disable=I0011,W0231

"""Game event catcher"""
from __future__ import print_function
from future import standard_library
standard_library.install_aliases()
import socket
from threading import Thread

BUFFER_SIZE = 1024
ADDRESS = ('', 5053)

class EventServerThread(Thread):
    """server thread"""
    def __init__(self, queue):
        Thread.__init__(self)
        self.work = True
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        self.socket.bind(ADDRESS)
        self.queue = queue

    def run(self):
        """start server thread"""
        try:
            print ("Server is listening for connections...")
            while self.work:
                try:
                    message, address = self.socket.recvfrom(BUFFER_SIZE)
                    if not message:
                        break
                    else:
                        self.queue.put(message.decode("UTF-8"))
                except socket.timeout:
                    pass
        finally:
            self.socket.close()
        print ("Server down")

    def join(self, timeout=None):
        """stop server and all listeners"""
        self.work = False
        Thread.join(self, timeout)

Encapsulate events into JSON package.

Last task for us to do is to switch from using a string to using JSON. WiFi keybard format is something like this:

{
    "chip_id": 13587330,
    "event": "event.left",
    "node": "node-1"
}

To achieve this, we need to change send function in local_key.py to encode event is JSON:

    def send(self, event):
        """send event to game"""
        packet = {
            "node": "local-keyboard",
            "event": event
        }
        self.conn_socket.sendto(json.dumps(packet).encode('UTF-8'), ADDRESS)

Do not forget about import json at the top of the file:)
And another matter appears, we are using different event names in Piader than in ESP keyboard. We must unite it. Rewrite actions dictionary in Pidaer to:

       self.actions = {
            'a': 'event.left',
            'd': 'event.right',
            'w': 'event.up',
            's': 'event.down',
            ' ': 'event.action1',
            'q': 'event.action2'
        }

Ok we can send as we want but receiver want understand it. Lets change it.
Back to event_server.py we need to add decode function:

    def __decode(self, message):
        """decode network packet to json and extract event name"""
        message = message.decode("UTF-8")
        try:
            data = json.loads(message)
        except ValueError:
            data = None

        if type(data) is dict and 'event' in data:
            return data['event']

        return None

And use it in try .. except. New body:

            while self.work:
                try:
                    message, address = self.socket.recvfrom(BUFFER_SIZE)
                    message = self.__decode(message)
                    if message:                        
                        self.queue.put(message)
                except socket.timeout:
                    pass

Finally we need to change event’s names in game.py, home.py and options.py views:
– move.up to event.up,
– move.down to event.down,
– move.left to event.left,
– move.right to event.right,
– action to event.action1.

Summary

We may now play Piader via remote WiFi keyboard. Or anything else:) Just write simple broadcaster and you are ready!

Piader source code

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s