Using pyUSB to read data from ELM327 OBDII to USB device - python

I am having problems using the pyUSB library to read data from an ELM327 OBDII to USB device. I know that I need to write a command to the device on the write endpoint and read the received data back on the read endpoint. It doesn't seem to want to work for me though.
I wrote my own class obdusb for this:
import usb.core
class obdusb:
def __init__(self,_vend,_prod):
'''Handle to USB device'''
self.idVendor = _vend
self.idProduct = _prod
self._dev = usb.core.find(idVendor=_vend, idProduct=_prod)
return None
def GetDevice(self):
'''Must be called after constructor'''
return self._dev
def SetupEndpoint(self):
'''Must be called after constructor'''
try:
self._dev.set_configuration()
except usb.core.USBError as e:
sys.exit("Could not set configuration")
self._endpointWrite = self._dev[0][(0,0)][1]
self._endpointRead = self._dev[0][(0,0)][0]
#Resetting device and setting vehicle protocol (Auto)
#20ms is required as a delay between each written command
#ATZ resets device
self._dev.write(self._endpointWrite.bEndpointAddress,'ATZ',0)
sleep(0.002)
#ATSP 0 should set vehicle protocol automatically
self._dev.write(self._endpointWrite.bEndpointAddress,'ATSP 0',0)
sleep(0.02)
return self._endpointRead
def GetData(self,strCommand):
data = []
self._dev.write(self._endpintWrite.bEndpointAddress,strCommand,0)
sleep(0.002)
data = self._dev.read(self._endpointRead.bEndpointAddress, self._endpointRead.wMaxPacketSize)
return data
So I then use this class and call the GetData method using this code:
import obdusb
#Setting up library,device and endpoint
lib = obdusb.obdusb(0x0403,0x6001)
myDev = lib.GetDevice()
endp = lib.SetupEndpoint()
#Testing GetData function with random OBD command
#0902 is VIN number of vehicle being requested
dataArr = lib.GetData('0902')
PrintResults(dataArr)
raw_input("Press any key")
def PrintResults(arr):
size = len(arr)
print "Data currently in buffer:"
for i in range(0,size):
print "[" + str(i) + "]: " + str(make[i])
This only ever prints the numbers 1 and 60 from [0] and [1] element in the array. No other data has been return from the command. This is the case whether the device is connected to a car or not. I don't know what these 2 pieces of information are. I am expecting it to return a string of hexadecimal numbers. Does anyone know what I am doing wrong here?

If you don't use ATST or ATAT, you have to expect a timeout of 200ms at start, between every write/read combination.
Are you sending a '\r' after each command? It looks like you don't, so it's forever waiting for a Carriage Return.
And a hint: test with 010D or 010C or something. 09xx might be difficult what to expect.
UPDATE:
You can do that both ways. As long as you 'seperate' each command with a carriage return.
http://elmelectronics.com/ELM327/AT_Commands.pdf
http://elmelectronics.com/DSheets/ELM327DS.pdf (Expanded list).
That command list was quite usefull to me.
ATAT can be used to the adjust the timeout.
When you send 010D, the ELM chip will wait normally 200 ms, to get all possible reactions. Sometimes you can get more returns, so it waits the 200 ms.
What you also can do, and it's a mystery as only scantools tend to implement this:
'010D1/r'
The 1 after the command, specifies the ELM should report back, when it has 1 reply from the bus. So it reduces the delay quite efficiently, at the cost of not able to get more values from the address '010D'. (Which is speed!)
Sorry for my english, I hope send you in the right direction.

Related

Examining file on different node (different IP address), same network - possible?

