Ampy: Open source python display/interface project for ASI controllers

xenodius

10 kW
Joined
Sep 3, 2012
Messages
551
Location
Florida
I'm opening this thread in hopes of getting input from others who like ASI's controllers and are interested in a custom full-featured display. Once I finish my prototype, I would like to develop some popular requests so that others can benefit from the underlying work, and I would like to begin collecting those requests here. Eventually, this post will have a BOM, SD image, and .STL's for printing a case so anyone can build their own. EDIT: BOM, image, case on Git! 8)

EDIT: Updates here are in excruciating detail. The software is ready on git, including JBD bms integration. If you want a basic overview please look at the readme.
https://github.com/Xenodius/ASI_AmpyDisplay/tree/master

The problem: Generic displays like Kingmeter/Bafang don't display all the info I want. The amazing CycleAnalyst could replicate every function I want except for on-the-fly retuning of whole controller profiles, and would require an external shunt. This is my endgame zero-compromise bike, so, that's not quite good enough :lol: I really want to retune profiles... I have to be able to quickly change field weakening and phase current, if I want to have max power quickly available yet not sacrifice efficiency all the rest of the time when I don't need it. Currently there is no convenient way to do this for ASI but the capability is there. I also want trip functions so I can quickly ballpark my range and self-regulate, and have a trip limiter to guarantee I arrive at my next charge. I want all features fed by ASI's serial interface so I can use its internal shunt, and all associated sensors. It even has accelerometers that could be used to detect a crash and commit a dashcam recording!

Hardware: Raspberry Pi4 with a build of piCore, a fork of tinyCore linux that is exceptionally minimalist and runs entirely in memory for fast boot times, zero risk of SD corruption from abrupt power loss, and plenty of remaining overhead for development of other functions like a dashcam logger with auto-backup through local wifi, a lowjack tied into the controller antitheft and and a horn/alarm, etc. The Raspberry Pi Zero W is more than powerful enough for display functions only, but I'll be testing on a Pi4. For sunlight readability I've chosen a 7.8 inch 1872x1404 Waveshare e-ink display and capacitive touch overlay.

With the PyQT5 gui, the simple GUI app QT Designer can be used to arrange or style widgets as desired. Then, the command-line tool pyuic5 converts that .ui configuration to python that can be interpreted by the app. I will eventually make a few other .ui's configs for smaller displays, if there is interest.

The gui test;
aKdUrQc.png

