255 lines
8.9 KiB
Org Mode
255 lines
8.9 KiB
Org Mode
:PROPERTIES:
|
|
:LOGGING: PROGRESS(!) DONE(!) CANCELED(!)
|
|
:END:
|
|
|
|
#+SETUPFILE: ~/src/org-themes/src/white_clean/white_clean.theme
|
|
#+TITLE: E-Bike-Tracker
|
|
|
|
* Requirements
|
|
|
|
** PROGRESS [#A] Device components
|
|
- State "PROGRESS" from "TODO" [2022-05-27 Fri 00:42]
|
|
|
|
Components needed to develop a PoC:
|
|
- BMS
|
|
- GSM (SIM800L is the cheapest and most reliable, i.e. best value for
|
|
money, but only supports GPRS)
|
|
- GPS module
|
|
- BLE module
|
|
- Accelerometer
|
|
- Flash for storing local device data (no need for more than 4M?)
|
|
|
|
Esp32 has most of these, is pretty cheap and compact and easy to
|
|
develop on.
|
|
|
|
Choose Rust ...
|
|
|
|
** PROGRESS [#A] BMS
|
|
- State "PROGRESS" from "TODO" [2022-05-27 Fri 00:44]
|
|
Find a BMS that will do good for the battery, measure all cells and
|
|
give out data needed for various measurments.
|
|
|
|
*** TODO [#A] Measure bike battery level
|
|
|
|
** TODO [#A] GPS module
|
|
Find a minimal GPS chip and read from it, test stability and tolerance.
|
|
|
|
** PROGRESS [#A] SIM module (GPRS/3G/4G/LTE)
|
|
- State "PROGRESS" from "TODO" [2022-05-26 Thu 23:57]
|
|
Find a minimal GSM chip and try to communicate with it.
|
|
- [[https://www.aliexpress.com/item/4000830117417.html?spm=a2g0o.productlist.0.0.6f9ad49ffLG35d&algo_pvid=594a04bb-8a80-4596-8025-9486269f0923&algo_exp_id=594a04bb-8a80-4596-8025-9486269f0923-28&pdp_ext_f=%7B%22sku_id%22%3A%2210000008680354389%22%7D&pdp_npi=2%40dis%21RSD%21%21288.56%21%21%21%21%21%40210318c916529073178195889e0edd%2110000008680354389%21sea][SIM800L]] - GPRS only, no 3G or 4G ([[https://github.com/lesha108/sim800_ups_monitor/blob/main/src/sim800l.rs][Rust implementation example]]) ... cheapest one there is.
|
|
|
|
** TODO [#A] Connect to SIM data (GPRS/3G/4G/LTE)
|
|
Get a library, or write minimal code that will *only connect to mobile
|
|
data* and send the coordinates retrieved from the GPS module.
|
|
|
|
** TODO [#A] Accelerometer tracking
|
|
|
|
** TODO [#B] GSM triangulation for better accuracy
|
|
|
|
** CANCELED Hardware button to switch the device on and off?
|
|
- State "CANCELED" from "WONTFIX" [2022-05-26 Thu 19:55]
|
|
In case the phone is lost or whatnot, the device needs to have a hw
|
|
switch so it doesn't bother anyone. There is an edge-case here, maybe
|
|
even an anti-feature where the bike gets stolen and the thief clicks
|
|
the hw button and disables all alerts.
|
|
|
|
** HOLD [#A] Lock/Unlock mode on the device
|
|
The device will need to store a state (lock/unlock). When locked and
|
|
the bike moves, the device will send coordinates through GPRS/3G/4G
|
|
(depends which GSM module we choose).
|
|
In this scenario an alert must be sent to the mobile app.
|
|
|
|
|
|
* Software on the device
|
|
|
|
We should consider the scenario where we send GPS data independently
|
|
from other modules (like BMS, Accelerometer ...) because if the bike
|
|
gets stolen it would be great to send the coordinates as fast as
|
|
possible. In this case I don't think that battery data would be of
|
|
much relevance.
|
|
|
|
The other way around, when the owner of the device is riding the bike,
|
|
then there's no need to send GPS, or Accelerometer data to the server.
|
|
|
|
Also, the battery status could be measured at different intervals that
|
|
GPS and acceleration, depending on the mode the device is in.
|
|
|
|
Therefore we send every measurment separately for every device.
|
|
|
|
#+begin_src plantuml :file img/device_arch.png
|
|
participant BMS_Thread
|
|
participant Accelerometer_Thread
|
|
participant GPS_Thread
|
|
entity MPSC_Queue
|
|
|
|
Consumer -> MPSC_Queue : recv()
|
|
activate Consumer
|
|
...
|
|
BMS_Thread -> MPSC_Queue : send(battery)
|
|
activate BMS_Thread
|
|
MPSC_Queue --> Consumer
|
|
Consumer -> GSM_Modem : write(json)
|
|
...
|
|
Accelerometer_Thread -> MPSC_Queue : send(acceleration)
|
|
activate Accelerometer_Thread
|
|
MPSC_Queue --> Consumer
|
|
Consumer -> GSM_Modem : write(json)
|
|
...
|
|
GPS_Thread -> MPSC_Queue : send(position)
|
|
activate GPS_Thread
|
|
MPSC_Queue --> Consumer
|
|
Consumer -> GSM_Modem : write(json)
|
|
...
|
|
MPSC_Queue --> GPS_Thread
|
|
MPSC_Queue --> Accelerometer_Thread
|
|
MPSC_Queue --> BMS_Thread
|
|
|
|
deactivate GPS_Thread
|
|
deactivate Accelerometer_Thread
|
|
deactivate BMS_Thread
|
|
deactivate Consumer
|
|
#+end_src
|
|
|
|
We'll read different things from different modules, so it's best to
|
|
have proper structs for all of them.
|
|
|
|
#+begin_src rust
|
|
#[derive(Serialize, Deserialize)]
|
|
struct BatteryStatus {
|
|
capacity: f32,
|
|
cells: usize,
|
|
active_cells: usize,
|
|
voltage: f32,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
struct GpsCoordinates {
|
|
latitude: f32,
|
|
longitude: f32,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
struct AccelerometerStatus {
|
|
acceleration: f32,
|
|
}
|
|
#+end_src
|
|
|
|
Messages sent from the producers (senders) will be of different
|
|
types. Therefore we need to create an enum for those messages and fill
|
|
in a struct that will be serialized to JSON and then sent to the
|
|
server.
|
|
|
|
#+begin_src rust
|
|
enum SensorData {
|
|
GPS(GpsCoordinates),
|
|
Battery(BatteryStatus),
|
|
Accelerometer(AccelerometerStatus),
|
|
}
|
|
#+end_src
|
|
|
|
Then in some function executed in a thread ...
|
|
|
|
#+begin_src rust
|
|
fn write_gprs(modem: GsmModem, rx: Receiver<SensorData>) -> Result<()> {
|
|
let json_for_server = match rx.recv() {
|
|
GPS(coordinates) => serde_json::to_string(&coordinates)?,
|
|
Battery(status) => serde_json::to_string(&status)?,
|
|
Accelerometer(status) => serde_json::to_string(&status)?,
|
|
};
|
|
modem.write(json_for_server)?;
|
|
}
|
|
#+end_src
|
|
|
|
* Device and phone registration
|
|
#+begin_src plantuml :file img/registration.png
|
|
Phone -> Device: Get device ID
|
|
Phone <-- Device: Device ID
|
|
|
|
Phone -> Server: Register IDs (device_id, phone_id)
|
|
Phone <-- Server: Client TLS certificate
|
|
Phone -> Device: Set TLS certificate
|
|
Phone <-- Device: OK
|
|
|
|
Device -> Server: ID verification request
|
|
Device <-- Server: ID verification response
|
|
#+end_src
|
|
|
|
** TODO [#A] Phone gets its own device ID
|
|
The phone needs to get its own device ID as part of the registration
|
|
procedure. Some code exists in [[https://stackoverflow.com/questions/45031499/how-to-get-unique-device-id-in-flutter][this example]], needs to be verified
|
|
though.
|
|
|
|
** TODO [#A] Phone gets device ID via BLE
|
|
The phone needs to retrieve the device ID via BLE and pack it together
|
|
with the phone's ID before sending it to the server as part of the
|
|
registration procedure.
|
|
|
|
** TODO [#A] Generate client certificates with rustls
|
|
After the CA cert and server keys are all set up, we can use it to
|
|
generate client certificates for the devices. This should all be done
|
|
in the web server code, i.e. no exit to shell and call openssl, but
|
|
use rustls to generate the cert itself.
|
|
|
|
** TODO [#A] Phone sets client certs to device
|
|
The phone needs to retrieve the certificate from the server and pass
|
|
it to the device.
|
|
|
|
** TODO [#A] Device sends an HTTPS request with its own ID
|
|
The device needs to send a verification HTTPS request using the
|
|
certificate it received with its ID as the body of the request. This
|
|
is the final part of the registration procedure.
|
|
|
|
** TODO [#A] Handler that registers the phone
|
|
The phone needs to get its ID and send it within the registration data.
|
|
|
|
Registration data:
|
|
#+begin_src
|
|
{ phone_id
|
|
, device_id
|
|
}
|
|
#+end_src
|
|
|
|
After the server receives this it needs to generate a client cert that
|
|
the device will use to send its updates.
|
|
|
|
** TODO [#A] Handler that registers the device
|
|
The final stage of the registration process. The server would need to
|
|
validate that the request came with the cert generated for that
|
|
device, and that the device id sent in the body matches the one for
|
|
which the cert was generated,
|
|
|
|
** TODO Investigate if we can use Wireguard on the device
|
|
See if possible at all. There are some implementations and if it works
|
|
we will have much better security than tokens, JWTs and the
|
|
like. maybe not much better than TLS certificates though :)
|
|
|
|
If possible, the keys should be generated on the device, but not sure
|
|
if it has physical limitations (cpu, randomness ...). If the device
|
|
has physical limitations then we could generate the keys on the phone.
|
|
|
|
|
|
* Tracking
|
|
The tracking can be done in different ways. Some possible scenarios
|
|
where we could send alerts:
|
|
- The device and phone could both send their location, and if they
|
|
diverge we could start raising alerts on the network.
|
|
- Add a lock in the app that will trigger alerts when the device moves.
|
|
- etc.
|
|
|
|
** TODO [#A] Report phone coordinates?
|
|
** TODO [#A] Report device coordinates
|
|
** TODO [#A] Handle new coordinates on the server
|
|
- Check if the phone and device are sending same coordinates (or within range).
|
|
- If the device and phone are paired, do nothing.
|
|
- If the device and phone are not paired, send alerts to the phone that new coordinates are received.
|
|
- If the device and phone are not paired and wifi is disconnected, send alerts to the phone and all phones near by (configurable).
|
|
|
|
** Outgoing message configuration:
|
|
- broadcast - broadcast to all subscribers that are listening to broadcast messages
|
|
- multicast - sends alerts and status reports to a list of devices/users
|
|
- unicast - sends alerts and status reports only to the phone that owns the device
|
|
|
|
** Incoming message configuration:
|
|
- broadcast - receives and shows all alerts that are broadcasting outgoing messages
|
|
- multicast - receives and shows alerts from a list of device/users
|