I have a small group of Raspberry Pis, all on the same local network (192.168.1.2xx) All are running Python 3.7.3, one (R Pi CM3) on Raspbian Buster, the other (R Pi 4B 8gig) on Raspberry Pi OS 64.
I have a file on one device (the Pi 4B), located at /tmp/speech.wav, that is generated on the fly, real-time:
192.168.1.201 - /tmp/speech.wav
I have a script that works well on that device, that tells me the play duration time of the .wav file in seconds:
import wave
import contextlib
def getPlayTime():
fname = '/tmp/speech.wav'
with contextlib.closing(wave.open(fname,'r')) as f:
frames = f.getnframes()
rate = f.getframerate()
duration = round(frames / float(rate), 2)
return duration
However - the node that needs to operate on that duration information is running on another node at 192.168.1.210. I cannot simply move the various files all to the same node as there is a LOT going on, things are where they are for a reason.
So what I need to know is how to alter my approach such that I can change the script reference to something like this pseudocode:
fname = '/tmp/speech.wav # 192.168.1.201'
Is such a thing possible? Searching the web it seems that I am up against millions of people looking for how to obtain IP addresses, fix multiple IP address issues, fix duplicate ip address issues... but I can't seem yet to find how to simply examine a file on a different ip address as I have described here. I have no network security restrictions, so any setting is up for consideration. Help would be much appreciated.
There are lots of possibilities, and it probably comes down to how often you need to check the duration, from how many clients, and how often the file changes and whether you have other information that you want to share between the nodes.
Here are some options:
set up an SMB (Samba) server on the Pi that has the WAV file and let the other nodes mount the filesystem and access the file as if it was local
set up an NFS server on the Pi that has the WAV file and let the other nodes mount the filesystem and access the file as if it was local
let other nodes use ssh to login and extract the duration, or scp to retrieve the file - see paramiko in Python
set up Redis on one node and throw the WAV file in there so anyone can get it - this is potentially attractive if you have lots of lists, arrays, strings, integers, hashes, queues or sets that you want to share between Raspberry Pis very fast. Example here.
Here is a very simple example of writing a sound track into Redis from one node (say Redis is on 192.168.0.200) and reading it back from any other. Of course, you may just want the writing node to write the duration in there rather than the whole track - which would be more efficient. Or you may want to store loads of other shared data or settings.
This is the writer:
#!/usr/bin/env python3
import redis
from pathlib import Path
host='192.168.1.200'
# Connect to Redis
r = redis.Redis(host)
# Load some music, or otherwise create it
music = Path('song.wav').read_bytes()
# Put music into Redis where others can see it
r.set("music",music)
And this is the reader:
#!/usr/bin/env python3
import redis
from pathlib import Path
host='192.168.1.200'
# Connect to Redis
r = redis.Redis(host)
# Retrieve music track from Redis
music = r.get("music")
print(f'{len(music)} bytes read from Redis')
Then, during testing, you may want to manually push a track into Redis from the Terminal:
redis-cli -x -h 192.168.0.200 set music < OtherTrack.wav
Or manually retrieve the track from Redis to a file:
redis-cli -h 192.168.0.200 get music > RetrievedFromRedis.wav
OK, this is what I finally settled on - and it works great. Using ZeroMQ for message passing, I have the function to get the playtime of the wav, and another gathers data about the speech about to be spoken, then all that is sent to the motor core prior to sending the speech. The motor core handles the timing issues to sync the jaw to the speech. So, I'm not actually putting the code that generates the wav and also returns the length of the wav playback time onto the node that ultimately makes use of it, but it turns out that message passing is fast enough so there is plenty of time space to receive, process and implement the motion control to match the speech perfectly. Posting this here in case it's helpful for folks in the future working on similar issues.
import time
import zmq
import os
import re
import wave
import contextlib
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://*:5555") #Listens for speech to output
print("Connecting to Motor Control")
jawCmd = context.socket(zmq.PUB)
jawCmd.connect("tcp://192.168.1.210:5554") #Sends to MotorFunctions for Jaw Movement
def getPlayTime(): # Checks to see if current file duration has changed
fname = '/tmp/speech.wav' # and if yes, sends new duration
with contextlib.closing(wave.open(fname,'r')) as f:
frames = f.getnframes()
rate = f.getframerate()
duration = round(frames / float(rate), 3)
speakTime = str(duration)
return speakTime
def set_voice(V,T):
T2 = '"' + T + '"'
audioFile = "/tmp/speech.wav" # /tmp set as tmpfs, or RAMDISK to reduce SD Card write ops
if V == "A":
voice = "Allison"
elif V == "B":
voice = "Belle"
elif V == "C":
voice = "Callie"
elif V == "D":
voice = "Dallas"
elif V == "V":
voice = "David"
else:
voice = "Belle"
os.system("swift -n " + voice + " -o " + audioFile + " " +T2) # Record audio
tailTrim = .5 # Calculate Jaw Timing
speakTime = eval(getPlayTime()) # Start by getting playlength
speakTime = round((speakTime - tailTrim), 2) # Chop .5 s for trailing silence
wordList = T.split()
jawString = []
for index in range(len(wordList)):
wordLen = len(wordList[index])
jawString.append(wordLen)
jawString = str(jawString)
speakTime = str(speakTime)
jawString = speakTime + "|" + jawString # 3.456|[4, 2, 7, 4, 2, 9, 3, 4, 3, 6] - will split on "|"
jawCmd.send_string(jawString) # Send Jaw Operating Sequence
os.system("aplay " + audioFile) # Play audio
pronunciationDict = {'teh':'the','process':'prawcess','Maeve':'Mayve','Mariposa':'May-reeposah','Lila':'Lala','Trump':'Ass hole'}
def adjustResponse(response): # Adjusts spellings in output string to create better speech output.
for key, value in pronunciationDict.items():
if key in response or key.lower() in response:
response = re.sub(key, value, response, flags=re.I)
return response
SpeakText="Speech center connected and online."
set_voice(V,SpeakText) # Cepstral Voices: A = Allison; B = Belle; C = Callie; D = Dallas; V = David;
while True:
SpeakText = socket.recv().decode('utf-8') # .decode gets rid of the b' in front of the string
SpeakTextX = adjustResponse(SpeakText) # Run the string through the pronunciation dictionary
print("SpeakText = ",SpeakTextX)
set_voice(V,SpeakTextX)
print("Received request: %s" % SpeakTextX)
socket.send_string(str(SpeakTextX)) # Send data back to source for confirmation