Base GUI widgets:
  • Time of day
  • Battery SOC (by comparing pack voltage to Li-NMC SOC:voltage curve, not ASI's linear approximation)
  • Peak voltage drop over x seconds
  • Controller fault codes
  • Instantaneous wh/mi
  • Trip average wh/mi
  • Trip total wh or ah
  • Trip distance
  • Estimated range
  • Speed (mph default, analog gauge + text)
  • Power (kW, analog gauge + text)
  • Trip range slider (limit peak power to ensure a minimum range)
  • Profile selector (retune any number of controller variables, between three profiles. For example, to enable/disable field weakening or adjust phase current from a reasonable thermal limit, down to saturation limit, to optimize either efficiency or max power)
  • Digital assist level selector (If using torque sensor or PAS, allows you to control the digital assist level from the display)

Possible concerns:
  • piCore is an ultralight OS that is resilient and stable by operating entirely in memory and is thus read-only, but it will still require 7-10 seconds to boot. As well, the raspberry pi is a full-fledged computer, although it costs as much as an Arduino that's ~150 times slower and has no GPU. For those who don't want to sacrifice ~2 watts to computing power, or a few seconds to boot, and who don't need a large or high-resolution display, it should be possible to port the project from Python 3.8 to CircuitPython to run all features on Arduino. EDIT: As I have striven for unprecedented precision a CircuitPython port has become somewhat improbable since many of the libraries I depend on for integrations, etc do not exist in CircuitPython. Also, the space is quite limited. So, while this could be ported to CircuitPython, it would require rewriting most functions with different libraries and some functions could not be implemented due to space limits. Presently I am not interested in developed a crippled mini-Ampy display.
  • I have tried to make this project as plug-and-play as possible, certain vehicle-specific parameters needed are easily set via keyword arguments-- you can SSH into the pi over wifi from any OS to do this. However, the custom profiles are more involved and users will have to edit these register writes to setup their desired profiles. Thankfully, even if you have no experience in python this is extremely easy, as I've parsed all parameter names and attributes from the AsiObjectDictionary.xml and created functions that do most of the work for you. You need only to name the parameter and a value just as shown and entered in BacDoor, e.g. add the line 'self.writescaled('Maximum_Field_Weakening_Current', 50)' under Profile1 in BACModbus to write 50% field weakening current when enabling Profile1. Even if/when ASI changes the parameter addresses in a major revision, e.g. 7.xx, you will only have to replace the ObjectDictionary.xml to update the app just as you would for BacDoor.
 
Did you manage to figure out how to write updated parameters to flash, so that to current state is saved?
 
The parameter at address 511 for 6.xx version controllers will write all parameters to flash when it's written with 0x7FFF (32767) which using my BACModbus class would be called e.g.:

Code:
import BACModbus
BAC = BACModbus.BACModbus()
BAC.write('Save_Parameters', 32767)

Pandemic has forced crunch time at work leaving less for projects, but I am still quite motivated to finish this, as my bike will remain in pieces for benchtesting until I do. :lol:

It took me less than one day to write all the essential functions even as a python novice, but incorporating asynchronous control of the MODBUS serial I/O has proven a more complex challenge. The short of it, is that the first three Python community MODBUS modules I tried using were simply not up to the task, and failed to communicate properly when pushed to speeds even a quarter of real baudrate. MinimalModbus, pymodbus, libmodbus were trash-- after spending months trying to get them to work I discovered modbus_tk, reimplemented everything, and poof and all my problems are gone. Finally have a path forward, and it shouldn't be long now until the application itself is golden-- getting it to run on the Raspi will be the next step.
 
Now that the MODBUS issues are sorted development is actually moving again. Polling rate is 6x faster than BacDoor datalogging (16ms). New git commit has most of the features that anyone who wants a smaller display could use. Still playing with the design, now monochromed for e-ink. I do still have to implement PID control for the minimum range function and will do some code cleaning before mapping the last few trip labels, and implementing permanent storage of trip statistics... but this part is nearly done. Hoping to get it running on the Pi before new years. :mrgreen:

The state-of-charge is reset after charging with the battery icon under the clock, by mapping the battery voltage to a cubic spline fit from the experimentally derived voltage/state-of-charge curves for the 30Q cell from lygte.com, the fit is generated from a replaceable 'socmap.csv' with many voltage:soc points so it can be easily swapped for different chemistries, just as the parameter dictionary can be swapped for future ASI firmware. This is much more accurate than the linear approximation of the controller itself, but for best accuracy should be reset a few minutes after charging/discharging. Once set, the SOC is calculated from the battery Amp-hours.

I was originally adding a new integrand of Amp-hours/Watt-hours to the trip/lifetime counters every 16ms read interval, but after comparing 3 hours runtime with low (~1 amp) current, found that floating point rounding errors even for long-double floats (128-bit) resulted in 0.8% underestimation of charge/energy due to the very very small values. This was corrected by integrating only when the display is refreshed, the accuracy is now within 0.03% with a 250ms update interval!

When fault codes are detected on the controller, an extra check-controller button appears between the battery and lock buttons under the clock, that when pressed creates a popup listing the fault codes with the option to ignore or clear faults.

I implemented a trip selector to change the statistics displayed in the trip widget. I won't really need it for my gigantic display, which will fit every trip statistic I am interested in seeing while riding except lifetime stats and diagnostics e.g. hall and input voltages, but it would be handy if you want a smaller but full-featured trip stats widget for smaller displays.

To avoid loss of stored trip and lifetime data in the unlikely but possible case that power is cut during the few milliseconds it takes to write them to the SD, I'm first pickling the MD5 hash and date-time that the save begins to the SD, and saving to a rolling rotation of several files. When retrieving data on start, only the data corresponding to the pickle with the latest time and valid hash is parsed. This way it will simply avoid a corrupted file and fall back to the trip/lifetime stats from several seconds earlier.

5R9y7fcl.png
 
xenodius said:
I was originally adding a new integrand of Amp-hours/Watt-hours to the trip/lifetime counters every 16ms read interval, but after comparing 3 hours runtime with low (~1 amp) current, found that floating point rounding errors even for long-double floats (128-bit) resulted in 0.8% underestimation of charge/energy due to the very very small values. This was corrected by integrating only when the display is refreshed, the accuracy is now within 0.03% with a 250ms update interval!
That's really interesting. Normally you would assume that a shorter period for calculating the integral would improve accuracy by more closely tracking the signal. So you're saying that at a certain point the rounding errors start to outweigh the advantages of faster rates? Could this error also be attributed to noise, or are you certain that it is a rounding error?

TBH, if the error is worse at low current usage, and you only got 0.8% at 1A over 3 hours, then I still think that's excellent. I wouldn't have thought 0.03% would even be possible.
 
In either case, the integral is calculated with the same data into a 128-bit float that's added to the 128-bit watt-hour float. Every 16ms the current and voltage are stored into a list, then for integration all those points are interpolated and then integrated. Mathematically it should be the same result, but floating point calculations can be funny and not all digits are necessarily significant digits. On top of that with small integrations a bunch of the leading values are 0's, basically it's not using all 128 bits because everything is in the same units. Probably another way to correct it would be to calculate and store milli- or micro-Watt/hours and convert to watt/hours only for display purposes. I am pretty sure that the accuracy would be better with typical riding currents but it would never need to be integrated more often than the GUI refreshes anyway, so bigger chunks is more CPU-efficient and accurate. My 0.03% estimated accuracy is definitely within the margin of error of my meter, so may not represent real accuracy, but either way that's good enough for me! :lol:
 
Latest work log update-- software is golden, the next step is to setup PiCore with all the appropriate dependencies, libraries, drivers, etc and create a shareable image. I can officially say I've moved into the Beta phase-- all features implemented and optimized, no apparent bugs. With a bit of luck I'll be posting an IRL demo of probably the most obsessively precise and versatile ebike display anywhere within a few weeks! Highlights;

1. Improved remaining Watt-hours prediction formula, now it is dynamically integrated from the entire voltage:charge curve of the cell instead of simply scaling according to SOC like is usually done. Actual watt-hours will still depend on how much power you're using and this prediction will mirror 'rated' pack Watt-hours. Unlike Amp-hours which measure charge, Watt-hours of energy produced actually depends on how much power you're drawing so it's impossible to perfectly predict, but this is as good as it gets and should help with accurate range prediction and with the minimum-range power limiter PID function.

2. Improved accuracy of vehicle speed reading; I realized that reading the speed from the controller is considerably less precise than reading the RPM, integrating that into revolutions, and calculating distance from revolutions based on wheel diameter.

3. Implemented an SQLite database for logging and recovery of all trip, lifetime vehicle, and setup options e.g. last selected profile, assist level. SQL is transactional so there is a 0% chance of corrupting data even when power is suddenly cut ("atomicity, consistency, isolation, durability" e.g. ACID compliant). The lifetime vehicle stats are setup so that it records a bunch of parameters for each cycle, as you continue discharging the row is updated, then when it detects that you have charged it flags the row for permanent storage and creates another. So this enables all kinds of interesting statistics, e.g. the depth of discharge, distance traveled, regen stats, and date of every pack cycle while remaining a very small database... for 20,000 cycles it would be ~1 megabyte, all other records limited to ~800kb max. Changing a single value of the window class (iter_attribute_slicer) enables complete logging of all data to the trip statistics SQL table, at 6x as frequency of the ASI BacDoor apps (16ms vs. 100ms)-- otherwise, certain data not needed after incrementing the trip counters or generating averages for certain displayed stats is overwritten in a rolling 5-minute interval.

4. Setup keyword arguments for certain vehicle-specific parameters that it needs to function properly. With the revolution-based speed reading and all the functions depending on the high-resolution state of charge map, these now include; battery series groups, battery parallel groups, battery amp-hours, and wheel circumference. There is also a flag -speedparse that assumes v6.++ parameter addresses that saves some CPU time by reducing lookups although, with many other optimizations to speed I've made since profiling it this is not really necessary. Just want to see if it affects running temp of the Pi. The purpose of these arguments is so that different users can set these parameters easily by changing one line from the startup.ini on the pi without having to touch the code, e.g. 'python main.py -battseries 21 -battparallel 14 -battah 42.0 -wheel 1972.3 (mm).

5. Since the range power limiter PID function will depend on numerous vehicle-specific features, I set up a second UI file that can be used to manually tune the foldback. Back when I had an Adaptto controller I LOVED the range limiter feature, but was frustrated that it would allow me to exceed the range-permissible power and would often overshoot for seconds, then undershoot for about the same, time, before eventually stabilizing. With the exceptionally precise speed and power calculations and ~30ms cycle time with the range limiter engaged, this should be able to taper power down without exceeding the appropriate Wh/mi even if you setup a fast torque rise time (I'm using 100ms on my controller)

6. The very latest ASI firmware (6.015+) adds some new features that allow pullup resistors to be enabled on the PFS/Cruise inputs. So now it's possible to enable antitheft directly via serial, instead of having to wire in an extra GPIO and 3.3v->5v level shifter to one of these inputs. Once I get the new firmware I'll be testing this out.

7. I've configured the "analog" speed and power gauges so the solid border visible in previous photos is drawn only to the current needle position, serving as an additional value indicator. If you use a color display you can set color gradient ranges as well, e.g. green 0-40mph, yellow 40-60mph, red 60-80mph...

The recently released firmware adds some other features that I am excited about too, like a Features3 bit that forces the throttle input to disregard Assist level limits, so I can have e.g. 24kW at the throttle and only 3kW from the torque sensor simultaneously. So the modest 'Assist' levels will limit the torque sensor, and the vehicle-wide 'Profiles' I've setup will limit the much greater throttle power.

vq930iWl.png



Update 12/8
Got the new v6.016 firmware yesterday with Features 3 pullup bits for PFS and Cruise inputs. Now, it is possible to control antitheft and reverse via serial without additional GPIO lines. Just tested antitheft for the first time-- functionality is a bit different than Adaptto that I'm used to, it constantly energizes the hub, resulting in a quite noticeable constant buzzing to precisely read motor position and even slight rotation, instead of waiting for a Hall transition and then trying to turn back to the approximate previous position determined by halls. The actual current used is very low until force is applied, and it gets rid of a serious issue with the old Adaptto implementation-- in some circumstances, once set off it could overshoot, and set off an endless cycle of rocking back and forth potentially causing damage, scratches, or draining the battery or even overheating if left unattended and set off into seizure-mode. This is much better IMO. A new cmdline argument -lockpin, -lp, -pin, or -lock #int sets the code for the pinpad that pops up when tapping the lock button, antitheft remains engaged and popup visible until the correct PIN is entered. Power cycling the controller will also disable antitheft, as I did not include a command to save all parameters permanently to flash since I do not want other register changes to persist when enabling antitheft and my system power is already keyed. I recommend something classy;

pZfWmSol.png
 
This is an excellent project filling an important niche. I've been developing two python interfaces for ASI controllers that the community may also find useful.

The first is a simple desktop configuration app for tuning parameters.

https://github.com/dsoto/BAC-Configurinator

screenshot.png

The second is a CircuitPython dashboard for simultaneous display of BMS and controller information. It uses the Adafruit Grand Central and a TFT shield.

https://github.com/dsoto/ASI-ENNOID-dashboard

photo.jpg
 
Hey, thanks for sharing and I appreciate your interest-- I missed your post notification. That app should be helpful for people who cannot access ASI's official software. BMS integration is finally in my sights for this project, almost complete in fact, and is actually the reason I returned to this thread! :lol: Happy to see I'm not alone there. It's what I miss most about my old Adaptto controller, and there are *very* few examples of displays that integrate BMS functions despite the desirability. In the case of this display it provides better detection of charging current/cycles (I bypass BMS for controller discharge/regen), recreation of cell-level LVC/HVC faults with reporting to the same fault detection, reading, and clearing system used for controller faults, quick adjustment of charge limit for people like me who rarely charge all the way to 100%, convenient cell monitoring, and since I'm running all my 12v accessories off the BMS it doubles as a master switch for accessory power, and current/coulomb counter for accessory discharge to keep the total Wh discharged accurate.

A while back I learned that my e-ink display (and all e-ink displays, for that matter) only support writing of images to the display. I tried to use it as a live display anyway by copying the linux framebuffer from a VNC virtual display to the e-ink display as an image... but SPI is just not fast enough and even with hacked drivers it's still impractically slow especially on an 1872x1404 display, despite the high refresh rate of that e-ink. So I found a reasonably priced HDMI 5" 800x480 1100 nit IPS display with 1-point capacitive touch from Makerplane. It's VERY BRIGHT. Very easy to read even in direct Florida sun, and having 60fps/color is most welcome even at the cost of a few watts. :D

I have also made the first generation case for the display itself, designed for a pi 4B, Makerplane display, and 31.8mm handlebars. Enough room for a PermaProto HAT + modules between the pi and display. Designed for printed TPU seals on the bezel of the display, the case seam, and over and SD slot port for waterproofing. Amazing that today we can fit a full-fledged computer in something this size, that is about 10x faster, has 16x the memory, and uses 1/50th the power of my first PC tower!

I've spent the last couple months (!!!) setting up piCore dependencies for the app, even on a Pi 4B it takes a very long time to compile anything. So I only had 2-3 chances per week to call the right compiler flags that not only produce a working binary for an ARM linux OS, but the smallest possible binaries for each dep-- this is key to keep the boot time low. Now that this is done for 32-bit piCore 12.x ARMv7L and everything is traced/stripped as light as possible, this is compatible on all RPi versions. Eventually, before I move on to heavier long-term additions like dashcam, navigation, trip simulation I will repeat the process on 64-bit piCore 12.x AARCH64 for performance however that version would only work on the Pi 4B. While I will be sharing these custom extensions on the git repo, if anyone wants to run this on piCore just let me know and I can share a recent fully-configured image for easy setup. If you use the same hardware you only have to flash it and it's ready. There are a lot of additional settings required to run this on piCore out of the box, unlike bloaty Raspbian which is a very fast setup-- everything is in the package index already, and they have a convenient GUI to do most of the auto setup. The only problem with raspbian is inevitable corruption in months to years, even with a safe shutdown procedure, none of which matters for piCore. After I finish more pressing feature additions I'll write a piCore setup script but if you're already familiar with linux it's easy.

With piCore deps done I'm finally back to adding features! I found a JBD bms interface in Python with wonderfully detailed descriptions of their serial interface. From this I've already tested the second serial line on the Pi to read/write every BMS register, the native 3v3 UART is directly compatible unlike the ASI controllers which use 5V serial requiring either a logic level shifter or USB serial cable. Since I'm bypassing my BMS and discharging straight through the controller via breakers, and running accessories only off my BMS, this means that with both BMS and ASI serial inputs I can measure current and run independent coulomb counters for charging, accessory discharge, controller discharge, and controller regen currents. This is really nice as cell-level LVC/HVC, battery temperature, etc can be monitored centrally and any faults with BMS serial or defined limits for those parameters can be recreated and piped to the same interface as controller faults. I'm creating two extra panels for the BMS interface, one which is an overview pane viewable by button and automatically raised when charging is detected that shows:
  • each cell voltage/balance state with bars and mock-LED's,
  • each temperature sensor (natively supports 4 but the chip itself has pins/registers for 8 NTC thermistors) with bars + text labels,
  • slider for adjusting end-of-charge cutoff,
  • slider for adjusting voltage at which balancing begins,
  • buttons for manually switching BMS charge/discharge FET's
  • and basic info including BMS +/- current, pack voltage, cell voltage range/cell voltage max difference.
The second panel is a scrolling pane with spinboxes to view and change every single EEPROM register of the BMS for complete reconfiguration directly on the display. While I would like to do something like this for the controller too, but frankly there's just too many registers to do it in any easy-to-use way. The mobile app is more convenient for serious reconfiguration-- and it looks like open-source mobile alternatives to ASI's BacDoor are being developed by others to fill this role for those without dev access. Since I'm connecting the display serial in place of the bluetooth BMS module it is important to have these config options as I won't be able to use the ridiculously buggy xiaoxiang Android app to configure it. It could be connected via bluetooth instead of serial but I'm unsatisfied with the stability of the BLE connection. All you would have to do on a Pi is get bluez/kernel drivers, start the hci daemon, pair the devices, then open a port with bluetoothctl. Then just pass that port to the app.

I'm also adding a scrollable Options pane to hold interface elements I no longer think are important enough for the mainwindow but I want to keep, plus new extras, and a full spread of diagnostics/troubleshooting information including:
  • Field Weakening % slider,
  • % battery power limit slider,
  • PID range limiter slider, + Kp/Ki/Kd term tuning sliders to adjust its feel e.g. control how much overshoot is allowed.
  • reset trip statistics button,
  • Diagnostics infopane listing voltage of every controller input, and the resulting "Ebike Flags" (e.g. Brake Cutoff ON).

Last thing I'll do before moving onto BOM, documentation, and show-off vids is switch from CLI args for setup information, to a config.ini file. Since the number of things that should be configurable has grown so much this should be a little easier.
 
This project looks amazing. I've followed some of the GitHub features you've implemented and the proposed features has me keen to try this out, any chance you could share the compiled image for me to try out?

Also with the ASI bac having an inbuilt g sensor would there be a way to implement a wheelie throttle cutout depending on a angle threshold? Say for example 45deg angle triggered then scale throttle input back
 
BMS integration is now fully validated and pushed to the master branch on git. I'll make an overview video to show off at some point... there's two windows added. A main BMS overview window with widgets for each cell in the pack, cutoff and balance adjustment sliders, labels for BMS current/power and cell imbalance. Essentially just to check battery state, and adjust charge/balance thresholds for people like me who often change charge level to minimize pack wear. A subwindow from the overview pane provides read/write config of all EEPROM settings with touch-friendly spinboxes. This is nice since the physical serial line replaces any bluetooth adapter you may have had with your BMS. Credit goes to Eric Poulson for the backend that generates the serial i/o, everything known about these BMS has basically been reverse-engineered. The main window UI is updated so that the max temperature from any BMS thermistors are visible as a grey margin in the Motor Temperature bar at the same scale, in addition to all the BMS variables added to Trip Pane #3.

Final steps are to trick out the Options dialog with some extra controller tuning options and a full suite of diagnostic voltage/flags for all controller sensors. Then I'll setup some more app configuration options including mph->kph conversion (this can be currently kludged by just multiplying your wheel diameter by 1.609344) and finally, some themes to make it real purdy. :bigthumb:

There's now a link to a compressed image that's setup with all the trimmed up piCore dependency extensions, driver configs/scripts, and display startup script on Github. After flashing the image if you have a 5v USB-serial converter connected between your Pi and the ASI controller, it'll boot directly to the dashboard. However, you'll want to do some minor setup-- edit the configuration flags in /home/tc/.X.d/ampy to match your vehicle parameters e.g. cell series and parallel number, pack Ah, wheel circumference in mm, antitheft pin, and verify that your controller port and if using JBD BMS the bms port are correct. Then connect to your wifi and run filetool.sh -b to backup your WPA key for easy SSH updates later. Finally expand the filesystem to your SD's capacity according to the TinyCore Linux readme to prevent issues down the road. Commands below;

To edit the configuration flags:
Code:
# Terminal starts at /home/tc so:
nano .X.d/ampy
#else:
nano /home/tc/.X.d/ampy

To update the application over SSH:
Code:
tce-load -i git
cd /mnt/mmcblk0p2/ASI_AmpyDisplay
sudo git pull
sudo git checkout master # Or checkout dev if you want to provide feedback on things in testing =)

To expand the filesystem;
1) Start fdisk partitioning tool as root:
Code:
sudo fdisk /dev/mmcblk0
Now list partitions with 'p' command and write down the starting and
ending cylinders of the second partition.

2) Delete second partition with 'd' than recreate it with 'n' command.
Use the same starting cylinder as deleted had and provide end
cylinder or size greater than deleted had having enough free space
for Mounted Mode. When finished, exit fdisk with 'w' command. Now
partition size increased but file system size is not yet changed.

