This project hopes to contribute in some small way toward the United Nations Sustainable Development Goal of addressing climate change by encouraging adoption of electric vehicles. The thermal efficiency of the average automobile around 20% makes it a better producer of waste heat and greenhouse gas than mechanical motion. That is a vast waste of energy value that should be of interest to any engineer, economist or businessperson in recovering and monitizing.
One of the disrupters of electric vehicles is the current funding model for roads, highways and bridges from gasoline taxes. This project addresses a fair road use tax by collecting tolls as an electric vehicle is driven, as distinct from flat rate taxes on EVs.
Self driving automobiles will need well marked and maintained highways to function safely.
One way of recovering lost gasoline taxes has been flat fees on electric vehicles, regardless of how much they are used or sit in the garage. A state that levies a $200 annual fee on an electric vehicle collects as much as a gasoline vehicle that goes 12,000 miles a year, gets 30 miles per gallon and pays $0.50 a gallon tax.
Another idea is to utilize a crypto currency payment system akin to EZ-Pass, that will pay as the vehicle passes predefined areas, totally automatic and non-intrusive, unlike a physical toll booth that requires stopping and creates traffic jams that just waste more energy. The first electronically operated toll highway is in Canada, the Ontario highway 407 ETR . I had the opportunity to drive that route on a trip and the license plate recognition system detected my car and mailed a bill for nearly $20 some weeks later. Given widespread data connectivity on major highways, we think a more efficient modern system can be created for the payments side. A monitoring side will likely still be needed to keep drivers honest, but a simple on-board box that knows where toll areas are can make payments as you go and greatly reduce the administrative overhead.
This is an example "virtual toll booth" setup around Coinjock NC for testing the project on a summer vacation to the beaches of North Carolina.
The areas are defined with the help of google-earth-pro, and the above four corner coordinates around the bridge are:
[ {"latitude":36.3409,"longitude":-75.9553},
{"latitude":36.3417,"longitude":-75.9545},
{"latitude":36.3406,"longitude":-75.9534},
{"latitude":36.3399,"longitude":-75.9539}
]
Using a nodejs library 'geolib' the script can detect when it is inside the red box and initiate an iota payment to a 'toll collector' address.
Hardware Assembly and GPS testing
Re-using parts I had in the shop for other projects, this uses a Raspberry Pi Zero Wireless, a Hologram Nova cellular data connection usb dongle, and an EM-506 GPS module. A newer RPi4 with any working LTE dongle should also work with the nodejs script as it is.
The first step after updating Raspbian is to get the serial port connected to the EM-506 GPS unit with 3 wires for power, ground and data. The EM-506 is a very nice module with a big antenna that locks in seconds. Per p.10 of the datasheet it needs a 5.0v main power supply VIN on pin 2, ground on pins 1 and 5, and a RPi Zero compatible 3v3 TX data on pin 4. The serial port starts up at 4800 baud which is what we will use. Per the Raspberry Pi Zero GPIO pinout there is 5v, ground and serial input all at the top right.
Since the project operates largely headless, the application will be started up from a line in /etc/rc.local (or better, a systemd setup can be used), and will need to be shutdown gracefully somehow. I use a modified version of pi-shut that was missing an '&' in the service so it will run in the background. For that a normally open momentary contact switch or push button is connected to GPIO pins 7 & 9. There are many other shutdown button projects to choose from.
Connect those up and mount the Pi in a handy box with the Hologram and the rest is a simple matter of programming.
In the above, pin4 is 5v RED to the gps module, pin 6 is ground BLACK. Pin 10 is data YELLOW, RXD on the RPi which is TX on the gps module. Skip pin 8, the RPi need not send any commands to the EM-506. For the shutdown button, wire it to pins 7, GPIO input 7, BLUE above, and pin 9 ground GREEN.
The Hologram USB dongle is connected with an On The Go OTG adapter. I used some hot glue to tack it down to the box and ran the cellular antenna outside and attached to the side of the plastic box. My Hologram Nova shows up as a 3G device on their dashboard, and worked fine on a long trip thru the eastern US except for some areas known to be cellular challenged. A newer LTE service might work fine, the project just has to access the devnet endpoint https://nodes.devnet.iota.org:443 from the highways.
For power I use an older Mophie Powerstation XL which can run the RPi, gps and cellular dongle all day (Another budget usb battery pack from Big Lots works with everything but the Hologram never connects with it for some reason). That makes it portable enough to put in the back window shelf of our car. Car power adapter to 5v micro-usb would likely work fine.
For management, I like to connect the RPi to the house WiFi as a normal client to setup, get the Nova drivers installed and working, then configure it to work as a wifi access point with hostapd. Hologram has a 'spacebridge' and can ssh in over cellular also if needed. Just never do an 'apt upgrade' with the cellular up - trust me on this, it can get expensive. To switch back from hostapd to 'normal' wifi, I just # systemctl disable hostapd, edit /etc/rc.local and disable the Hologram driver setup, then edit /etc/dhcpcd.conf and comment out the interface wlan0 / static ip_address. With hostapd WiFi Access Point running, it is handy to connect to with a cell phone, ssh in (with JuiceSSH Android app) to 192.168.32.1 (as set in /etc/dhcpcd.conf) and check that the Hologram ppp0 interface is up and can ping the internet. (the wifi could show as having 'no internet' and ssh not work until cellular data is turned OFF).
Once networking is setup, the fun begins with testing the GPS device. It is wired to the serial port, and needs a little configuring to use /etc/ttyS0 by following the setup instructions with raspi-config. Test with picocom and you should see output like this
$ picocom -b 4800 /dev/ttyS0
picocom v1.7
port is : /dev/ttyS0
flowcontrol : none
baudrate is : 4800
parity is : none
databits are : 8
escape is : C-a
local echo is : no
noinit is : no
noreset is : no
nolock is : no
send_cmd is : sz -vv
receive_cmd is : rz -vv
imap is :
omap is :
emap is : crcrlf,delbs,
Terminal ready
fff̖W,1,05,2.1,300.0,M,-33.0,M,,0000*67
$GPGSA,M,3,21,15,20,13,29,,,,,,,,2.9,2.1,2.1*39
$GPRMC,221713.000,A,3749.6142,N,12225.3804,W,0.11,172.83,260819,,,A*72
$GPGGA,221714.000,3749.6142,N,12225.3805,W,1,05,2.1,300.0,M,-33.0,M,,0000*65
$GPGSA,M,3,21,15,20,13,29,,,,,,,,2.9,2.1,2.1*39
$GPGSV,3,1,12,21,75,263,25,15,55,047,32,20,53,310,22,13,19,045,28*78
$GPGSV,3,2,12,29,07,192,12,35,00,000,,18,57,065,,09,42,103,*7D
$GPGSV,3,3,12,08,35,093,,32,20,081,,04,12,143,,05,06,283,*7F
If that worked successfully, exit picocom with ctrl-a,ctrl-x and install nodejs and the required libraries for reading a serial port and parsing the NMEA sentences.
for nodejs, per https://tecadmin.net/install-nodejs-with-nvm/
$ curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash
$ nvm ls-remote
<big long list>
v10.16.1 (LTS: Dubnium)
v10.16.2 (Latest LTS: Dubnium)
v11.0.0
$ nvm install v10.16.2
Downloading and installing node v10.16.2...
Downloading https://nodejs.org/dist/v10.16.2/node-v10.16.2-linux-x64.tar.xz...
Then for the GPS module libraries
$ npm install -g serialport
$ npm install -g '@serialport/parser-readline'
$ npm install -g gps
A note on node package locations, the above -g switch installs the packages in /usr/local/lib/node_modules. Since I do *not* want to install packages as root, nor run the script as root, I changed the ownership of /usr/local/lib/node_modules to pi:staff (drwxr-sr-x 13 pi staff ) and put at the end of my /home/pi/.bashrc:
export NODE_PATH=/usr/local/lib/node_modules
and source ~/.bashrc or logout and back in. That may not be best practices but it can get complicated and that basically works.
After those library packages are install we can have some fun with
$ node
> var GPS = require('gps');
> const SerialPort = require('serialport');
> const Readline = require('@serialport/parser-readline');
> var gps = new GPS;
> const port = new SerialPort('/dev/ttyS0', { baudRate: 4800 });
> const parser = new Readline();
> port.pipe(parser);
> parser.on('data', line => gps.update(line));
At this point you have an object 'gps' that is actually consuming and parsing the NMEA sentences from the EM-506! You can query it at the command line with gps, gps.state, gps.state.lat and gps.state.lon to get the current location, satellites being tracked, etc. That is the good part about asynchronous nodejs, that all is happening in the background!
There is also a script in the iotaHighwayTolls repo, readGPS.js for testing
Paying with IOTA code walkthru
The iota docs site has some examples to get started, we are using this one, "Transfer test IOTA tokens", as a starting point for the project. Feel free to play with all the examples to get a feel for setting up your seed and accounts, finding address, get free devnet tokens and especially learn the idea of never making a withdrawal from the same address twice! Every transfer out of an account exposes a part of the private key, so they should only be used once. Fortunately there are many, many addresses (9^57) to derive from a single seed, more than you can use in a lifetime at a rate of billions or more a second. However, that means that paying a 1i toll out of an address with 1950i in it leaves a remainder of 1949i that has to be transferred to a new address.
Creating addresses are fairly easy as it just takes your seed and an index, and the function getNewAddress() can return 2 in sequence, the current one with our bank in it, and the next one to transfer the remainder to. It takes a few minutes to run on the RPi Zero with only one processor.
There is a test script in my iotaHighwayTolls github repo getAddr.js that takes command line arguments and generates addresses from a seed.
$ cat /dev/urandom |tr -dc A-Z9|head -c${1:-81}
CKUDRSH9VHBXK9SCQPMEDBQALUYMAR9CIAZBPTLTEYOBRIIQRXRBUFHPTMIKPMLIOOVFHCRJESXXSJNCE
$ ./getAddr.js CKUDRSH9VHBXK9SCQPMEDBQALUYMAR9CIAZBPTLTEYOBRIIQRXRBUFHPTMIKPMLIOOVFHCRJESXXSJNCE 5 3
`GetNewAddressOptions`: 3 options are deprecated and will be removed in v.2.0.0.
[ 'TJARQZKNWOJJXGRICYR9MSTQIGOSHUWEEUFLCLXHWKDXLHTKHDYB9PADQFA9XCMUOMMQTTYERPHXYLACY',
'DIBBZEPUICMXMUJHWLFLGVKIMSUYQKSXOLPTJIZYMNHEYUDQOZWJHEQOEENCXNKVFTZZLXPUYYZZCOKJ9',
'VICZJWWNJEAJOLSGAYDCUHRUMW9GNOUQWL9CIRCKXEJETWHQMAGRKJWKYRWCKZJKBHGNNYYB9DDRKIHMD' ]
To make things easier, my current version uses an .ini library to keep the seed, current address index and toll collector address in a file. That file can be kept more secure than then script, but does need to be writable to update the index. Another library used is the Nexmo for sending sms texts that a toll has been paid.
A note about nodejs asynchronous functions - a few years ago developers would have to deal with functions that return and continue in your script, but have no results yet, that can lead to things working at the REPL prompt, but failing when run in real time. That often led to using a 'tree' of callbacks. We now have the 'promise' system and async functions that can 'await' before continuing which help with that issue.
My current script uses two main functions, checkGPS() which, if we are getting valid gps readings, loops thru an array of toll booth coordinates looking for when our present position is inside any of them. If that condition is met, a 'paid' flag is checked to make sure we have not paid in the last 5 minutes, and if not a message is composed of the latitude, longitude and vehicle identification VIN, which is passed to the iota function payToll(msg).
payToll(msg) reads the config.ini and gets the seed, current index and toll collector address. I use a different seed for the toll collector address to simulate being a completely different entity we are transferring value to. A transfer bundle is created with the toll collectors address, a tag 'TBCOLLECT' and the message with location and VIN. Then the two addresses are generated, one to withdraw from, and the next one to put the remainder in after paying the 1i toll. The balance of the first address is obtained as it will be needed in the transfer bundle. As a courtesy, if the balance is below an amount like 50i, an sms warning is sent to warn of running out of funds. Then an options array of current address with funds, it's index, balance, and remainder address is created, a bundle created, signed and sent. If all goes well and no errors trapped, then the address index is incremented and saved in config.ini.
Back in checkGPS() an sms text is sent to notify of a toll paid, a timer started to clear the 'paid' flag in 5 minutes, and the function schedules itself to run again in 5 seconds. Toll booth sizes are planned so at highway speeds with 5 second checks it can trigger a payment. At 120 KPH a toll of length 165 meters along the highway should read a vehicle within the 5 seconds it is present in the area.
Installation
After apt update / apt upgrade, installing node.js , the node script needs these modules
npm install -g geolib
npm install -g ini
npm install -g nexmo
npm install -g gps
npm install -g serialport
npm install -g '@serialport/parser-readline'
npm install -g @iota/core
npm install -g @iota/converter
Wherever you cloned the script to, add this to /etc/rc.local to start it up:
# if using this be sure no relative paths are in the js script
su pi -c 'export NODE_PATH=/usr/local/lib/node_modules; /usr/local/bin/node /home/pi/src/iotaHighwayTolls/GPS_payToll.js &'
Install the pi-shut systemd unit per the instructions.
To create your own local tollbooths for testing, I like google-earth-pro as it runs fine on Ubuntu Linux. Just find your area and create a 'path' (dots connected by lines in the top right) of a box, with red thick lines, like this one near Pilot Mountain NC
Then using the placemark pin to get the latitude and longitude of each corner in decimal degree format (set tools / options / decimal degrees) and create an array like this example that has two toll booths. This should be readable as a require('tollbooths.json') in node
tollbooths.json:
[ [ {"latitude":36.3473,"longitude":-80.4650},
{"latitude":36.3478,"longitude":-80.4639},
{"latitude":36.3459,"longitude":-80.4623},
{"latitude":36.3455,"longitude":-80.4634}
],
[ {"latitude":36.9452,"longitude":-81.0406},
{"latitude":36.9468,"longitude":-81.0410},
{"latitude":36.9475,"longitude":-81.0357},
{"latitude":36.9458,"longitude":-81.0355}
] ]
> const tollbooths = require('./tollbooths.json');
> tollbooths[0]
[ {"latitude":36.3473,"longitude":-80.4650},
{"latitude":36.3478,"longitude":-80.4639},
{"latitude":36.3459,"longitude":-80.4623},
{"latitude":36.3455,"longitude":-80.4634}
]
Setup config.ini with your seed and starting index, which would be zero if never used before, and would be the address to initially request iota from the faucet to deposit into, and the toll collectors address
You can also draw a tollbooth area nearby and walk into it for testing. One should run the script manually (wifi connect to the hostapd access point and ssh in, with cellular wan up) to verify everything is working and it should report something like this
$ node /home/pi/src/iotaHighwayTolls/GPS_payToll.js
checking 37.826903333333334, -122.42300666666667
checking 37.826903333333334, -122.42300666666667
Once you walk into a tollbooth area it will print out "in toll area" and proceed to process the payment. If you have the IOTA Trinity app setup on a cell phone with the devnet and your driver and toll collector seeds, you can see the funds transfer in 3 or 4 minutes.
Field Test
Since we had a drive to the coast during the contest, I created 21 tollbooths on the route there and back. All triggered but for 2 with cellular issues and 2 that we took a different route around for visits or change of plans. Here is a collage of six of them, two have the location of the reported message marked.
The two tolls paid in Coinjock and Pilot Mountain NC are these bundles:
Coinjock Bundle:
CUWUUFVWJSCHCECXJPPVIHBZ9LBKKAZANQE9BLWGANIUQJJHERITYIITWXKBZYVRIHZLXIYXGKGJWGJGD
Message: LAT36P3410NLON75P9547WVIN1FACP44MXMF168217
Location: 36.3410 N 75.9547 W
Pilot Mountain, NC:
GAFIAIWVKHQPWKLSIBXHADTTPUJVGTZTTDCTLZNKSHAOHXUKZGNF9GVILFAKZT9B9CSWNYFCEOBQGXDXB
Message: LAT36P3462NLON80P4633WVIN1FACP44MXMF168217
Location: 36.3462 N 80.4633 W
It is the first 1i output that has the message with the location and Vehicle Identification Number.
Future work and enhancements
Obviously this project so far is a very basic and primitive Proof of Concept, and to me it worked well enough to warrant further research. Some improvements that come to mind are:
1. Update the tollbooth location from a central website on startup and periodically when running, maybe check every hour, keeping a local copy and applying changes. In a real world project that would be growing and changing rapidly during roll out.
2. Make it connectivity tolerant, save bundles in a local database and mark them as transmitted or not, and continue trying to send those still pending, to be able to work in areas of spotty or limited cellular connectivity.
3. Optimize flow by pre-calculating the current and remainder address during the 5 minute 'paid' period for the NEXT transaction and store in config.ini. That would make payments almost immediate instead of having to wait the few minutes to get the addresses after hitting a toll area.
I must say it was certainly fun during our vacation trip to keep tabs on text messages and Trinity when passing thru toll areas to see if it worked or not!
Comments