├── xbee_rssi.png ├── xbee_saved.db ├── pip-env.txt ├── .gitignore ├── env-rpi.txt ├── README.md ├── env-full.txt ├── bokeh_plots.py ├── bokeh_single_plot.py ├── xbee_listen.py └── api.py /xbee_rssi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quasiben/bokeh_examples/HEAD/xbee_rssi.png -------------------------------------------------------------------------------- /xbee_saved.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quasiben/bokeh_examples/HEAD/xbee_saved.db -------------------------------------------------------------------------------- /pip-env.txt: -------------------------------------------------------------------------------- 1 | Flask==0.10.1 2 | Flask-RESTful==0.2.5 3 | Jinja2==2.7.1 4 | MarkupSafe==0.18 5 | Werkzeug==0.9.4 6 | XBee==2.1.0 7 | bokeh==unknown 8 | distribute==0.6.45 9 | gevent==0.13.8 10 | gevent-websocket==0.3.6 11 | greenlet==0.4.1 12 | itsdangerous==0.23 13 | numpy==1.7.1 14 | pandas==0.12.0 15 | pyserial==2.7 16 | python-dateutil==1.5 17 | pytz==2013b 18 | redis==2.7.2 19 | requests==1.2.3 20 | scipy==0.13.0 21 | six==1.4.1 22 | wsgiref==0.1.2 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | .coverage 26 | .tox 27 | nosetests.xml 28 | 29 | # Translations 30 | *.mo 31 | 32 | # Mr Developer 33 | .mr.developer.cfg 34 | .project 35 | .pydevproject 36 | -------------------------------------------------------------------------------- /env-rpi.txt: -------------------------------------------------------------------------------- 1 | # This file may be used to create an environment using: 2 | # $ conda create --name --file 3 | # platform: osx-64 4 | distribute=0.6.45=py27_1 5 | flask=0.10.1=py27_1 6 | flask-restful=2.1.0=py27_0 7 | itsdangerous=0.23=py27_0 8 | jinja2=2.7.1=py27_0 9 | markupsafe=0.18=py27_0 10 | pip=1.4.1=py27_0 11 | pyserial=2.7=py27_0 12 | python=2.7.6=0 13 | readline=6.2=1 14 | six=1.4.1=py27_0 15 | sqlite=3.7.13=1 16 | tk=8.5.13=1 17 | werkzeug=0.9.4=py27_0 18 | xbee=2.1.0=py27_0 19 | zlib=1.2.7=1 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ##RPi 2 | 3 | ## Requriements: 4 | - pyserial 5 | - xbee 6 | - flask 7 | - flask-restful 8 | - bokeh 9 | - pandas 10 | - numpy 11 | 12 | ##RPi Side 13 | 14 | - run flask server 15 | - `python api.py` 16 | - start xbee on RPi or other device 17 | - `python xbee_listen.py` 18 | - start bokeh server and plotting routine: 19 | - `bokeh-server` 20 | - `python bokeh_plots.py` 21 | 22 | 23 | 24 | 25 | ##GIF Conversion: 26 | `ffmpeg -i input.mov -s 800x400 -pix_fmt rgb24 -r 10 -f gif - | gifsicle --optimize=3 --delay=3 > out.gif` -------------------------------------------------------------------------------- /env-full.txt: -------------------------------------------------------------------------------- 1 | # This file may be used to create an environment using: 2 | # $ conda create --name --file 3 | # platform: osx-64 4 | bokeh=0.2=np17py27_1 5 | dateutil=2.1=py27_2 6 | distribute=0.6.45=py27_1 7 | flask=0.10.1=py27_1 8 | flask-restful=2.1.0=py27_0 9 | gevent=0.13.8=py27_0 10 | gevent-websocket=0.3.6=py27_2 11 | greenlet=0.4.1=py27_0 12 | itsdangerous=0.23=py27_0 13 | jinja2=2.7.1=py27_0 14 | libevent=2.0.20=1 15 | markupsafe=0.18=py27_0 16 | numpy=1.7.1=py27_0 17 | pandas=0.12.0=np17py27_0 18 | pip=1.4.1=py27_0 19 | pyserial=2.7=py27_0 20 | python=2.7.6=0 21 | pytz=2013b=py27_0 22 | readline=6.2=1 23 | redis=2.6.9=0 24 | redis-py=2.7.2=py27_0 25 | requests=1.2.3=py27_0 26 | scipy=0.13.0=np17py27_0 27 | six=1.4.1=py27_0 28 | sqlite=3.7.13=1 29 | tk=8.5.13=1 30 | werkzeug=0.9.4=py27_0 31 | xbee=2.1.0=py27_0 32 | zlib=1.2.7=1 33 | -------------------------------------------------------------------------------- /bokeh_plots.py: -------------------------------------------------------------------------------- 1 | # The plot server must be running 2 | # Go to http://localhost:5006/bokeh to view this plot 3 | 4 | import numpy as np 5 | from bokeh.plotting import * 6 | 7 | import datetime 8 | import sqlite3 9 | import pandas as pd 10 | import pandas.io.json as pjson 11 | 12 | N = 20 13 | 14 | df = pd.io.json.read_json('http://localhost:5000/api/v1.0/slice/0:%d' % (N)) 15 | rssi = df.rssi*-1.0 16 | x = np.arange(len(df)) 17 | xbee_id = str(df.xbee[0]) 18 | 19 | output_server("XBee RSSI Signal") 20 | line(x,rssi, color="#0000FF", x_axis_type = "datetime", 21 | tools="pan,zoom,resize", width=1200,height=300, title = 'Streaming RSSI Values', 22 | legend='XBee %s Raw' % (xbee_id)) 23 | 24 | 25 | xaxis()[0].axis_label = "Time" 26 | yaxis()[0].axis_label = "Signal Strength" 27 | 28 | show() 29 | 30 | r = requests.get('http://localhost:5000/api/v1.0/count') 31 | total = r.json()['total'] 32 | 33 | i = 0 34 | import time 35 | from bokeh.objects import GlyphRenderer 36 | renderer = [r for r in curplot().renderers if isinstance(r, GlyphRenderer)][0] 37 | ds = renderer.data_source 38 | while True: 39 | lower = (i*N)%total 40 | upper = N+lower 41 | df = pd.io.json.read_json('http://localhost:5000/api/v1.0/slice/%d:%d' %(lower,upper)) 42 | print lower, upper 43 | ds.data["x"] = x+lower 44 | ds.data["y"] = df.rssi 45 | ds._dirty = True 46 | session().store_obj(ds) 47 | time.sleep(1.25) 48 | i+=1 49 | -------------------------------------------------------------------------------- /bokeh_single_plot.py: -------------------------------------------------------------------------------- 1 | # The plot server must be running 2 | # Go to http://localhost:5006/bokeh to view this plot 3 | 4 | import numpy as np 5 | from bokeh.plotting import * 6 | 7 | import datetime 8 | import sqlite3 9 | import pandas as pd 10 | import pandas.io.json as pjson 11 | import pandas.io.sql as psql 12 | 13 | 14 | lower = 20 15 | upper = 100 16 | 17 | #df = pd.io.json.read_json('http://localhost:5000/api/v1.0/slice/%d:%d' %(lower,upper)) 18 | conn = sqlite3.connect('xbee_saved.db', detect_types=sqlite3.PARSE_DECLTYPES) 19 | sql = 'SELECT * FROM signal WHERE ROWID >= %s AND ROWID < %s' % (lower,upper) 20 | df = psql.read_frame(sql,conn) 21 | 22 | rssi = df.rssi*-1.0 23 | x = np.arange(len(df)) 24 | xbee_id = str(df.xbee[0]) 25 | 26 | output_file("xbee_rssi.html", title="XBee RSSI Signal") 27 | 28 | line_plot = line(x,rssi, color="#0000FF", x_axis_type = "datetime", 29 | tools="pan,zoom,resize", width=600,height=200, title = 'Streaming RSSI Values', 30 | legend='XBee %s Raw' % (xbee_id), name='xbee_plot') 31 | 32 | 33 | xaxis()[0].axis_label = "Time" 34 | yaxis()[0].axis_label = "Signal Strength" 35 | 36 | line_snippet = line_plot.inject_snippet(server=False) 37 | 38 | open("embed_example.html","w").write(""" 39 | 40 | 41 | 42 |

embed example

43 | %s 44 |

after embed

45 | 46 | """ % line_snippet) 47 | 48 | print line_snippet 49 | 50 | if __name__ == "__main__": 51 | show() # open a browser 52 | -------------------------------------------------------------------------------- /xbee_listen.py: -------------------------------------------------------------------------------- 1 | import time 2 | import serial 3 | import datetime 4 | import sqlite3 5 | 6 | 7 | from xbee import XBee 8 | import platform 9 | 10 | PORT = '' 11 | 12 | if platform.machine() == 'armv6l': 13 | PORT = '/dev/ttyAMA0' #set tty port BEAGLE BONE NOTE: O1 14 | else: 15 | if platform.system() == 'Linux': 16 | PORT = '/dev/ttyUSB0' 17 | if platform.system() == 'Darwin': 18 | PORT = '/dev/tty.usbserial-FTF0FD46' #OSX 19 | 20 | 21 | BAUD_RATE = 9600 #set baud rate 22 | 23 | serial_port = serial.Serial(PORT, BAUD_RATE) 24 | 25 | conn = sqlite3.connect('xbee_rssi.db', detect_types=sqlite3.PARSE_DECLTYPES) 26 | 27 | with conn: 28 | cur = conn.cursor() 29 | 30 | cur.execute("DROP TABLE IF EXISTS signal") 31 | print 'Setting up Database...' 32 | # Create table 33 | cur.execute('''CREATE TABLE signal 34 | (date TIMESTAMP, xbee INTEGER, rssi INTEGER)''') 35 | 36 | xbee = XBee(serial_port, escaped=True) #asynchronous calling to 37 | 38 | def dump(data): #define callback function 39 | conn = sqlite3.connect('xbee_rssi.db', detect_types=sqlite3.PARSE_DECLTYPES) 40 | cur = conn.cursor() 41 | 42 | if 'source_addr' in data: 43 | addr = data['source_addr'] 44 | rssi_val = int(data['rssi'].encode('hex'),16) 45 | addr_int = int(addr.encode('hex'),16) 46 | 47 | rf_data = "got the message (%s)" % data['rf_data'] 48 | fid = data['options'] 49 | 50 | # now = datetime.datetime.now().strftime('%Y-%m-%d_%H:%M:%S') 51 | now = datetime.datetime.now() 52 | 53 | print rssi_val 54 | cur.execute("INSERT INTO signal VALUES(?, ?, ?)", (now,addr_int,-1*rssi_val)) 55 | # Save (commit) the changes 56 | conn.commit() 57 | 58 | xbee.send('tx', frame_id=fid, dest_addr=addr, data=rf_data) 59 | 60 | conn.close() 61 | 62 | serial_port = serial.Serial(PORT, BAUD_RATE) 63 | xbee = XBee(serial_port, callback=dump, escaped=True) #asynchronous calling to 64 | 65 | # loop forever 66 | while True: 67 | try: 68 | time.sleep(0.5) 69 | except KeyboardInterrupt: 70 | break 71 | 72 | 73 | xbee.halt() 74 | serial_port.close() -------------------------------------------------------------------------------- /api.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, jsonify 2 | from flask.ext import restful 3 | from flask import g #sqlite connection 4 | 5 | import sqlite3 6 | 7 | import datetime 8 | import sqlite3 9 | 10 | import platform 11 | 12 | app = Flask(__name__) 13 | api = restful.Api(app) 14 | 15 | DATABASE = 'xbee_saved.db' 16 | 17 | def get_db(): 18 | db = getattr(g, '_database', None) 19 | if db is None: 20 | db = g._database = sqlite3.connect(DATABASE, detect_types=sqlite3.PARSE_DECLTYPES) 21 | return db 22 | 23 | 24 | @app.route('/') 25 | def index(): 26 | return "Data is at: /api/v1.0/INTEGER" 27 | 28 | 29 | class Count(restful.Resource): 30 | def get(self): 31 | cur = get_db().cursor() 32 | sql = 'SELECT COUNT(*) FROM signal' 33 | cur.execute(sql) 34 | result = cur.fetchall()[0][0] 35 | 36 | result_dict = {'total':result} 37 | return jsonify(result_dict) 38 | 39 | if platform.machine() == 'armv6l': 40 | class RSSIData(restful.Resource): 41 | def get(self,update_lim): 42 | cur = get_db().cursor() 43 | sql = 'SELECT * FROM signal ORDER BY date DESC LIMIT %s' % (update_lim) 44 | cur.execute(sql) 45 | result = cur.fetchall() 46 | date = [] 47 | xbee = [] 48 | rssi = [] 49 | 50 | for pt in result: 51 | date.append(pt[0]) 52 | xbee.append(pt[1]) 53 | rssi.append(pt[2]) 54 | 55 | #df = psql.read_frame(sql,conn) 56 | result_dict = {'date':date,'rssi':rssi,'xbee':xbee} 57 | return jsonify(result_dict) 58 | else: 59 | import pandas as pd 60 | import pandas.io.sql as psql 61 | 62 | 63 | class RSSIData(restful.Resource): 64 | def get(self,update_lim): 65 | conn = get_db() 66 | sql = 'SELECT * FROM signal ORDER BY date DESC LIMIT %s' % (update_lim) 67 | df = psql.read_frame(sql,conn) 68 | return jsonify(df.to_dict()) 69 | 70 | class RSSIDataSlice(restful.Resource): 71 | def get(self,lowerupper): 72 | lower,upper = lowerupper.split(':') 73 | print lower, upper 74 | lower = str(int(lower)+1) 75 | conn = get_db() 76 | sql = 'SELECT * FROM signal WHERE ROWID >= %s AND ROWID < %s' % (lower,upper) 77 | df = psql.read_frame(sql,conn) 78 | return jsonify(df.to_dict()) 79 | 80 | 81 | 82 | 83 | api.add_resource(Count, '/api/v1.0/count') 84 | api.add_resource(RSSIData, '/api/v1.0/') 85 | api.add_resource(RSSIDataSlice, '/api/v1.0/slice/') 86 | 87 | @app.teardown_appcontext 88 | def close_connection(exception): 89 | db = getattr(g, '_database', None) 90 | if db is not None: 91 | db.close() 92 | 93 | if __name__ == '__main__': 94 | app.run(host='0.0.0.0',debug=True) 95 | --------------------------------------------------------------------------------