3) Reboot piCore. It is necessary to make Kernel aware of changes.

4) After reboot expand file system to the new partition boundaries with
typing the following command as root:

Code:
resize2fs /dev/mmcblk0p2


Wheelie protection would be fun, but would likely require an accelerometer directly on the Pi. The ASI controller also has an accelerometer but reading that register would interfere with the main fast-loop and at least halve the update frequency, by the time you add throttle control loops it'd be at least 50ms between compensating for the accelerometer signal. Probably this is not fast enough. I suspect since this would have to be extraordinarily responsive to work well, you may have to externally change the throttle signal itself by passing through a dedicated microcontroller with tuning/etc set on it over serial commands from the Pi. And it would probably have to be tweaked a bit for each bike and I don't care enough to have it, to risk it behaving oddly and crashing my baby =)
 
Finally, my last bump and necro of this thread for an age, unless anyone has suggestions/requests. Coolest feature of latest update is the ability to hack access codes on the controller. :twisted: The other planned additions to this project like the lojack and the intelligent route-based range limiter are big endeavors and months/years away from realization.

Changelog:

The options pane has a button to brute-force unlock the access level codes of your controller, displaying them as they're unlocked and writing them to the controller and saving them to an 'access-codes.txt' file. Took about 90 minutes on my controller, max theoretical time is ~8 hours. I also added more tuning sliders, now you can scale % field weakening, % rated battery current, % rated motor current, enable/disable Engine Braking, Walk Mode, Throttle Bypass Assist Levels, and setup the rangelimiter (on/off, distance, PID tuning). The diagnostics poller is now setup and displays voltages of all analog inputs, and lists states of all digital inputs, 'ebike flags', warnings, etc. for troubleshooting and debugging.

