1633

The cold season stresses every car battery. This project helps to prevent deep discharges and record the voltage over a longer timespan.

The original intent of this project was, that modern vehicles have increasingly sophisticated charging electronics that may not keep a battery 100% charged.
Thanks to the modern start-stop mechanisms and emission regulations, it is no secret that some of the batteries are only charged in overrun / propulsion operation.
In addition, the cold season also bothers batteries, which can cause sudden failures or deep discharges.

In order to get a constant state of charge of the car battery, a power-saving solution should be developed, which receives a short voltage measurement at certain intervals.
Further this value should be displayed on an power saving e-paper module, and stored for a longer graphical evaluation.
In order not to complicate the project, it was my goal to use as far as possible finished breakouts, and thus to keep the external wiring and layout creation as low as possible.
For example, the Arduino platform offered various breakouts with wireless radio standards, that allow strong radio signals without a lot of work.
Further, when used in a car, a suitable protective circuit of the hardware is necessary in order to remove interfering signals from the vehicle electrical system on the one hand and to protect the electronics used on the other hand.
For the reasons mentioned, a transmitter and a receiver were developed.

After hardware development and assembly, the development of an Arduino monitor software followed, in order to be able to serial control and debug both devices.
The logging function on an microSD card is also in function.
A continuously extended CSV file can be opened directly in Excel and can be evaluated graphically with just a few clicks.

The result ist very power saving for the car.
The current consumption of the transmitter is about 1mA in Idle mode, and 40mA for a very short transmitting process (about 1 second) every 100s.
The receiver needs about 40mA.


All functions are already fully realized, and work very well.

What ElektorLabs has changed

Hardware

After we started working on the project we did some changed to the hardware and the software for differen reasons. But lets begin step by step with the hardware. As transmitter the original design used the Adafruit Feather M0 with LoRa in a 433MHz version. Also a couple of modules we used in this design. This part is changed to the LoRa Nexus Board ( Atmega328p powered and including a RFM95 module), as ADC we use the internal 10 Bit one with a resistordevider in front of it. To supply the hardware we grabbed a Wüth DC/DC Module ( 173010578 ) we had left from an other project. This few parts we set up at a breadboard and software was programmed using an apropriate FTDI Cable ( one with 3.3V Level and VCC). We attached also a DHT22 sensor to capture humidity and temperature to give better data to see what to expect from the battery. The Batteryvoltage is messured on A1 and we used A0 for the DHT22 data pin.

For the receiver we have jsut the RMF95 as LoRa-Modem keept in place. As the receiver is usually in you home and can run of the grid on a wallplug we can replace the Adafruit Feather M0 with LoRa on the reveiver side with a complete raspberry pi (here a model 3b+). The RMF95 is attached with a few jumperwire to the pi. For the wireing we use the following:
 
RFM95               Raspberry Pi
SCK GPIO11
MISO GPIO09
MOSI GPIO10
NSS GPIO08
RESET GOIO22
DIO0 GPIO25

That is the hardwareside of the project. The rest is up to the Software.

Software

All code mentiond here can be access throuh our GitHub repository.
After we have talked about the pi, lets take a look at this side of the software. Based on the modified radiohead library from https://github.com/hallard/RadioHead a small application was build to just capture any packet comming on  869.5MHz. As modemparamter the default RFM95 LoRa ones have been used to stay in sync with the arduino ( transmitter ) side of the project. The rest of the Software is capturing any new data it receives and push it to a mqtt broker with the help o the libmosquitto library. The frequency and also the broker settings are fixed ( 127.0.0.1 ), menaing it expects on the same machine a broker to be present. Also the topics are hard coded here as we do a bit of porcessing using node-red.
One thing hat i have seen on my desk it that the rfm95 don't like to be used with jumper wire and may lead to instability, menaing unwanted resets of the module. Due to this the library has given a small patch to check the modem configuration and may do a reinit if something got wrong.

The software reads the packets we get from the radio and looks for a message from id 0x20 transmitted to 0x10 with a lenght of 12 byte payload. If this has been found the structure of the packet is expected as the following:



We use the 64Bit ID to identify that this is from our sensor, and process the rest to the different topics. For a MQTT setup with node red, you can have a look here. This infromaton is broken up to three topics "car\battery\voltage", "car\interior\temperature" and " car\interior\humidity", so we can process the values individually. The messages for the MQTT Broker are formed in JSON so we can easy move the elements arround. If you like to know more about MQTT you can have a look here (german book). To get the Software on the pi running after you have your MQTT broker and node-red running, we need some preparations to compile the code. In a terminal we need to run a few command to install some dependencys for the software
 