nfcpy: How to get on-release event correctly with NFCPY?

I try to listen to different RFID ID cards with a ACR122 reader and the nfcpy python library.
I would like to have the card's ID when the user connect it (without recognized it over and over) and get an event when user release it. Ideally in a loop, in order to listen to the next card when the user take his card away.
Below is my code, but the on-release event is fired even if the card is still on the reader. What is the correct way to
Get on-connect without recognizing over and over ?
Get on-release when user the card is away ?
import nfc
def on_startup(targets):
return targets
def on_connect(tag):
uid = str(tag.identifier).encode("hex").upper()
print(uid)
return True
def on_release(tag):
print('Released')
return tag
rdwr_options = {
'on-startup': on_startup,
'on-connect': on_connect,
'on-release': on_release,
'beep-on-connect': False,
}
with nfc.ContactlessFrontend('usb') as clf:
tag = clf.connect(rdwr=rdwr_options)
You might need to set an interval in your ContactlessFrontend config. Try this example:
import nfc
import ndef
tags = set()
rec = ndef.UriRecord("https://google.com")
def on_connect(tag):
if tag.identifier not in tags:
tags.add(tag.identifier)
fmt = tag.format()
if fmt is None:
print("Tag cannot be formatted (not supported).")
elif fmt is False:
print("Tag failed to be formatted (for some reason).")
else:
tag.ndef.records = [rec]
if __name__ == "__main__":
clf = nfc.ContactlessFrontend()
if not clf.open('usb'):
raise RuntimeError("Failed to open NFC device.")
while True:
config = {
'interval': 0.35,
'on-connect': on_connect
}
ret = clf.connect(rdwr=config)
if ret is None:
pass
elif not ret:
print ("NFC connection terminated due to an exception.")
break
else:
pass
clf.close()
https://gist.github.com/henrycjc/c1632b2d1f210ae0ff33d860c7c2eb8f
This discussion helped me figuring out how to solve this.
When reading the docs (‘on-release’ : function(tag)) very carefully – yes, it took me some loops – it becomes apparent that on-release is called as soon as on-connect returns True.
This function is called when the presence check was run (the ‘on-connect’ function returned a true value) and determined that communication with the tag has become impossible, or when the ‘terminate’ function returned a true value. The tag object may be used for cleanup actions but not for communication.
It seems that on-release must not be understood in a physical way, but rather in a communicative way (Released from communications, you may now remove the card).
To solve this issue, one needs to determine whether a card is present after it connected (or more precisely, after it was released – more about that later). The following code does the trick:
import nfc
import time
import logging
import inspect
logging.basicConfig(format="[%(name)s:%(levelname).4s] %(message)s")
logging.getLogger().setLevel(logging.DEBUG)
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
def on_startup(targets):
logger.debug(inspect.currentframe().f_code.co_name)
for target in targets:
target.sensf_req = bytearray.fromhex("0012FC0000")
return targets
def on_discover(target):
logger.debug(inspect.currentframe().f_code.co_name)
logger.info(target)
return True
def on_connect(tag):
logger.debug(inspect.currentframe().f_code.co_name)
logger.info(tag)
return True
def on_release(tag):
logger.debug(inspect.currentframe().f_code.co_name)
# Loop while card is present
while True:
time.sleep(1)
if not clf.sense(*[nfc.clf.RemoteTarget(target) for target in rdwr_options["targets"]]):
logger.info("Card removed")
break
return True
rdwr_options = {
"targets": ("106A", "106B", "212F"),
"on-startup": on_startup,
"on-discover": on_discover, # Here just for completeness :)
"on-connect": on_connect,
"on-release": on_release,
}
if __name__ == "__main__":
logger.debug(inspect.currentframe().f_code.co_name)
with nfc.ContactlessFrontend() as clf:
if not clf.open("usb"):
raise RuntimeError("Failed to open NFC device.")
while True:
ret = clf.connect(rdwr=rdwr_options)
if not ret:
break
Now is later: If we wait for removal of the card during the on-connect state, we run into trouble as on-release expects to retrieve information from the card (tag argument), which it cannot get anymore as communication is not possible with the card removed.
PS: The above mentioned discussion reads that the behavior of on-release depends on the type of card one is using.
So, I have a need to register when a card is present and when it leaves again. I have some Type2 tags for this.
Given this code:
def connected(tag):
print(tag)
return True def released(tag):
print("Bye")
tag = clf.connect(rdwr={'on-connect': connected, 'on-release':
released})
I would expect it to echo out the Tag ID when I present it to the
reader, and the echo "Bye" once I remove it. This works as expected on
a Type4 tag I have..
I'm afraid those cards are Mifare Classic 1K not supported by nfcpy. It is possible to read the UID but any other command requires to first authenticate and use the Mifare Crypto scheme. This should be doable with NXP reader ICs [...]. And NXP has a good selection of NFC Forum compatible Type 2 Tags that work just great.