The controller serial I/O thread has been moved to a subprocess to ensure polling is as fast as possible and completely consistent, with all the features I've added I was seeing poll times occasionally up to 24ms, now guaranteed 16ms flat.

SQL database has been improved to use information from a JBD bms if connected to determine whether charging is ongoing for cycle-counting and lifetime statistics. The SQL database now also re-initializes the all the tuning parameters set in the options pane after restarting, so any adjustments you've made to a power profile are retained on restart.

Vehicle-specific configuration parameters including imperial/metric units are now entered in a setup.csv file instead of cli args, which also allows relatively convenient editing of power profile parameters. Any number of lines can be added to set registers and values to be written when switching to each profile, e.g. 'profile1, Maximum_Field_Weakening_Current, 50'. Full details of settings syntax is on git, and an example file is included.

Future plans:
I decided my all-in-one case was too chonky so I'm making a slim display-only case and moving the Pi, gps, optorelays, serial adapters inside my frame and will release an ultra-slim display mount soon... with final video tour.

I've also decided to use a LoRa radio for the lowjack I'm developing, instead of an IoT SIM as I don't want to subscribe to monthly fees. LoRa has exceptional range, realistically at least 100 miles, the world record with line-of-sight is about 500 miles! And it is still very low power. This will require a tiny custom FOB using a Teensy or some microcontroller-- simple, very small, very little power consumption with remote alarm/lock/unlock buttons and Bluetooth to connect to a mobile device, from which .gpx tracks can be transmitted to the bike for the intelligent route-based range limiter I'm developing. As LoRa is not very fast and could take up to a minute to receive a long navigation track, I may add an NFC reader for automatically locking/unlocking the bike and receiving .gpx tracks from a Qi-capable phone dock... *mad scientist cackle*
 