apt-get install libmosquittopp-dev
apt-get install libmosquittopp1
apt-get install libmosquitto1

Also we need the bcm2835 library installed on the pi , which you can get from here.
Extract the fiel and go into the directory.

From there run ./configure to prepare the code for compilation. After this is done and no errors appeard you can type
make
sudo make check
sudo make install

With this done the library should be in place. If this is all done we are ready to compile the code for the receiver part. Grab the code from the Git and copy it to the pi. Go to the directory and run make. After this ends with no errors the receiver is ready to be used. Sadly you must run this with root rights to get access to some raspberry pi subsystems, so ./sudo main will start the software.

To store the data you can use sqlite3 as databse engine. Install it with:
apt-get install sqlite3

This gives us access to the sqlite database. We will use that to store the infromation we got from our voltage monitor.
After this we go to the home folder of out pi user and create a database. Open a terminal and type:
 
cd ~
sqlite3 carsensor.db

this will create a new data base but we also need to provide tables to it
 
sqlite> CREATE TABLE battery_voltage ( id INTEGER PRIMARY KEY AUTOINCREMENT, uuid NUMERIC, voltage NUMERIC, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP);
sqlite> CREATE TABLE humidity ( id INTEGER PRIMARY KEY AUTOINCREMENT, uuid NUMERIC, humidity NUMERIC, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP);
sqlite> CREATE TABLE temperature ( id INTEGER PRIMARY KEY AUTOINCREMENT, uuid NUMERIC, temperature NUMERIC, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP);
sqlite> COMMIT;
sqlite> .exit

This will create the requiered tables for the database. Now we need also to install the nore-red :
 
cd ~/.node-red
npm i --unsafe-perm node-red-node-sqlite

after this has been done we need to restart the pi and have almost all components up and running. Afterwards we need to import the flow into node-red. 