Why thread did not work on sniff() function on python scapy?

I have a code, i want to run the function cek_paket() and delete_paket() at the same time. My friend suggest me to use thread. So shortly, im using this to sniffing arp packet, and save the information (source, destination address,and the amount of packet arrived) into list in dictionary python. So sniff() function is the function by scapy that i use to sniff packet. After the packet arrive, it will return it to cek_paket() to save the information to jumlah_reply list. And i use delete_reply() to delete the first list(jumlah_reply[0]) every 3 second. I have no problem in cek_paket() function. The problem is, why the sniffing() function only works? But delete_reply function did not work?
from scapy.all import*
import thread
import time
jumlah_reply = []
def cek_paket(pkt):
if pkt[ARP].op ==2:
destinasi = str(pkt[ARP].pdst)
source = str(pkt[ARP].psrc)
dikirim = {'src':source,'dst':destinasi}
if len(jumlah_reply)==0:
dikirim['count']=1
jumlah_reply.append(dikirim)
found = True
else:
found=False
for itung in jumlah_reply:
if itung['src']==dikirim['src'] and itung['dst']==dikirim['dst']:
itung['count']+=1
found = True
break
if not found:
jumlah_reply.append(dikirim)
dikirim['count']=1
print("reply")
print(jumlah_reply)
print("--------------------------------")
def delete_paket():
if len(jumlah_reply) > 0:
del jumlah_reply[0]
print("*********************")
print (jumlah_reply)
print("**********************")
time.sleep(3)
def sniffing():
sniff(prn=cek_paket,filter="arp",store=0)
try:
thread.start_new_thread(sniffing())
thread.start_new_thread(delete_paket())
except:
print("error")
while 1:
pass
I was expecting the output:
When there is a ARP reply packet, the information will be added into the list, and 3 second after it, the first item will be deleted. but the actual output is
As you can see the information packet have added into the list but i've run the code for a minute there is no action from delete_reply() function. Why this is happen? is the sniff() function from scapy lock all the process?

asterisk ari calling stuck in ringing with python

I'm quite new with ARI scripting for Asterisk, and I've been trying to make some script to handle a 1 to 1 communication with ari-py in python. I've been following the example that provided in the asterisk wiki and so far so good. But when I try to create a call, the recipient always keep ringing, even if I have answered it. Is there something wrong with how I handle the call? Here's my script
def stasis_start_cb(self, channel, ev):
"""Handler for StasisStart event"""
chan = channel.get('channel')
chan.answer()
print "Channel %s has entered the application" % chan.json.get('name')
outgoing = client.channels.originate(endpoint="SIP/1002", extension='1002', callerId='Tes', app='channel-dump', appArgs='dialed')
I tried using OOP to simplify the function usage, are there anything wrong with that script? And here's another script trying to make a call by using a bridge:
def outgoing_call(self,channel):
try:
outgoing = client.channels.originate(endpoint="SIP/1002", app='channel-dump', appArgs='dialed')
except requests.HTTPError:
channel.hangup()
return
def outgoing_start(self, bri, channel):
channel.answer()
self.addChan(channel, bridge)
def stasis_start(self, channel, ev):
chan = channel.get('channel')
name = chan.json.get('name')
"""ars = ev.get('args')
if not ars:
print "Error: {} didn't provide any arguments!".format(name)
return
if ars and ars[0] != 'inbound':
return
if len(ars) != 2:
print "Error: {} didn't tell us who to dial".format(name)
chan.hangup()"""
print "Channel {} entered the application".format(name)
chan.ring()
self.outgoing_call(chan)
self.outgoing_start(bridge, chan)
Both the client is able to be added in the bridge, and I can make a call, but the problem still persist, the recipient keep saying they are ringing despite I have answered the call
Turns out, the problem is in here
def outgoing_call(self,channel):
try:
outgoing = client.channels.originate(endpoint="SIP/1002", app='channel-dump', appArgs='dialed')
except requests.HTTPError:
channel.hangup()
return
As the dialed number answer the call, they uses the same script, so they ended up calling themselves again. A simple if condition to make the dialed number not call to itself again is all that is needed

BLE subscribe to notification using gatttool or bluepy