Works with LFP voltage values?

Still 21S max?

24s LFP for many is 87V, but for me more like 82V maximum
 
john61ct said:
Works with LFP voltage values?

Still 21S max?

24s LFP for many is 87V, but for me more like 82V maximum

If you use LFP you'll want to update the voltage:state-of-charge mapping, named 'socmap'. Since LFP have phenomenally stable voltage in the middle-80% of their SOC, like any other coulomb-counter you may have to occassionally charge or discharge them beyond that 3.3v/cell plateau to reset it and keep the Ah reading accurate. I had to do this on my old Adaptto controller every 20-30 charges. I extracted data for 30Q NMC cells for the default state-of-charge mapping file using WebPlotDigitizer and a chart from lygte.com (0.2c discharge). The 'socmap' file is interpolated so you can add as many or few points as you want; more points at the top and bottom of SOC where the voltage is actually changing a lot would help precision in your case. If you would like to use the BMS integration with more than 21s I can set it up for 24s, with common chemistries the voltage limit precludes using >21 series groups on ASI controllers so I didn't foresee your case. :wink: I'll do this Tuesday or next weekend depending on work.

I forgot a small update that'll be coming up in the near future. I'm in the process of adding a 12v lighting system to my bike (DOT hi/lo, front/rear brake, turn signals), horn, and handlebar switches for controlling those as well as adjusting the power profile, assist level, and a potentiometer to dynamically map to any setting currently in the Options pane. The potentiometer/digital buttons will require a small i2c ADC (testing with INA219) but I will make that and the GPIO switch pinouts and function mappings dynamically set in the setup.csv file, so that users can map any display functions to physical buttons as desired and remap them without changing the wiring.
 
Wow fantastic!

Yes the lower range stop-charge setpoint

3.42V to 3.48V

is for "normal daily-use" cycling in production, depends on charging C-rate and cell temperatures.

Periodic "maintenance cycling" balancing / cap testing etc will use cell mfg datasheet values, maybe annually for known-good new cells, down to monthly for worn or suspect packs.

Are there upper / lower limits as to current rates (charging or discharging?) or Ah capacity per cell?

Can an external set of relays be used for very high ampacity cases? Just use the BMS for voltage sensing not run the power circuit through the FETs?

Could modular "sub packs" be individually protected / monitored, yet also joined to create a larger "mothership" bank?

In serial or parallel, or both?

I don't know whether the dual- or single- "common port" design is best for that, I assume LLT/JBD offer both?
 
john61ct said:
Are there upper / lower limits as to current rates (charging or discharging?) or Ah capacity per cell?

That depends entirely on your BMS/controller. I'm bypassing BMS for controller discharge via 3x 100 amp MNEDC-100 breakers, and do use a 300-amp battery current limit for my 350-amp rated pack. With this config the BMS detects only charging current and 12v accessory discharge. Controller detects motoring current and regen current. This is more efficient than going through the BMS fets (and IMO, safer- I don't trust most BMS power ratings I've had them fail occasionally) but by reading serial data from both with this wiring config I can have individual counters for accessory discharge, charging, motor discharge, and motor regen braking. If you power your ASI controller through a JBD BMS and you connect both devices serial to the display, then as the app is currently coded, it would double-count your regen braking current and charge current from each device and interpret regen as a charging cycle in lifetime statistics. In that case it would be better to connect the controller only and let it figure out cycles from SOC jumps.

I've considered adding a setup.csv switch for BMS integration with controller power through the BMS but... With the remote battery info getting sent from the BMS to the ASI controller, plus centralized fault detection, plus the pack-level limits you can program into the ASI controller... I see no benefit and only drawbacks. I think it's much much better to bypass the BMS and use it only for charging/balancing/monitoring. With a dumber or lower power controller, it'd be different.

john61ct said:
Wow fantastic!

Could modular "sub packs" be individually protected / monitored, yet also joined to create a larger "mothership" bank?

In serial or parallel, or both?

I don't know whether the dual- or single- "common port" design is best for that, I assume LLT/JBD offer both?

Generally speaking split packs are possible. As for this project you could connect multiple BMS it would take only a slight alteration of the code (name another BMSSerialProcess class and set of pipes in the "if name == __main__: of main.py... Hardcode its port). It would probably be better to use a series-split if you have to do this so you don't have to concern yourself with imbalance resulting in large currents when the packs are paralleled. Em3ev used to sell split packs but supplied because they had too much trouble from misuse...

Not sure what precisely you're referring to about dual or common ports.

As for donations- no I don't. This is my gift to the community (and myself). But if you do build one you're encouraged to post a pic and brag about all the cool crap you can do with it =)

Sent from my SM-N960U using Tapatalk
 
Very sensible approach not trusting cheap BMS for high currents.

I do not even use them for charge control nor balancing, RC Hobby chargers are faster and more reliable. As a failsafe backup HVC maybe.

Dual port means one for charge control HVC, separate path for discharge LVC, while single port one port / switch controls both.

I will need to be able to connect sub packs not only serially for voltage

but at the sub-pack voltage level, also in parallel.

So e.g. 12V 10Ah sub-packs first paralleled say 5P to get to 50Ah, then those 50Ah "groups" serial'd into a single string x4S to get to 48V

Any issues from imbalanced current flow detected and resolved by the frequent per-subPack balancing / cap testing maintenance routines.

_______
are those breakers used as contactors controlled by the BMS?

You're paralleling 3x 100A to give 300A ampacity tolerance?

Or is the controller the only current limiter?

Sorry I'm no coder, so if I can't throw money your way I'll just have to "support" the project by testing feedback, unreasonably edge-case feature requests marketing the heck out of it, and maybe monetizing commercially :cool:
 
In that case single/dual port doesn't really matter with respect to the display. As long as it's a JBD bms it should work with this display. Mine from ICGOGOGO has separate charge/discharge switches but only one port. At least, it does in software. I haven't tested to see if they cut out both ways.

The breakers are always-on and the controller provides the current limit. When the controller logic board is not powered, it doesn't draw any current that I can measure. The logic board power is on a keyswitch with a blade fuse.

Testing feedback is most welcome! :wink:
 
Would anyone want to make some money? I am going to post it up on upwork but would rather it goto a member whos familiar with the project first.

I just want to add something to this app to control a 12V relay? I just want to be able to turn my head lights on and off from the app, as well as the tail light.

Not looking for anyone's time for free. I have a ton of time invested into learning how to get his amazing screen working and just want to put the cherry on top! If you would rather the job be posted and gone thru on up work that is fine as well.

Thank you!

Thank you again Creator!
 
longwood said:
I just want to add something to this app to control a 12V relay? I just want to be able to turn my head lights on and off from the app, as well as the tail light.

The software part would be relatively straightforward (The display backlight slider is an example of a screen button bound to one of the Rpi GPIO outputs) For the hardware you can definitely look up guides. Changing the GUI to add a new button vs. reassigning one is slightly more involved. You can use one of the logical outputs to switch a transistor and gate a 12v optoisolated relay.

For myself I just added a 12v supply and a 3-pos switch to control the optorelay (off/lowbeam/hibeam) it's simple and I like having a tactile switch for some things. Also, it works even if for some reason the display did not/is booting.
 
Thanks for the information man, I was hoping it would be some what easy for one of you GURUS!

I have a few different lights on the bike. ( Head light, tail light, storage light, under glow light ( I know lame, but helps not get killed at night))

For anyone else looking to do this, IF you do as I did. You will have a bad time.. This gentleman did a standup job doing all of the project and I tried using cheaper or lesser components hoping it would work.. I found out wrong.
Do not cheap on the Pi version, the screen or any of the usb cables. If you follow his build out as specified it will be as easy as 1-2-3. If you are pro coder maybe such would not be impossible.. If not FOLLOW His parts and make your life easy!

Even the STL for this is RAD!



I finally ordered the correct makerplan touch screen and look forward to full usage soon.

Thanks again for answering all of my PM, and making this avl to the public.
 
Back
Top