pemessier / SoulEVSpy

Apache License 2.0
9 stars 8 forks source link

Design overview? #3

Open langemand opened 7 years ago

langemand commented 7 years ago

I finally have some time for working on this project.

I am able to build the app, run it on my Android phone, and connect to the (replaced) KW-902 bluetooth in the Soul EV. As far as I can see in the code, that's it; the current version of the source makes no attempt at sending any OBD-commands.

I would like to start debugging the communication. However, being new to all of Android development, Java, bluetooth and OBD; I face a steep learning curve.

I have been browsing around in the code, but I am uncertain how the design for the OBD command sending and response parsing is envisioned. So, as a first goal, I will see if I can get one command and response to work, by shortcutting as much as needed, for the first communication attempts. Later, when I understand how the protocol works, and hopefully you have shared your thoughts on OBD communication design, I can make the right structures for handling commands and replies in an orderly fashion.

You mentioned that you had a prototype that crashed when receiving a reply. Is this something you can easily share? So I can see how you got it to send that first command?

langemand commented 7 years ago

I got the first command to succeed: The VIN. It isn't pretty, I made lots of shortcuts and tentative hacks, so the code is not structured properly. I pushed the commit to my clone, so I don't mess up your work: https://github.com/langemand/SoulEVSpy, in case you want to take a look.

pemessier commented 7 years ago

Hello! Sorry for the late answer!

I will explain the general idea of the code structure (as far as I remember..!).

The package org.hexpresso.elm327 defines a module able to communicate with a ELM327 device. I only implemented the Bluetooth communication as a service (io.bluetooth), similarly one could implement a Wi-Fi device as another service extending the Service class.

One starts by creating a Service instance, the BluetoothService for example. The client can either connect or disconnect the ELM327 service. The service has also a state, which could be displayed to the end user.

The Service has a ELM327 Protocol class. It has 2 threads. One thread is used to send Message objects to the ELM327 device (executeMesssages()). The other thread is then used to process (decode) the received messages from the ELM327 device. The first thread is used so that the UI could send requests without blocking. The other thread is used to notify the UI (various parts of the UI could subscribe to specific messages). In oher words, the UI contains few intelligence to decode the messages.

The Message class is the object that is used by the protocol. It contains a state, a unique identifier and a command. It is created by the Protocol class (addCommand()).

A Command object can be executed by the first thread of the Protocol class. It means it will be sent to the ELM327 device and the output will be received by the object (AbstractCommand.execute()). This could be a valid response or an error code. The key here is that only the raw bytes are received. The actual response will be decoded (if needed) by the client by calling getResponse(), creating a Response object.

AbstractCommand the base class for most commands. AbstractMultiCommand is a base class for a list of commands to be executed to get the whole information (for example, the "2101/2102/2103/2104/2105" commands could be implemented as a multicommand).

Some generic ELM327 are implemented in the library. Most ELM327 commands are under commands.protocol. These are most used during initialization of the device upon connection. There is also a generic VIN command, as you found out. It is implemented in commands.general.VehicleIdentificationNumberCommand, and sends the "0902" command.

The Response object always contains the raw response string. You can also add objects called ResponseFilters to the Response objects. These act like generic text filters (hence the name) and can be chained together. Generic filters can be found commands.filters. For example, RegularExpressionResponseFilter can be used to match data with a regular expression. RemoveSpacesResponseFilter can be used to strip all spaces from the string.

Let's look at the VehicleIdentifierNumberCommand class. It will send the "09 02" command to the ELM device. It will then use a regular expression to filter out some data and only keep the VIN bytes. Finally it will remove the spaces in the string. When calling getValue(), the data will be decoded once and it will be returned. If the object is saved and used, the data won't be decoded and the value will be re-used.

As far as I remember, the org.hexpresso.obd is the older version of the system described above. It was developed when the data bytes from the 210x commands were being decoded in the Google Sheet by all folks. I think these classes should be deleted and the tests in org.hexpresso.soulevspy.obd should be migrated to the new system.

The derived classes of ObdMessageFilter under org.hexpresso.soulevspy.obd were designed for another mode of the ELM327. I don't know how to handle this properly however. One should put the ELM327 in "listen all CAN messages" mode and the data should be parsed. However, when I tried it with another Android app, I would get a buffer full and it stopped. Don't know if this will work, but the essence of the algorithms to decode the messages are there.

I think I'm done for tonight, hope this helps! :) I'll try to write more in a couple of days! Hope it helps a bit!

Cheers!

langemand commented 7 years ago

Hi again. I finally took the time to do something with this code. I now have the app repeatedly collecting battery data, ldc data, gps data and more, and storing the data on SD card. Yes, it handles filtered monitoring for CAN messages without buffer overflow :-) The UI sucks, but it will display some of the collected values, just to show it works. I am sorry for breaking some of your design principles, and for pushing all of this as one "big bang" commit. I guess you would prefer individual commits pushed to your repo. You can try it out, and get inspiration (or even pull everything, if you can live with the uglyness of my code, in your repo) from: https://github.com/langemand/SoulEVSpy

langemand commented 5 years ago

In my fork of the code, I have added a page displaying the nearest Chademo chargers, and routing link; very handy while driving. I have also added a "Demo" mode, so you can see data "recorded" by my car, while driving. This can help a new user decide whether a KW-902 should be ordered. Stability has also been improved a lot lately. For the latest APK, see: https://github.com/langemand/SoulEVSpy/blob/master/README.md