I am writing a program using bluepy that listen for a characteristic sent by a bluetooth device. I can also use any library or language, the only constraint is to run on Linux and not in mobile environment (it seems is widely used only in mobile devices, no one use BLE with desktop).
Using bluepy I register the delegate and after trying to register for notification calling write('\x01\x00') as described in the bluetooth rfc.
But it doesn't work, any notification for the characteristic is received.
Maybe I am wrong in writing the message for subscribing.
Is there an error in the small snippet I wrote? Thank you so much.
class MyDelegate(btle.DefaultDelegate):
def __init__(self, hndl):
btle.DefaultDelegate.__init__(self)
self.hndl=hndl;
def handleNotification(self, cHandle, data):
if (cHandle==self.hndl):
val = binascii.b2a_hex(data)
val = binascii.unhexlify(val)
val = struct.unpack('f', val)[0]
print str(val) + " deg C"
p = btle.Peripheral("xx:xx:xx:xx", "random")
try:
srvs = (p.getServices());
chs=srvs[2].getCharacteristics();
ch=chs[1];
print(str(ch)+str(ch.propertiesToString()));
p.setDelegate(MyDelegate(ch.getHandle()));
# Setup to turn notifications on, e.g.
ch.write("\x01\x00");
# Main loop --------
while True:
if p.waitForNotifications(1.0):
continue
print "Waiting..."
finally:
p.disconnect();
I was struggling with this myself, and jgrant's comment really helped. I'd like to share my solution, if it could help anyone.
Note that I needed indication, hence the x02 rather than x01.
If it were possible to read the descriptors using bluepy, I would do that, but it doesn't seem to work (bluepy v 1.0.5). The method in the service class appears to be missing, and the method in the peripheral class gets stuck when I try to use it.
from bluepy import btle
class MyDelegate(btle.DefaultDelegate):
def __init__(self):
btle.DefaultDelegate.__init__(self)
def handleNotification(self, cHandle, data):
print("A notification was received: %s" %data)
p = btle.Peripheral(<MAC ADDRESS>, btle.ADDR_TYPE_RANDOM)
p.setDelegate( MyDelegate() )
# Setup to turn notifications on, e.g.
svc = p.getServiceByUUID( <UUID> )
ch = svc.getCharacteristics()[0]
print(ch.valHandle)
p.writeCharacteristic(ch.valHandle+1, "\x02\x00")
while True:
if p.waitForNotifications(1.0):
# handleNotification() was called
continue
print("Waiting...")
# Perhaps do something else here
It looks like the problem is that you're trying to write \x01\x00 to the characteristic itself. You need to write it to the Client Characteristic Configuration descriptor that proceeds it (0x2902). The handle is likely 1 greater than the characteristic (but you may want to confirm by reading the descriptors).
ch=chs[1]
cccd = ch.valHandle + 1
cccd.write("\x01\x00")
What was confusing for me was that in https://ianharvey.github.io/bluepy-doc/notifications.html
the part that enabled the notifications was in comments, so it didn't look obligatory to me.
the bare minimum (given you know the MAC-adress already an you included everything and declared the Delegateclass) for me is
p1 = Peripheral(<MAC>)
ch1 = p1.getCharacteristics()[3]
p1.setDelegate(MyDelegate())
p1.writeCharacteristic(ch1.valHandle + 1, b"\x01\x00")
Note that I already knew I wanted to get notifications from characteristic#3.
Also, without the 'b'-bytesprefix infront of "\x0\x00", it wouldn't work for me.
bluepy classes docs and samples are crazy, and not complete. To get more details, just checkout bluepy source (it is not big and easy to read)
But, as starting point you can use this notifications code sample, working with from Heart Rate Service (tested on bluepy 1.3.0)
Don't forget to replace device MAC to your own in Peripheral!
from bluepy import btle
from bluepy.btle import AssignedNumbers
import binascii
class MyDelegate(btle.DefaultDelegate):
def __init__(self, handle):
btle.DefaultDelegate.__init__(self)
self.handle = handle
print "Created delegate for handle", self.handle
# ... more initialise here
def handleNotification(self, cHandle, data):
if(cHandle == self.handle):
print "handleNotification for handle: ", cHandle, "; Raw data: ", binascii.b2a_hex(data)
#Found somewhere. Not tested is this working, but leave here as decode example
#val = binascii.b2a_hex(data)
#val = binascii.unhexlify(val)
#val = struct.unpack('f', val)[0]
#print str(val) + " deg C"
print "Connecting..."
dev = btle.Peripheral("c8:2b:96:a3:d4:76")
try:
print "Device services list:"
for svc in dev.services:
print str(svc)
HRService = dev.getServiceByUUID(AssignedNumbers.heartRate)
print "HRService", HRService
print "HRService characteristics list: "
for char in HRService.getCharacteristics():
print "HRService char[", char.getHandle(), "]: ", char
HRMeasurementChar = HRService.getCharacteristics(AssignedNumbers.heart_rate_measurement)[0] #Notice! Check is characteristic found before usage in production code!
print "HRMeasurementChar", HRMeasurementChar, HRMeasurementChar.propertiesToString();
# Assign delegate to target characteristic
dev.setDelegate(MyDelegate(HRMeasurementChar.getHandle()));
# We need to write into org.bluetooth.descriptor.gatt.client_characteristic_configuration descriptor to enabe notifications
# to do so, we must get this descriptor from characteristic first
# more details you can find in bluepy source (def getDescriptors(self, forUUID=None, hndEnd=0xFFFF))
desc = HRMeasurementChar.getDescriptors(AssignedNumbers.client_characteristic_configuration);
print "desc", desc
print "Writing \"notification\" flag to descriptor with handle: ", desc[0].handle
dev.writeCharacteristic(desc[0].handle, b"\x01\x00")# Notice! Do not use [0] in production. Check is descriptor found first!
print "Waiting for notifications..."
while True:
if dev.waitForNotifications(1.0):
# handleNotification() was called
continue
finally:
dev.disconnect();

Categories

Resources