Copy the following lines as show for the ESP32 Bedroom clock into node-red:
[
    {
        "id": "ed4fda19.d9467",
        "type": "tab",
        "label": "Flow 1",
        "disabled": false,
        "info": ""
    },
    {
        "id": "537a7633.f1b2c",
        "type": "mqtt-broker",
        "z": "",
        "name": "",
        "broker": "localhost",
        "port": "1883",
        "clientid": "",
        "usetls": false,
        "compatmode": true,
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "willTopic": "",
        "willQos": "0",
        "willPayload": ""
    },
    {
        "id": "6a4589cf.e9f658",
        "type": "ui_group",
        "z": "",
        "name": "RawData",
        "tab": "ebb3177a.a9a6f",
        "order": 1,
        "disp": true,
        "width": "6",
        "collapse": true
    },
    {
        "id": "3fbd176c.c51e78",
        "type": "ui_group",
        "z": "",
        "name": "Chart",
        "tab": "ebb3177a.a9a6f",
        "order": 2,
        "disp": true,
        "width": "6",
        "collapse": false
    },
    {
        "id": "ebb3177a.a9a6f",
        "type": "ui_tab",
        "z": "",
        "name": "MQTT-Sensor-Daten",
        "icon": "dashboard",
        "order": 0
    },
    {
        "id": "c1b9583b.f1765",
        "type": "ui_base",
        "theme": {
            "name": "theme-light",
            "lightTheme": {
                "default": "#0094CE",
                "baseColor": "#0094CE",
                "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif",
                "edited": true,
                "reset": false
            },
            "darkTheme": {
                "default": "#097479",
                "baseColor": "#097479",
                "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif",
                "edited": false
            },
            "customTheme": {
                "name": "Untitled Theme 1",
                "default": "#4B7930",
                "baseColor": "#4B7930",
                "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif"
            },
            "themeState": {
                "base-color": {
                    "default": "#0094CE",
                    "value": "#0094CE",
                    "edited": false
                },
                "page-titlebar-backgroundColor": {
                    "value": "#0094CE",
                    "edited": false
                },
                "page-backgroundColor": {
                    "value": "#fafafa",
                    "edited": false
                },
                "page-sidebar-backgroundColor": {
                    "value": "#ffffff",
                    "edited": false
                },
                "group-textColor": {
                    "value": "#1bbfff",
                    "edited": false
                },
                "group-borderColor": {
                    "value": "#ffffff",
                    "edited": false
                },
                "group-backgroundColor": {
                    "value": "#ffffff",
                    "edited": false
                },
                "widget-textColor": {
                    "value": "#111111",
                    "edited": false
                },
                "widget-backgroundColor": {
                    "value": "#0094ce",
                    "edited": false
                },
                "widget-borderColor": {
                    "value": "#ffffff",
                    "edited": false
                },
                "base-font": {
                    "value": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif"
                }
            },
            "angularTheme": {
                "primary": "indigo",
                "accents": "blue",
                "warn": "red",
                "background": "grey"
            }
        },
        "site": {
            "name": "Node-RED Dashboard",
            "hideToolbar": "false",
            "allowSwipe": "false",
            "lockMenu": "false",
            "allowTempTheme": "true",
            "dateFormat": "DD/MM/YYYY",
            "sizes": {
                "sx": 48,
                "sy": 48,
                "gx": 6,
                "gy": 6,
                "cx": 6,
                "cy": 6,
                "px": 0,
                "py": 0
            }
        }
    },
    {
        "id": "8a2edaec.9a7868",
        "type": "sqlitedb",
        "z": "",
        "db": "/home/pi/carsensor.db",
        "mode": "RWC"
    },
    {
        "id": "35f49e4.e322162",
        "type": "websocket-listener",
        "z": "",
        "path": "/lora/raw",
        "wholemsg": "false"
    },
    {
        "id": "89acb2e7.4ee32",
        "type": "mqtt in",
        "z": "ed4fda19.d9467",
        "name": "RAW LORA DATA",
        "topic": "lora_raw",
        "qos": "1",
        "broker": "537a7633.f1b2c",
        "x": 110,
        "y": 320,
        "wires": [
            [
                "91970165.27ddc8",
                "d0fe0016.9b1b08"
            ]
        ]
    },
    {
        "id": "91970165.27ddc8",
        "type": "debug",
        "z": "ed4fda19.d9467",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "x": 370,
        "y": 320,
        "wires": []
    },
    {
        "id": "4ab01d8f.b7f46c",
        "type": "mqtt in",
        "z": "ed4fda19.d9467",
        "name": "Car Battery Voltage",
        "topic": "car\\battery\\voltage",
        "qos": "1",
        "broker": "537a7633.f1b2c",
        "x": 110,
        "y": 440,
        "wires": [
            [
                "85c0503e.81db88"
            ]
        ]
    },
    {
        "id": "85c0503e.81db88",
        "type": "json",
        "z": "ed4fda19.d9467",
        "name": "Parsed JSON",
        "property": "payload",
        "action": "obj",
        "pretty": false,
        "x": 380,
        "y": 440,
        "wires": [
            [
                "b47b9b4.409e568",
                "eee63fcf.ddc9e"
            ]
        ]
    },
    {
        "id": "b47b9b4.409e568",
        "type": "function",
        "z": "ed4fda19.d9467",
        "name": "",
        "func": "var msg1={};\n\n\nswitch( msg.payload.uint ){\n    case \"uV\":{\n         msg1.payload = msg.payload.voltage/1000000;   \n    } break;\n   \n    case \"mV\":{\n         msg1.payload = msg.payload.voltage/1000;   \n    } break;\n    \n    case \"V\":{\n        /* do nothing */\n        msg1.payload = msg.payload.voltage;        \n    } break;\n    \n    \n   \n    \n    default:{\n        \n    }\n}\n\n\n\nreturn msg1;",
        "outputs": 1,
        "noerr": 0,
        "x": 730,
        "y": 440,
        "wires": [
            [
                "c3d02536.2048a8",
                "9aaf0a8a.e6ff5"
            ]
        ]
    },
    {
        "id": "c3d02536.2048a8",
        "type": "ui_chart",
        "z": "ed4fda19.d9467",
        "name": "",
        "group": "3fbd176c.c51e78",
        "order": 0,
        "width": 0,
        "height": 0,
        "label": "Batteryvoltage",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": "24",
        "removeOlderPoints": "1000",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": false,
        "colors": [
            "#1f77b4",
            "#aec7e8",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "useOldStyle": false,
        "x": 980,
        "y": 460,
        "wires": [
            [],
            []
        ]
    },
    {
        "id": "9aaf0a8a.e6ff5",
        "type": "ui_gauge",
        "z": "ed4fda19.d9467",
        "name": "Battery Voltage",
        "group": "3fbd176c.c51e78",
        "order": 3,
        "width": 0,
        "height": 0,
        "gtype": "gage",
        "title": "Battery Voltage",
        "label": "Volt",
        "format": "{{value}}",
        "min": 0,
        "max": "24",
        "colors": [
            "#ff0000",
            "#e6e600",
            "#00ff00"
        ],
        "seg1": "10.4",
        "seg2": "11.9",
        "x": 980,
        "y": 420,
        "wires": []
    },
    {
        "id": "fa19aac1.95561",
        "type": "mqtt in",
        "z": "ed4fda19.d9467",
        "name": "Car Temperature",
        "topic": "car\\interior\\temperature",
        "qos": "1",
        "broker": "537a7633.f1b2c",
        "x": 100,
        "y": 700,
        "wires": [
            [
                "41050644.f25878"
            ]
        ]
    },
    {
        "id": "41050644.f25878",
        "type": "json",
        "z": "ed4fda19.d9467",
        "name": "Parsed JSON",
        "property": "payload",
        "action": "obj",
        "pretty": false,
        "x": 380,
        "y": 700,
        "wires": [
            [
                "cf82d703.97ea78",
                "5225a0e4.e800c8"
            ]
        ]
    },
    {
        "id": "cf82d703.97ea78",
        "type": "function",
        "z": "ed4fda19.d9467",
        "name": "",
        "func": "var msg1={};\n\n\nswitch( msg.payload.uint ){\n    case \"kelvin\":{\n         msg1.payload = msg.payload.temperature-273.1;   \n    } break;\n   \n    case \"fahrenheit\":{\n         msg1.payload = (msg.payload.temperature-32)*5/9;   \n    } break;\n    \n    case \"celsius\":{\n        /* do nothing */\n        msg1.payload = msg.payload.temperature;        \n    } break;\n    \n    \n   \n    \n    default:{\n        \n    }\n}\n\n\n\nreturn msg1;",
        "outputs": 1,
        "noerr": 0,
        "x": 730,
        "y": 700,
        "wires": [
            [
                "6d7f2935.62fc18"
            ]
        ]
    },
    {
        "id": "6d7f2935.62fc18",
        "type": "ui_chart",
        "z": "ed4fda19.d9467",
        "name": "",
        "group": "3fbd176c.c51e78",
        "order": 1,
        "width": 0,
        "height": 0,
        "label": "Temperature",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": "24",
        "removeOlderPoints": "",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": false,
        "colors": [
            "#1f77b4",
            "#aec7e8",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "useOldStyle": false,
        "x": 970,
        "y": 700,
        "wires": [
            [],
            []
        ]
    },
    {
        "id": "79fadca9.de8e4c",
        "type": "mqtt in",
        "z": "ed4fda19.d9467",
        "name": "Car Humidity",
        "topic": "car\\interior\\humidity",
        "qos": "1",
        "broker": "537a7633.f1b2c",
        "x": 90,
        "y": 900,
        "wires": [
            [
                "71e28251.1a4794"
            ]
        ]
    },
    {
        "id": "71e28251.1a4794",
        "type": "json",
        "z": "ed4fda19.d9467",
        "name": "Parsed JSON",
        "property": "payload",
        "action": "obj",
        "pretty": false,
        "x": 380,
        "y": 900,
        "wires": [
            [
                "50b5ba6c.082ee4",
                "65ac8d39.77b40c"
            ]
        ]
    },
    {
        "id": "50b5ba6c.082ee4",
        "type": "function",
        "z": "ed4fda19.d9467",
        "name": "",
        "func": "var msg1={};\n\n\nmsg1.payload = msg.payload.humidity\n\n\nreturn msg1;",
        "outputs": 1,
        "noerr": 0,
        "x": 730,
        "y": 900,
        "wires": [
            [
                "530929e0.d2e5"
            ]
        ]
    },
    {
        "id": "530929e0.d2e5",
        "type": "ui_chart",
        "z": "ed4fda19.d9467",
        "name": "",
        "group": "3fbd176c.c51e78",
        "order": 1,
        "width": 0,
        "height": 0,
        "label": "Humidity",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": "24",
        "removeOlderPoints": "",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": false,
        "colors": [
            "#1f77b4",
            "#aec7e8",
            "#ff7f0e",
            "#2ca02c",
            "#98df8a",
            "#d62728",
            "#ff9896",
            "#9467bd",
            "#c5b0d5"
        ],
        "useOldStyle": false,
        "x": 960,
        "y": 900,
        "wires": [
            [],
            []
        ]
    },
    {
        "id": "4efe29a9.cc2cc",
        "type": "sqlite",
        "z": "ed4fda19.d9467",
        "mydb": "8a2edaec.9a7868",
        "sqlquery": "msg.topic",
        "sql": "",
        "name": "CarSensorLog",
        "x": 1340,
        "y": 620,
        "wires": [
            []
        ]
    },
    {
        "id": "eee63fcf.ddc9e",
        "type": "function",
        "z": "ed4fda19.d9467",
        "name": "",
        "func": "var msg1={};\nmsg1.topic =  \"insert into battery_voltage ( uuid,voltage ) VALUES(\" + msg.payload.uuid + \",\" + msg.payload.voltage + \")\";\nreturn msg1;",
        "outputs": 1,
        "noerr": 0,
        "x": 1010,
        "y": 360,
        "wires": [
            [
                "4efe29a9.cc2cc",
                "85e87935.f86f08"
            ]
        ]
    },
    {
        "id": "85e87935.f86f08",
        "type": "debug",
        "z": "ed4fda19.d9467",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "x": 1270,
        "y": 320,
        "wires": []
    },
    {
        "id": "5225a0e4.e800c8",
        "type": "function",
        "z": "ed4fda19.d9467",
        "name": "",
        "func": "var msg1={};\nmsg1.topic =  \"insert into temperature ( uuid,temperature ) VALUES(\" + msg.payload.uuid + \",\" + msg.payload.temperature + \")\";\nreturn msg1;",
        "outputs": 1,
        "noerr": 0,
        "x": 890,
        "y": 620,
        "wires": [
            [
                "91215a73.d44038",
                "4efe29a9.cc2cc"
            ]
        ]
    },
    {
        "id": "91215a73.d44038",
        "type": "debug",
        "z": "ed4fda19.d9467",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "x": 1050,
        "y": 580,
        "wires": []
    },
    {
        "id": "65ac8d39.77b40c",
        "type": "function",
        "z": "ed4fda19.d9467",
        "name": "",
        "func": "var msg1={};\nmsg1.topic =  \"insert into humidity ( uuid,humidity ) VALUES(\" + msg.payload.uuid + \",\" + msg.payload.humidity + \")\";\nreturn msg1;",
        "outputs": 1,
        "noerr": 0,
        "x": 970,
        "y": 840,
        "wires": [
            [
                "844fc145.4f6688",
                "4efe29a9.cc2cc"
            ]
        ]
    },
    {
        "id": "844fc145.4f6688",
        "type": "debug",
        "z": "ed4fda19.d9467",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "x": 1270,
        "y": 880,
        "wires": []
    },
    {
        "id": "d0fe0016.9b1b08",
        "type": "websocket out",
        "z": "ed4fda19.d9467",
        "name": "",
        "server": "35f49e4.e322162",
        "client": "",
        "x": 370,
        "y": 260,
        "wires": []
    }
]

The last peace on the receiver side we need to do is now to start the compiled software. Go to ~/LoRA_MQTT_VoltMeter and start the compiled programm with sudo ./main . This is the first step, later we will move the software to the pi autostart.

Now it's time to look at the transmitterside. We use the LoRa Nexus Board as base for the messurment side. We need to get a few librarys in place. This are a patched version of the DS2401 library you can find as download at out Git. Also you need This library relies on PaulStoffregen's OneWire Library. To save some  power we also need to have the LowPower library installed. To get the LoRa module running we also need to have a patched radiohead version installed. You can grab it also from the Git here. Also as we going to use a humidty and temperaturesensor we need the DHT library from adafruit, you can gab here .

The code simply initializes the hardware and will transmitt every 120 seconds a new messurment. This is done to comply with the maximum given airtime of 10% on the choosen band. For the prototype testing in the lab we we ended with the little breadboard for the circutry. If you now ask if this is ready to be installed into a car, no. You may read carfully in the march /april issue 2019 what problems are ahead if you try to integrate components in a car.

So far we attached the system to the MQTT broker and can now proces the data as we like. From the Ui it now looks like the following: