├── .gitignore ├── LICENSE ├── README.md ├── echo_client.py ├── hello.py ├── images ├── LightEaster.png └── pattern_40.gif ├── index.html ├── radio-smbus-tea5767-class.py ├── radio-smbus-tea5767.py ├── radio_server.py ├── radioweb.py ├── ss.py ├── tea5767.png ├── tea5767_tornado.html ├── tea5767_tornado_server.py ├── tea5767controller.py ├── tea5767stationscanner.py ├── telek.txt ├── websocket-other.py ├── workfile └── wstester.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 LinuxCircle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Raspberry Pi FM receiver using Python 3 I2C and Tea5767

2 | 3 | Contributor: Dipto Pratyaksa 4 | LinuxCircle.com 5 | 6 | Description: 7 | 8 | This is a simple TEA5767 driver to tune into a local radio station 9 | with Raspberry Pi 2. You can either use the console program or run the web interface. 10 | 11 | 12 | 13 | Running on Linux command console: 14 | 15 | sudo python3 tea5767controller.py 16 | 17 | or with sufficient permission and executable file: 18 | sudo ./tea5767controller.py 19 | 20 | Running the Web Interface 21 |
22 | Run it with: sudo python3 tea5767_tornado_server.py 23 |
24 | Open browser from a client computer: http://IPADDRESSOFYOURPI:8888 25 |
26 | Example: http:/192.168.1.2:8888 27 | 28 | -------------------------------------------------------------------------------- /echo_client.py: -------------------------------------------------------------------------------- 1 | #from __future__ import print_function 2 | import websocket 3 | 4 | if __name__ == "__main__": 5 | websocket.enableTrace(True) 6 | ws = websocket.create_connection("ws://192.168.1.2:8888/ws") 7 | print("Sending 'Hello, World'...") 8 | ws.send("Hello, World") 9 | print("Sent") 10 | print("Receiving...") 11 | result = ws.recv() 12 | print("Received '%s'" % result) 13 | ws.close() 14 | -------------------------------------------------------------------------------- /hello.py: -------------------------------------------------------------------------------- 1 | print("TEA5767 FM Radio project") 2 | print("dipto@linuxcircle.com") 3 | print("Excellent codes will be uploaded here!") 4 | 5 | -------------------------------------------------------------------------------- /images/LightEaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxCircle/tea5767/e915e11c7324a702e850768681c9a7e3e253b5c1/images/LightEaster.png -------------------------------------------------------------------------------- /images/pattern_40.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxCircle/tea5767/e915e11c7324a702e850768681c9a7e3e253b5c1/images/pattern_40.gif -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 49 | 50 | 51 | 52 |

Raspberry Pi 2 TEA5767 Radio Tuner

53 |
54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /radio-smbus-tea5767-class.py: -------------------------------------------------------------------------------- 1 | ######!/usr/bin/python3 2 | ###### # -*- coding: utf-8 -*- 3 | 4 | import smbus as smbus 5 | import subprocess 6 | import time 7 | import sys 8 | 9 | import quick2wire.i2c as i2clib 10 | from quick2wire.i2c import I2CMaster, writing_bytes, reading 11 | 12 | cof = 32768 #crystal constant 13 | 14 | 15 | class tea5767: 16 | def __init__(self): 17 | self.i2c = smbus.SMBus(1) 18 | self.bus = i2clib.I2CMaster() 19 | self.add = 0x60 # I2C address circuit 20 | self.freq = 101.9 21 | 22 | print("FM Radio Module TEA5767") 23 | 24 | 25 | def getFreq(self): 26 | # getReady() 27 | frequency = 0.0 28 | results = self.bus.transaction( 29 | reading(self.add, 5) 30 | ) 31 | 32 | frequency = ((results[0][0]&0x3F) << 8) + results[0][1]; 33 | # Determine the current frequency using the same high side formula as above 34 | frequency = round(frequency * 32768 / 4 - 225000) / 1000000; 35 | # print(frequency) 36 | return round(frequency,2) 37 | 38 | 39 | def calculateFrequency(self): 40 | """calculate the station frequency based upon the upper and lower bits read from the device""" 41 | repeat = 0 42 | f =0.0 43 | with i2clib.I2CMaster() as b: 44 | results = b.transaction( 45 | reading(self.add, 5) 46 | ) 47 | 48 | uF = results[0][0]&0x3F 49 | lF = results[0][1] 50 | # this is probably not the best way of doing this but I was having issues with the 51 | # frequency being off by as much as 1.5 MHz 52 | current_freq = round((float(round(int(((int(uF)<<8)+int(lF))*cof/4-22500)/100000)/10)-.2)*10)/10 53 | return current_freq 54 | 55 | 56 | 57 | #script to get ready 58 | def getReady(self): 59 | 60 | readyFlag = 0 61 | i = False 62 | attempt = 0 63 | results=[] 64 | standbyFlag = 0 65 | sys.stdout.flush() 66 | time.sleep(0.1) 67 | print("Getting ready ", end ="") 68 | while (i==False): 69 | results = self.bus.transaction( 70 | reading(self.add, 5) 71 | ) 72 | 73 | readyFlag = 1 if (results[0][0]&0x80)==128 else 0 74 | standbyFlag = 1 if (results[0][3]+0x40)!=319 else 0 75 | 76 | #print("result search mode:" , results[0][0]+0x40) 77 | #s = results[0][3]+0x40 78 | sys.stdout.flush() 79 | time.sleep(0.9) 80 | print(".", end = "") 81 | # print("Soft mute ", results[0][3]&0x08) 82 | #print(results[0][3]+0x40) 83 | i=standbyFlag*readyFlag 84 | attempt+=1 85 | if(attempt>10): 86 | break 87 | if(i==True): 88 | print("Ready! (",attempt,")") 89 | # print("Raw output ", results[0]) 90 | else: 91 | self.i2c.read_byte(self.add) 92 | print("Not ready!") 93 | 94 | def writeFrequency(self,f, mute): 95 | freq = f # desired frequency in MHz (at 101.1 popular music station in Melbourne) 96 | cof = 32768 97 | i=False 98 | attempt = 0 99 | # Frequency distribution for two bytes (according to the data sheet) 100 | freq14bit = int (4 * (freq * 1000000 + 225000) / cof) 101 | freqH = freq14bit >>8 102 | freqL = freq14bit & 0xFF 103 | 104 | data = [0 for i in range(4)] 105 | # Descriptions of individual bits in a byte - viz. catalog sheets 106 | if(mute==0): 107 | init = freqH&0x3F# freqH # 1.byte (MUTE bit; Frequency H) // MUTE is 0x80 disable mute and search mode & 0x3F 108 | else: 109 | init = freqH&0x7F 110 | data[0] = freqL # 2.byte (frequency L) 111 | if(mute==0): 112 | data[1] = 0b10010000 # 3.byte (SUD; SSL1, SSL2; HLSI, MS, MR, ML; SWP1) 113 | else: 114 | data[1] = 0b00010110 115 | data[2] = 0b00010010 # 4.byte (SWP2; STBY, BL; XTAL; smut; HCC, SNC, SI) 116 | data[3] = 0b00000000 # 5.byte (PLREFF; DTC; 0; 0; 0; 0; 0; 0) 117 | 118 | #data[1]=0xB0; #3 byte (0xB0): high side LO injection is on,. 119 | #data[2]=0x10; #4 byte (0x10) : Xtal is 32.768 kHz 120 | #data[3]=0x00; #5 byte0x00) 121 | 122 | while (i==False): 123 | try: 124 | self.i2c.write_i2c_block_data (self.add, init, data) # Setting a new frequency to the circuit 125 | except IOError as e : 126 | i = False 127 | attempt +=1 128 | if attempt > 100000: 129 | break 130 | except Exception as e: 131 | print("I/O error: {0}".format(e)) 132 | 133 | else: 134 | i = True 135 | cf = self.calculateFrequency() 136 | gf = self.getFreq() 137 | averageF =round((cf+gf)/2,2) 138 | 139 | 140 | def scan(self,direction): 141 | i=False 142 | self.freq = self.getFreq() 143 | fadd = 0 144 | while (i==False): 145 | if(direction==1): 146 | fadd+=0.05 147 | else: 148 | fadd-=0.05 149 | self.freq = self.getFreq() #round((self.calculateFrequency()+self.getFreq())/2,2) 150 | if(self.freq<87.5): 151 | self.freq=108 152 | elif(self.freq>108): 153 | self.freq=87.5 154 | self.writeFrequency(self.freq+fadd,1) 155 | time.sleep(0.1) 156 | results = self.bus.transaction( 157 | reading(self.add, 5) 158 | ) 159 | 160 | readyFlag = 1 if (results[0][0]&0x80)==128 else 0 161 | level = results[0][3]>>4 162 | #print(results[0][0]&0x80 , " " , results[0][3]>>4) 163 | if(readyFlag and level>9): 164 | i=True 165 | print("Frequency tuned: ",self.calculateFrequency(), "FM (Strong Signal: ",level,")") 166 | 167 | else: 168 | i=False 169 | print("Station skipped: ",self.calculateFrequency(), "FM (Weak Signal: ",level,")") 170 | 171 | self.writeFrequency(self.calculateFrequency(),0) 172 | 173 | def off(self): 174 | print("Radio off: Goodbye now!") 175 | self.writeFrequency(self.calculateFrequency(), 1) 176 | 177 | radio = tea5767() 178 | radio.getReady() 179 | radio.scan(1) 180 | time.sleep(10) 181 | radio.scan(1) 182 | time.sleep(10) 183 | radio.scan(0) 184 | time.sleep(10) 185 | radio.scan(0) 186 | time.sleep(10) 187 | radio.off() 188 | 189 | -------------------------------------------------------------------------------- /radio-smbus-tea5767.py: -------------------------------------------------------------------------------- 1 | ######!/usr/bin/python3 2 | ###### # -*- coding: utf-8 -*- 3 | 4 | import smbus as smbus 5 | import subprocess 6 | import time 7 | import sys 8 | 9 | from Adafruit_I2C import Adafruit_I2C 10 | import quick2wire.i2c as i2clib 11 | from quick2wire.i2c import I2CMaster, writing_bytes, reading 12 | 13 | attempt = 0 14 | output=[] 15 | bus = i2clib.I2CMaster() 16 | add = 0x60 # I2C address circuit 17 | cof = 32768 18 | i = False 19 | freq = 105.1 20 | # Frequency distribution for two bytes (according to the data sheet) 21 | freq14bit = int (4 * (freq * 1000000 + 225000) / cof) 22 | freqH = freq14bit >>8 23 | freqL = freq14bit & 0xFF 24 | 25 | data = [0 for i in range(4)] 26 | # Descriptions of individual bits in a byte - viz. catalog sheets 27 | add = 0x60 # I2C address circuit 28 | init = freqH&0x3F# freqH # 1.bajt (MUTE bit; Frequency H) // MUTE is 0x80 disable mute and search mode & 0x3F 29 | 30 | def backspace(n): 31 | # print((b'\x08' * n).decode(), end='') # use \x08 char to go back 32 | print('\r' * n, end='') 33 | 34 | 35 | i2c = smbus.SMBus(1) # newer version RASP (512 megabytes) 36 | #bus = smbus.SMBus (0) # RASP older version (256MB) 37 | 38 | def getFreq(): 39 | # getReady() 40 | frequency = 0.0 41 | results = bus.transaction( 42 | reading(add, 5) 43 | ) 44 | 45 | frequency = ((results[0][0]&0x3F) << 8) + results[0][1]; 46 | # Determine the current frequency using the same high side formula as above 47 | frequency = round(frequency * 32768 / 4 - 225000) / 1000000; 48 | # print(frequency) 49 | return round(frequency,2) 50 | 51 | 52 | def calculateFrequency(): 53 | """calculate the station frequency based upon the upper and lower bits read from the device""" 54 | #bus = smbus.SMBus (0) # RASP older version (256MB) 55 | repeat = 0 56 | f =0.0 57 | with i2clib.I2CMaster() as b: 58 | results = b.transaction( 59 | reading(add, 5) 60 | ) 61 | 62 | uF = results[0][0]&0x3F 63 | lF = results[0][1] 64 | # this is probably not the best way of doing this but I was having issues with the 65 | # frequency being off by as much as 1.5 MHz 66 | current_freq = round((float(round(int(((int(uF)<<8)+int(lF))*cof/4-22500)/100000)/10)-.2)*10)/10 67 | return current_freq 68 | 69 | 70 | 71 | 72 | #import pigpio 73 | 74 | #i2c = smbus.SMBus(1) # newer version RASP (512 megabytes) 75 | #bus = smbus.SMBus (0) # RASP older version (256MB) 76 | 77 | #af = Adafruit_I2C(0x60, 1, True) 78 | #pipi = pigpio.pi() 79 | 80 | 81 | 82 | print("FM Radio Module TEA5767") 83 | 84 | #script to get ready 85 | #def getReady(): 86 | readyFlag = 0 87 | i = False 88 | attempt = 0 89 | results=[] 90 | standbyFlag = 0 91 | sys.stdout.flush() 92 | time.sleep(0.1) 93 | print("Getting ready ", end ="") 94 | while (i==False): 95 | #output = i2c.read_i2c_block_data(add,init) 96 | #i2c.read_byte(add) 97 | results = bus.transaction( 98 | reading(add, 5) 99 | ) 100 | 101 | readyFlag = 1 if (results[0][0]&0x80)==128 else 0 102 | standbyFlag = 1 if (results[0][3]+0x40)!=319 else 0 103 | 104 | #print("result search mode:" , results[0][0]+0x40) 105 | #s = results[0][3]+0x40 106 | sys.stdout.flush() 107 | time.sleep(0.9) 108 | print(".", end = "") 109 | # print("Soft mute ", results[0][3]&0x08) 110 | #print(results[0][3]+0x40) 111 | i=standbyFlag*readyFlag 112 | attempt+=1 113 | if(attempt>10): 114 | break 115 | if(i==True): 116 | print("Ready! (",attempt,")") 117 | # print("Raw output ", results[0]) 118 | else: 119 | i2c.read_byte(add) 120 | print("Not ready!") 121 | 122 | 123 | #current_address = i2c.read_byte(add) 124 | #time.sleep(1) 125 | #getReady() 126 | time.sleep(1) 127 | print("Current frequency is " , getFreq(), "FM or ", calculateFrequency(),"FM") 128 | #current_address = i2c.read_byte(add) 129 | #print("Current address " , current_address) 130 | 131 | #writeFrequency(105.1) 132 | 133 | def writeFrequency(f, mute): 134 | freq = f # desired frequency in MHz (at 101.1 popular music station in Melbourne) 135 | cof = 32768 136 | i=False 137 | attempt = 0 138 | # Frequency distribution for two bytes (according to the data sheet) 139 | freq14bit = int (4 * (freq * 1000000 + 225000) / cof) 140 | freqH = freq14bit >>8 141 | freqL = freq14bit & 0xFF 142 | 143 | data = [0 for i in range(4)] 144 | # Descriptions of individual bits in a byte - viz. catalog sheets 145 | add = 0x60 # I2C address circuit 146 | if(mute==0): 147 | init = freqH&0x3F# freqH # 1.bajt (MUTE bit; Frequency H) // MUTE is 0x80 disable mute and search mode & 0x3F 148 | else: 149 | init = freqH&0x7F 150 | data[0] = freqL # 2.bajt (frequency L) 151 | if(mute==0): 152 | data[1] = 0b10010000 # 3.bajt (SUD; SSL1, SSL2; HLSI, MS, MR, ML; SWP1) 153 | else: 154 | data[1] = 0b00010110 155 | data[2] = 0b00010010 # 4.bajt (SWP2; STBY, BL; XTAL; smut; HCC, SNC, SI) 156 | data[3] = 0b00000000 # 5.bajt (PLREFF; DTC; 0; 0; 0; 0; 0; 0) 157 | 158 | #data[1]=0xB0; #3 byte (0xB0): high side LO injection is on,. 159 | #data[2]=0x10; #4 byte (0x10) : Xtal is 32.768 kHz 160 | #data[3]=0x00; #5 byte0x00) 161 | 162 | while (i==False): 163 | try: 164 | i2c.write_i2c_block_data (add, init,data) # Setting a new frequency to the circuit 165 | except IOError as e : 166 | #print("I/O error: {0}".format(e)) 167 | i = False 168 | attempt +=1 169 | if attempt > 100000: 170 | break 171 | except Exception as e: 172 | print("I/O error: {0}".format(e)) 173 | 174 | else: 175 | i = True 176 | print(attempt) 177 | # subprocess.call(['i2cdetect', '-y', '1']) 178 | # flag = 1 #optional flag to signal your code to resend or something 179 | 180 | # if (i == False): 181 | # print("Took too long to change") 182 | # else: 183 | # cf = calculateFrequency() 184 | # gf = getFreq() 185 | # averageF =(cf+gf)/2 186 | # print("Frequency changed: ", cf , " " , gf, "(",attempt,")") 187 | 188 | #writeFrequency(91.5) 189 | 190 | i=False 191 | f = getFreq() 192 | fadd = 0 193 | while (i==False): 194 | #output = i2c.read_i2c_block_data(add,init) 195 | #i2c.read_byte(add) 196 | fadd+=0.05 197 | f = (calculateFrequency()+getFreq())/2 198 | if(f<87 or f>108): 199 | f=87 200 | writeFrequency(f+fadd,1) 201 | #print(f+fadd) 202 | time.sleep(0.1) 203 | results = bus.transaction( 204 | reading(add, 5) 205 | ) 206 | # time.sleep(0.1) 207 | 208 | readyFlag = 1 if (results[0][0]&0x80)==128 else 0 209 | level = results[0][3]>>4 210 | print(results[0][0]&0x80 , " " , results[0][3]>>4) 211 | if(readyFlag and level>8): 212 | i=True 213 | else: 214 | i=False 215 | #print(results[0][0]&0x40) 216 | 217 | writeFrequency((getFreq()+calculateFrequency())/2,0) 218 | print("Frequency tuned: ", calculateFrequency(), "FM") 219 | 220 | -------------------------------------------------------------------------------- /radio_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | 5 | """ 6 | Programmer : Dipto Pratyaksa for www.LinuxCircle.com 7 | Dependencies : tea5767stationscanner.py, index.html 8 | Original source : http://www.svesoftware.com/documents/radio_server.py 9 | Last update : 17 August 2015 - Indonesia's independence day 10 | """ 11 | 12 | import sys 13 | import http 14 | from http import server 15 | import os 16 | import glob 17 | import time 18 | import datetime 19 | import tea5767stationscanner 20 | import websocket 21 | import socket 22 | import sys 23 | 24 | 25 | class MyRequestHandler(http.server.SimpleHTTPRequestHandler): 26 | 27 | tea = None 28 | 29 | def __init__(self, request, address, server): 30 | if(self.tea==None): 31 | self.tea = rr 32 | self.tea.on() 33 | http.server.SimpleHTTPRequestHandler.__init__(self, request, address, server) 34 | 35 | 36 | def do_GET(self): 37 | if self.path == '/': 38 | self.path = 'index.html' 39 | if self.path == '/searchup': 40 | self.tea.scan(1) 41 | print('search up finished') 42 | self.send_response(200) 43 | self.send_header('Content-type','text/html') 44 | #self.send_header("Content-type", "application/json") 45 | #self.send_header("Content-length", 2) 46 | self.end_headers() 47 | self.wfile.write(bytes("ok","UTF-8")) 48 | self.wfile.flush() 49 | return 50 | if self.path == '/searchdown': 51 | self.tea.scan(0) 52 | print('search down finished') 53 | self.send_response(200) 54 | self.send_header('Content-type','text/html') 55 | #self.send_header("Content-type", "application/json") 56 | #self.send_header("Content-length", 2) 57 | self.end_headers() 58 | self.wfile.write(bytes("ok","UTF-8")) 59 | self.wfile.flush() 60 | return 61 | if self.path == '/off': 62 | self.tea.off() 63 | print('radio mute') 64 | self.send_response(200) 65 | self.send_header('Content-type','text/html') 66 | #self.send_header("Content-type", "application/json") 67 | #self.send_header("Content-length", 2) 68 | self.end_headers() 69 | self.wfile.write(bytes("ok","UTF-8")) 70 | self.wfile.flush() 71 | return 72 | 73 | if self.path == '/info': 74 | resp = self.tea.info() 75 | resp = "{" + '"freq":' + resp['freq'] + ',"level":' + resp['level']+',"stereo":\"'+resp['stereo'] + "\"}" 76 | print(resp) 77 | self.send_response(200) 78 | self.send_header("Content-type", "application/json") 79 | self.send_header("Content-length", len(resp)) 80 | self.end_headers() 81 | self.wfile.write(bytes(resp, 'UTF-8')) 82 | return 83 | 84 | return http.server.SimpleHTTPRequestHandler.do_GET(self) 85 | 86 | 87 | rr = tea5767stationscanner.tea5767() 88 | HandlerClass = MyRequestHandler 89 | ServerClass = http.server.HTTPServer 90 | Protocol = "HTTP/1.0" 91 | 92 | 93 | 94 | if sys.argv[1:]: 95 | port = int(sys.argv[1]) 96 | else: 97 | port = 8888 98 | server_address = ('0.0.0.0', port) 99 | 100 | HandlerClass.protocol_version = Protocol 101 | httpd = ServerClass(server_address, HandlerClass) 102 | 103 | WS_PORT = 9876 104 | #ws = websocket.Websocket(WS_PORT, driver) 105 | #Thread(target=ws.serve_forever, args=(stop,)).start() 106 | 107 | 108 | try: 109 | sa = httpd.socket.getsockname() 110 | print ("Serving HTTP on ", sa[0], "port", sa[1]) 111 | httpd.serve_forever() 112 | except: 113 | print("Program finished") 114 | rr.off() 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /radioweb.py: -------------------------------------------------------------------------------- 1 | import http.server 2 | import socketserver 3 | 4 | PORT = 8001 5 | 6 | Handler = http.server.SimpleHTTPRequestHandler 7 | 8 | httpd = socketserver.TCPServer(("", PORT), Handler) 9 | 10 | print("serving at port", PORT) 11 | httpd.serve_forever() 12 | -------------------------------------------------------------------------------- /ss.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Simple socket server using threads 3 | ''' 4 | 5 | import socket 6 | import sys 7 | import time 8 | 9 | HOST = '' # Symbolic name, meaning all available interfaces 10 | PORT = 7878 # Arbitrary non-privileged port 11 | 12 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 13 | print ('Socket created') 14 | 15 | #Bind socket to local host and port 16 | try: 17 | s.bind((HOST, PORT)) 18 | except socket.error as msg: 19 | print ('Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]) 20 | sys.exit() 21 | 22 | print ('Socket bind complete') 23 | 24 | #Start listening on socket 25 | s.listen(10) 26 | print ('Socket now listening') 27 | 28 | def mysend(msg): 29 | totalsent = 0 30 | while totalsent < MSGLEN: 31 | sent = s.send(msg[totalsent:]) 32 | if sent == 0: 33 | raise RuntimeError("socket connection broken") 34 | totalsent = totalsent + sent 35 | 36 | 37 | 38 | #now keep talking with the client 39 | while 1: 40 | #wait to accept a connection - blocking call 41 | conn, addr = s.accept() 42 | print ('Connected with ' + addr[0] + ':' + str(addr[1])) 43 | #mysend("gaga".encode('utf-8')) 44 | i = 0 45 | while 1: 46 | s.sendall(str("gagaga").encode("UTF-8")) 47 | result=conn.recv(1024) 48 | print(result) 49 | time.sleep(1) 50 | 51 | s.close() 52 | 53 | def mysend(msg): 54 | totalsent = 0 55 | while totalsent < MSGLEN: 56 | sent = s.send(msg[totalsent:]) 57 | if sent == 0: 58 | raise RuntimeError("socket connection broken") 59 | totalsent = totalsent + sent 60 | -------------------------------------------------------------------------------- /tea5767.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxCircle/tea5767/e915e11c7324a702e850768681c9a7e3e253b5c1/tea5767.png -------------------------------------------------------------------------------- /tea5767_tornado.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 272 | 273 | 274 | 275 | 452 | 453 | 454 | 455 |

Raspberry Pi 2 TEA5767 Radio Tuner

456 | 457 | 458 | 459 |
460 |
461 | 462 |
463 | 479 | 480 |
481 | 482 | 483 | 484 | 485 | 486 | -------------------------------------------------------------------------------- /tea5767_tornado_server.py: -------------------------------------------------------------------------------- 1 | import tornado.httpserver 2 | import tornado.websocket 3 | import tornado.ioloop 4 | import tornado.web 5 | 6 | import tea5767stationscanner 7 | 8 | 9 | class IndexHandler(tornado.web.RequestHandler): 10 | @tornado.web.asynchronous 11 | def get(self): 12 | self.render("tea5767_tornado.html") 13 | 14 | class WSHandler(tornado.websocket.WebSocketHandler): 15 | 16 | controller = None 17 | def check_origin(self, origin): 18 | return True 19 | 20 | def open(self): 21 | print ("connecting...") 22 | try: 23 | #self.controller = DeviceController() 24 | #self.write_message("Hello world!") 25 | self.controller=tea5767stationscanner.tea5767() 26 | self.controller.on() 27 | data=self.controller.info() 28 | self.write_message(data) 29 | # self.controller.prepareSocket() 30 | except Exception as a: 31 | print(a) 32 | 33 | def on_message(self, message): 34 | print("Command:", message) 35 | data="" 36 | try: 37 | 38 | if(message=="up"): 39 | self.controller.scan(1) 40 | elif(message=="down"): 41 | self.controller.scan(0) 42 | elif(message=="off"): 43 | data=self.controller.off() 44 | elif(message=="mute"): 45 | data=self.controller.mute() 46 | data=self.controller.info() 47 | 48 | if(message=="off"): 49 | data=self.controller.off() 50 | 51 | self.write_message(data) 52 | except Exception as a: 53 | print("Error: ", a) 54 | 55 | def on_close(self): 56 | print ("closing sockets") 57 | self.controller ="" 58 | 59 | 60 | static_path = "/home/pi/Projects/tea5767/" 61 | favicon_path ="" 62 | 63 | application = tornado.web.Application([ 64 | (r'/favicon.ico', tornado.web.StaticFileHandler, {'path': favicon_path}), 65 | (r"/images/(.*)",tornado.web.StaticFileHandler, {"path": "./images"},), 66 | (r'/static/(.*)', tornado.web.StaticFileHandler, {'path': static_path}), 67 | (r'/', IndexHandler), 68 | (r"/ws", WSHandler), 69 | ]) 70 | 71 | 72 | if __name__ == "__main__": 73 | http_server = tornado.httpserver.HTTPServer(application) 74 | print ("Waiting client connection via browser port 8888") 75 | http_server.listen(8888) 76 | tornado.ioloop.IOLoop.instance().start() 77 | -------------------------------------------------------------------------------- /tea5767controller.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Filename : tea5767controller.py 6 | Programmer : Dipto Pratyaksa for www.LinuxCircle.com 7 | Date / Version : August 2015. V1.0 8 | Tech : Python 3, SMBUS, i2C, TEA5767 FM Radio, Raspberry Pi 2 9 | Project : Raspberry Pi Voice command robot via FM transmitter 10 | and receiver 11 | Module : TEA5767 Radio 12 | 13 | Wish list : 14 | - Save strong stations into text file list 15 | 16 | Reference : 17 | 1. https://raw.githubusercontent.com/JTechEng/tea5767/ 18 | 2. https://github.com/pcnate/fm-radio-python 19 | 3. http://www.astromik.org/raspi/38.htm 20 | 21 | Usage : 22 | sudo python3 tea5767controller.py 23 | or with executable file 24 | sudo ./tea5767controller.py 25 | """ 26 | 27 | 28 | 29 | from tea5767stationscanner import tea5767 30 | 31 | a = tea5767() 32 | test ="" 33 | 34 | while(test!="x"): 35 | test=input("Radio command (u)p, (d)own, (t)est, e(x)it:") 36 | if(test=="u"): 37 | a.scan(1) 38 | elif(test=="d"): 39 | a.scan(0) 40 | elif(test=="t"): 41 | a.test() 42 | elif(test=="x"): 43 | a.off() 44 | -------------------------------------------------------------------------------- /tea5767stationscanner.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Programmer: Dipto Pratyaksa for LinuxCircle.com 6 | August 2015. Tech: Python 3, SMBUS, i2C, TEA5767 FM Radio, Raspberry Pi 2 7 | Project:Raspberry Pi Voice command robot via FM transmitter and receiver 8 | Module: Tea 5767 Station Scanner 9 | Future wish list: 10 | - Save strong stations into text file list 11 | Reference: 12 | https://raw.githubusercontent.com/JTechEng/tea5767/ 13 | https://github.com/pcnate/fm-radio-python 14 | http://www.astromik.org/raspi/38.htm 15 | """ 16 | 17 | import smbus as smbus 18 | import subprocess 19 | import time 20 | import sys 21 | 22 | import websocket 23 | 24 | 25 | import quick2wire.i2c as i2clib 26 | from quick2wire.i2c import I2CMaster, writing_bytes, reading 27 | 28 | cof = 32768 #crystal constant 29 | 30 | def get_bit(byteval,idx): 31 | return ((byteval&(1< 107.9: 47 | self.freq = 101.9 48 | 49 | self.signal = self.getLevel() 50 | self.stereoFlag = self.getStereoFlag() 51 | print("Last frequency = " , self.freq, "FM. Signal level = ", self.signal, " " , self.stereoFlag) 52 | self.writeFrequency(self.freq, 1, 1) 53 | # self.preparesocket() 54 | # self.ws = None 55 | def prepareSocket(self): 56 | #time.sleep(1) 57 | websocket.enableTrace(True) 58 | self.ws = websocket.create_connection("ws://192.168.1.2:8888/ws") 59 | self.ws.send('GET HTTP/1.1 200 OK\nContent-Type: text/html\n\n'.encode('utf-8')) 60 | print("Sending 'Hello, World'...") 61 | self.ws.send("Hello, World") 62 | print("Sent") 63 | print("Receiving...") 64 | result = self.ws.recv() 65 | print("Received '%s'" % result) 66 | self.ws.close() 67 | def on(self): 68 | self.writeFrequency(self.freq, 0, 0) 69 | 70 | def reset(self): 71 | #initiation 72 | if(not self.getReady()): 73 | print("resetting to default") 74 | self.i2c.write_byte(self.add, 0x00) 75 | self.getReady() 76 | 77 | 78 | def getFreq(self): 79 | frequency = 0.0 80 | results = self.bus.transaction( 81 | reading(self.add, 5) 82 | ) 83 | 84 | frequency = ((results[0][0]&0x3F) << 8) + results[0][1]; 85 | # Determine the current frequency using the same high side formula as above 86 | frequency = round(frequency * 32768 / 4 - 225000) / 1000000; 87 | return frequency 88 | 89 | def getLevel(self): 90 | level = 0 91 | results = self.bus.transaction( 92 | reading(self.add, 5) 93 | ) 94 | level = results[0][3]>>4 95 | return level 96 | 97 | def getChipID(self): 98 | id = 0 99 | results = self.bus.transaction( 100 | reading(self.add, 5) 101 | ) 102 | id = results[0][3]+0x0f 103 | 104 | return id 105 | 106 | def getStereoFlag(self): 107 | sf = 0 108 | results = self.bus.transaction( 109 | reading(self.add, 5) 110 | ) 111 | sf = 1 if results[0][2]&0x80 else 0 112 | stereoflag = "stereo" if sf else "mono" 113 | return stereoflag 114 | 115 | def getTuned(self): 116 | results = self.bus.transaction( 117 | reading(self.add, 5) 118 | ) 119 | elem=results[0][0] 120 | print("0 bits", int(get_bit(elem,0)), int(get_bit(elem,1)), int(get_bit(elem,2)), int(get_bit(elem,3)), int(get_bit(elem,4)), int(get_bit(elem,5)), int(get_bit(elem,6)),int(get_bit(elem,7))) 121 | 122 | elem=results[0][1] 123 | print("1 bits", int(get_bit(elem,0)), int(get_bit(elem,1)), int(get_bit(elem,2)), int(get_bit(elem,3)), int(get_bit(elem,4)), int(get_bit(elem,5)), int(get_bit(elem,6)),int(get_bit(elem,7))) 124 | 125 | elem=results[0][2] 126 | print("2 bits", int(get_bit(elem,0)), int(get_bit(elem,1)), int(get_bit(elem,2)), int(get_bit(elem,3)), int(get_bit(elem,4)), int(get_bit(elem,5)), int(get_bit(elem,6)),int(get_bit(elem,7))) 127 | 128 | elem=results[0][3] 129 | print("3 bits", int(get_bit(elem,0)), int(get_bit(elem,1)), int(get_bit(elem,2)), int(get_bit(elem,3)), int(get_bit(elem,4)), int(get_bit(elem,5)), int(get_bit(elem,6)), int(get_bit(elem,7))) 130 | 131 | 132 | 133 | 134 | return int(get_bit(elem,7)) 135 | 136 | 137 | 138 | def calculateFrequency(self): 139 | """calculate the station frequency based upon the upper and lower bits read from the device""" 140 | repeat = 0 141 | f =0.0 142 | with i2clib.I2CMaster() as b: 143 | results = b.transaction( 144 | reading(self.add, 5) 145 | ) 146 | 147 | uF = results[0][0]&0x3F 148 | lF = results[0][1] 149 | 150 | #good formula 151 | current_freq = round((float(round(int(((int(uF)<<8)+int(lF))*cof/4-22500)/100000)/10)-.2)*10)/10 152 | return current_freq 153 | 154 | 155 | 156 | #script to get ready 157 | def getReady(self): 158 | 159 | readyFlag = 0 160 | i = False 161 | attempt = 0 162 | results=[] 163 | standbyFlag = 0 164 | sys.stdout.flush() 165 | time.sleep(0.1) 166 | print("Getting ready ", end ="") 167 | while (i==False): 168 | results = self.bus.transaction( 169 | reading(self.add, 5) 170 | ) 171 | 172 | readyFlag = 1 if (results[0][0]&0x80)==128 else 0 173 | standbyFlag = 1 if (results[0][3]&0x40)!=319 else 0 174 | 175 | sys.stdout.flush() 176 | time.sleep(0.1) 177 | print(".", end = "") 178 | i=standbyFlag*readyFlag 179 | attempt+=1 180 | if(attempt>20): 181 | break 182 | time.sleep(0.2) 183 | 184 | if(i==True): 185 | print("Ready! (",attempt,")") 186 | return True 187 | # print("Raw output ", results[0]) 188 | else: 189 | self.i2c.read_byte(self.add) 190 | print("Not ready! (", attempt, ")") 191 | return False 192 | 193 | def writeFrequency(self,f, mute, direction): 194 | freq = f # desired frequency in MHz (at 101.1 popular music station in Melbourne) 195 | #cof = 32768 196 | i=False 197 | attempt = 0 198 | # Frequency distribution for two bytes (according to the data sheet) 199 | freq14bit = int (4 * (freq * 1000000 + 225000) / cof) 200 | freqH = freq14bit >>8 201 | freqL = freq14bit & 0xFF 202 | 203 | self.muteFlag = mute 204 | 205 | data = [0 for i in range(4)] 206 | # Descriptions of individual bits in a byte - viz. catalog sheets 207 | if(mute==0): 208 | init = freqH&0x3F# freqH # 1.byte (MUTE bit; Frequency H) // MUTE is 0x80 disable mute and search mode & 0x3F 209 | elif(mute==1): 210 | init = freqH&0x7F #search mode 211 | elif(mute==2): 212 | init = freqH&0x80 #mute both channels 213 | data[0] = freqL # 2.byte (frequency L) 214 | if(mute==0 and direction==1): 215 | data[1] = 0b10010000 # 3.byte (SUD; SSL1, SSL2; HLSI, MS, MR, ML; SWP1) 216 | elif(mute==0 and direction==0): 217 | data[1] = 0b00010000 218 | else: 219 | data[1] = 0b00011110 #mute L & R during scanning 220 | if(mute==0): 221 | data[2] = 0b00010000 # 4.byte (SWP2; STBY, BL; XTAL; smut; HCC, SNC, SI) 222 | else: 223 | data[2] = 0b00011111 224 | data[3] = 0b00000000 # 5.byte (PLREFF; DTC; 0; 0; 0; 0; 0; 0) 225 | 226 | #data[1]=0xB0; #3 byte (0xB0): high side LO injection is on,. 227 | #data[2]=0x10; #4 byte (0x10) : Xtal is 32.768 kHz 228 | #data[3]=0x00; #5 byte0x00) 229 | 230 | while (i==False): 231 | try: 232 | self.i2c.write_i2c_block_data (self.add, init, data) # Setting a new frequency to the circuit 233 | except IOError as e : 234 | i = False 235 | attempt +=1 236 | self.reset() #error prevention 237 | if attempt > 100000: 238 | break 239 | except Exception as e: 240 | print("I/O error: {0}".format(e)) 241 | self.reset() 242 | else: 243 | i = True 244 | 245 | 246 | def scan(self,direction): 247 | i=False 248 | fadd = 0 249 | softMute = 0 250 | while (i==False): 251 | if(direction==1): 252 | fadd=0.1 253 | else: 254 | fadd=-0.1 255 | #get current frequency, more accurately by averaging 2 method results 256 | self.freq = round((self.calculateFrequency()+self.getFreq())/2,2) 257 | if(self.freq<87.5): 258 | self.freq=108 259 | elif(self.freq>107.9): 260 | self.freq=87.5 261 | self.writeFrequency(self.freq+fadd,1,direction) 262 | 263 | #give time to finish writing, and then read status 264 | time.sleep(0.03) 265 | results = self.bus.transaction( 266 | reading(self.add, 5) 267 | ) 268 | self.freq = round((self.calculateFrequency()+self.getFreq())/2,2) #read again 269 | 270 | softMute = results[0][3]&0x08 271 | standbyFlag = 0 if results[0][3]&0x40 else 1 272 | self.signal = results[0][3]>>4 273 | self.stereoFlag = self.getStereoFlag() 274 | self.IFcounter = results[0][2]&0x7F 275 | self.readyFlag = 1 if results[0][3]&0x80 else 0 276 | 277 | 278 | f = open('telek.txt', 'w') 279 | f.write(str(self.freq)+"\n") 280 | # self.ws.send(self.freq) 281 | # self.serversocket.sendall(str(self.freq).encode('UTF-8')) 282 | print("Before tuning", self.getTuned()) 283 | #tune into station that has strong signal only 284 | if(self.readyFlag): 285 | print("Frequency tuned:",self.freq , "FM (Strong",self.stereoFlag,"signal:",self.signal,")") 286 | else: 287 | print("Station skipped:",self.freq , "FM (Weak",self.stereoFlag,"signal:",self.signal,")") 288 | i=self.readyFlag 289 | self.writeFrequency(self.freq ,0,direction) 290 | 291 | self.readyFlag = self.getTuned() 292 | print("After tuning:", self.readyFlag) 293 | 294 | def off(self): 295 | print("Radio off") 296 | self.writeFrequency(self.calculateFrequency(), 2,0) 297 | #a = self.getMute() 298 | a = self.getTuned() 299 | print("Radio off",a) 300 | return ("radio off") 301 | 302 | def mute(self): 303 | if(self.muteFlag): 304 | self.writeFrequency(self.calculateFrequency(), 0,0) 305 | print("unmute") 306 | else: 307 | self.writeFrequency(self.calculateFrequency(), 1,0) 308 | print("mute") 309 | return ("radio muted") 310 | 311 | 312 | def test(self): 313 | print("Testing mode") 314 | print("Scanning up...") 315 | self.scan(1) 316 | print("Listening for 10 seconds") 317 | time.sleep(10) 318 | print("Scanning down...") 319 | self.scan(0) 320 | print("Listening for 10 seconds") 321 | time.sleep(10) 322 | print("done") 323 | 324 | def info(self): 325 | data ={} 326 | data['freq'] = str(self.freq) 327 | data['level'] = self.signal 328 | data['stereo'] = str(self.stereoFlag) 329 | data['tuned'] = self.readyFlag 330 | data['mute'] = self.muteFlag 331 | 332 | print(data) 333 | return data 334 | #sample usage below: 335 | #radio = tea5767() 336 | #radio.scan(1) 337 | #time.sleep(15) 338 | #radio.scan(1) 339 | #time.sleep(15) 340 | #radio.scan(0) 341 | #time.sleep(15) 342 | #radio.scan(0) 343 | #time.sleep(15) 344 | #radio.off() 345 | 346 | -------------------------------------------------------------------------------- /telek.txt: -------------------------------------------------------------------------------- 1 | 89.9 2 | -------------------------------------------------------------------------------- /websocket-other.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | A partial implementation of RFC 6455 5 | http://tools.ietf.org/pdf/rfc6455.pdf 6 | Brian Thorne 2012 7 | 8 | TODO: 9 | - support long messages and multipacket frames 10 | - closing handshake 11 | - ping/pong 12 | """ 13 | 14 | import socket 15 | import threading 16 | import time 17 | import base64 18 | import hashlib 19 | import textwrap 20 | import select 21 | 22 | # for python2: 23 | import struct 24 | 25 | def calculate_websocket_hash(key): 26 | magic_websocket_string = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11" 27 | result_string = key + magic_websocket_string 28 | sha1_digest = hashlib.sha1(result_string).digest() 29 | response_data = base64.encodestring(sha1_digest).strip() 30 | response_string = response_data.decode('utf8') 31 | return response_string 32 | 33 | def is_bit_set(int_type, offset): 34 | """ 35 | >>> is_bit_set(1, 0) 36 | True 37 | >>> is_bit_set(2, 0) 38 | False 39 | >>> is_bit_set(0xFF, 2) 40 | True 41 | 42 | """ 43 | mask = 1 << offset 44 | return not 0 == (int_type & mask) 45 | 46 | def set_bit(int_type, offset): 47 | """ 48 | >>> set_bit(2, 0) 49 | 3 50 | """ 51 | return int_type | (1 << offset) 52 | 53 | def bytes_to_int(data): 54 | """Convert a bytes/str/list to int. 55 | 56 | >>> bytes_to_int(b"a0") 57 | 24880 58 | 59 | Passed a list with null bytes: 60 | 61 | >>> bytes_to_int([97, 48]) 62 | 24880 63 | 64 | Passed a string: 65 | 66 | >>> bytes_to_int("a") 67 | 97 68 | """ 69 | # note big-endian is the standard network byte order 70 | try: 71 | return int.from_bytes(data, byteorder='big') 72 | except: 73 | # PYTHON2 HACK... 74 | if type(data) == str: 75 | return sum(ord(c) << (i * 8) for i, c in enumerate(data[::-1])) 76 | else: 77 | return sum(c << (i * 8) for i, c in enumerate(data[::-1])) 78 | 79 | 80 | def int_to_bytes(number, bytesize): 81 | """Convert an integer to a bytearray. 82 | 83 | The integer is represented as an array of bytesize. An OverflowError 84 | is raised if the integer is not representable with the given number 85 | of bytes. 86 | 87 | >>> int_to_bytes(97, 1) 88 | bytearray(b'a') 89 | 90 | >>> int_to_bytes(159487778, 2) 91 | Traceback (most recent call last): 92 | ... 93 | OverflowError: Need more bytes to represent that number 94 | 95 | >>> int_to_bytes(159487778, 4) == bytearray([9, 129, 151, 34]) 96 | True 97 | 98 | """ 99 | try: 100 | return bytearray(number.to_bytes(bytesize, byteorder='big')) 101 | except: 102 | # PYTHON2 HACK... 103 | d = bytearray(bytesize) 104 | # Use big endian byteorder 105 | fmt = '!' + {1: 'b', 2: 'H', 4: 'L', 8: 'Q'}[bytesize] 106 | try: 107 | struct.pack_into(fmt, d, 0, number) 108 | except: 109 | raise OverflowError("Need more bytes to represent that number") 110 | return d 111 | 112 | 113 | def pack(data): 114 | """pack bytes for sending to client""" 115 | frame_head = bytearray(2) 116 | 117 | # set final fragment 118 | frame_head[0] = set_bit(frame_head[0], 7) 119 | 120 | # set opcode 1 = text 121 | frame_head[0] = set_bit(frame_head[0], 0) 122 | 123 | # payload length 124 | if len(data) < 126: 125 | frame_head[1] = len(data) 126 | elif len(data) < ((2**16) - 1): 127 | # First byte must be set to 126 to indicate the following 2 bytes 128 | # interpreted as a 16-bit unsigned integer are the payload length 129 | frame_head[1] = 126 130 | frame_head += int_to_bytes(len(data), 2) 131 | elif len(data) < (2**64) -1: 132 | # Use 8 bytes to encode the data length 133 | # First byte must be set to 127 134 | frame_head[1] = 127 135 | frame_head += int_to_bytes(len(data), 8) 136 | 137 | # add data 138 | frame = frame_head + data.encode('utf-8') 139 | #print(list(hex(b) for b in frame)) 140 | return frame 141 | 142 | def receive(s): 143 | """blocking call to receive data from client""" 144 | 145 | # read the first two bytes 146 | frame_head = s.recv(2) 147 | 148 | # On severed connection 149 | if frame_head == '' or frame_head == b'': 150 | return 151 | 152 | # PYTHON2 HACK 153 | frame_head = bytearray(frame_head) 154 | 155 | # very first bit indicates if this is the final fragment 156 | #print("final fragment: ", is_bit_set(frame_head[0], 7)) 157 | 158 | # bits 4-7 are the opcode (0x01 -> text) 159 | #print("opcode: ", frame_head[0] & 0x0f) 160 | 161 | # mask bit, from client will ALWAYS be 1 162 | #assert is_bit_set(frame_head[1], 7) 163 | 164 | # length of payload 165 | # 7 bits, or 16 bits, 64 bits 166 | payload_length = frame_head[1] & 0x7F 167 | if payload_length == 126: 168 | raw = s.recv(2) 169 | payload_length = bytes_to_int(raw) 170 | elif payload_length == 127: 171 | raw = s.recv(8) 172 | payload_length = bytes_to_int(raw) 173 | #print('Payload is {} bytes'.format(payload_length)) 174 | 175 | """masking key 176 | All frames sent from the client to the server are masked by a 177 | 32-bit nounce value that is contained within the frame 178 | """ 179 | # PYTHON2 HACK 180 | masking_key = bytearray(s.recv(4)) 181 | #print("mask: ", masking_key, bytes_to_int(masking_key)) 182 | 183 | # finally get the payload data: 184 | bytes_received = 0 185 | masked_data_in = bytearray(payload_length) 186 | while bytes_received < payload_length: 187 | data_in = bytearray(s.recv(payload_length)) 188 | #print "Received {} bytes".format(len(data_in)) 189 | masked_data_in[bytes_received:bytes_received+len(data_in)] = data_in 190 | 191 | bytes_received += len(data_in) 192 | #print "Done received {} bytes".format(len(masked_data_in)) 193 | 194 | data = bytearray(payload_length) 195 | 196 | # The ith byte is the XOR of byte i of the data with 197 | # masking_key[i % 4] 198 | for i, b in enumerate(masked_data_in): 199 | data[i] = b ^ masking_key[i%4] 200 | 201 | return data 202 | 203 | class Websocket(object): 204 | """ 205 | 206 | """ 207 | 208 | def __init__(self, port, new_client_callback=None): 209 | self.port = port 210 | self.callback = new_client_callback 211 | 212 | 213 | def serve_forever(self, end=None): 214 | """ 215 | end is an optional event to trigger server shutdown 216 | """ 217 | self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 218 | 219 | self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 220 | self.s.bind(('', self.port)) 221 | self.s.listen(1) 222 | 223 | # because socket.accept is blocking we use select for its timeout 224 | # so every second we check if "forever" is over. 225 | 226 | while True: 227 | r, w, e = select.select((self.s,), (), (), 1) 228 | for l in r: 229 | t, address = self.s.accept() 230 | print("Accepting connection from {}:{}".format(*address)) 231 | threading.Thread(target=self.handle_connection, args = (t, )).start() 232 | else: 233 | # should we quit? 234 | if end is not None and end.is_set(): 235 | return 236 | 237 | 238 | def transmit(self, msg_str): 239 | self.s.send(pack(msg_str)) 240 | 241 | def handle_connection(self, s): 242 | client_request = s.recv(4096) 243 | key = None 244 | # get to the key 245 | for line in client_request.splitlines(): 246 | #print(line.strip()) 247 | if b'Sec-WebSocket-Key:' in line: 248 | key = line.split(b': ')[1] 249 | break 250 | if key is None: 251 | raise IOError("Couldn't find the key?\n\n", client_request) 252 | 253 | response_string = calculate_websocket_hash(key) 254 | 255 | header = '''HTTP/1.1 101 Switching Protocols\r 256 | Upgrade: websocket\r 257 | Connection: Upgrade\r 258 | Sec-WebSocket-Accept: {}\r 259 | \r 260 | '''.format(response_string) 261 | 262 | s.send(header.encode()) 263 | 264 | if self.callback is not None: 265 | self.callback(s) 266 | 267 | 268 | def __del__(self): 269 | self.s.close() 270 | 271 | 272 | if __name__ == "__main__": 273 | import doctest 274 | doctest.testmod() -------------------------------------------------------------------------------- /workfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinuxCircle/tea5767/e915e11c7324a702e850768681c9a7e3e253b5c1/workfile -------------------------------------------------------------------------------- /wstester.py: -------------------------------------------------------------------------------- 1 | import websocket 2 | try: 3 | import thread 4 | except ImportError: #TODO use Threading instead of _thread in python3 5 | import _thread as thread 6 | import time 7 | import sys 8 | 9 | 10 | def on_message(ws, message): 11 | print(message) 12 | 13 | 14 | def on_error(ws, error): 15 | print(error) 16 | 17 | 18 | def on_close(ws): 19 | print("### closed ###") 20 | 21 | 22 | def on_open(ws): 23 | def run(*args): 24 | for i in range(3): 25 | # send the message, then wait 26 | # so thread doesnt exit and socket 27 | # isnt closed 28 | ws.send("Hello %d" % i) 29 | time.sleep(1) 30 | 31 | time.sleep(1) 32 | ws.close() 33 | print("Thread terminating...") 34 | 35 | thread.start_new_thread(run, ()) 36 | 37 | if __name__ == "__main__": 38 | websocket.enableTrace(True) 39 | if len(sys.argv) < 2: 40 | host = "ws://echo.websocket.org/" 41 | else: 42 | host = sys.argv[1] 43 | ws = websocket.WebSocketApp(host, 44 | on_message = on_message, 45 | on_error = on_error, 46 | on_close = on_close) 47 | ws.on_open = on_open 48 | ws.run_forever() 49 | 50 | 51 | 52 | --------------------------------------------------------------------------------