Voltaic Battery Voltage Adapter

A rundown of how to build an adapter to read the voltage from a Voltaic battery pack on a RAK19007 with RAK4631 and MeshCore.

About

Voltaic have two posts on getting the voltage at https://blog.voltaicsystems.com/reading-charge-level-of-voltaic-usb-battery-packs/ which was later updated on https://blog.voltaicsystems.com/updated-usb-c-pd-and-always-on-for-v25-v50-v75-batteries/ to include the V2 (aluminum body) packs on different pins. We’ll combine information from both of these to build an adapter to get some voltage.

I’ve split this into two parts: Hardware and Software. We need to make some minor firmware changes to read the voltage from another analog pin. For this some knowledge of PlatformIO and compiling the MeshCore firmware is necessary.

Let’s see how we can wrap up everything in the articles to get the voltage on a board like the RAK19007.

Parts and Tools

Required Parts:

Hardware Tools:

Software Tools:

Optional Stuff:

Pololu USB-C breakout board and packs of jumper wire

Hardware

I cut the jumpers in half since I am making multiple adapters. If you are only making a single adapter for a single version of pack then you will only need 2 wires (the divider and ground).

Jumper wires pre-cutJumper wires cut

I stripped a very small bit off the wires and tinned them. Then solder them up to the USB-C breakout board. The important part here is that you solder up the correct pins for the Voltaic. The V1 (plastic case) battery packs use the D+ pin. The V2 (aluminum case) packs use the SBU pins (either SBU pin is fine). I’m using the yellow wire for V1 packs and blue for V2.

Tinned wireSolder USB-C board

At this point you should be able to test the voltage divider from the pack. Make sure you are using a proper USB 3.1 cable if you are using a V2 pack or you’ll get nothing at all! Here you can see the V2 pack at 4 volts (using blue wire and ground) and the V1 pack at 3.6 volts (using yellow wire and ground). Multiply by two since that is the voltage divider inside the Voltaic.

Yes, that test breadboard has been through some stuff! ;)

Testing V2 wiringTesting V1 wiring

I’m using a RAK19007 as a testbed. You’ll need to solder the headers onto J10 and J11. We’re using pin #2 on J10 (which is ground) and pin #1 on J11 (which is the analog input pin AIN1).

RAK pinsRAK with adapter connected to header

Software

Both MeshCore and Meshtastic read battery voltage from an analog pin. That pin is usually baked into the specific board’s configuration. I’ll use MeshCore as an example on how we can change the pin to the external AIN1 that we used on the RAK19007.

In MeshCore the RAK4631 pins are set in variants/rak4631/RAK4631Board.h. PIN_VBAT_READ is the analog pin and ADC_MULTIPLIER is the multiplier. We need to wrap PIN_VBAT_READ and ADC_MULTIPLIER in #ifndef guards so they can be redefined in our new custom environment. MeshCore PR #3328 wraps them in #ifndef guards but until that gets merged we need to manually make these changes locally:

--- a/variants/rak4631/RAK4631Board.h
+++ b/variants/rak4631/RAK4631Board.h
@@ -5,8 +5,12 @@
 #include <helpers/NRF52Board.h>
 
 // built-ins
-#define  PIN_VBAT_READ    5
-#define  ADC_MULTIPLIER   (3 * 1.73 * 1.187 * 1000)
+#ifndef PIN_VBAT_READ
+  #define  PIN_VBAT_READ    5
+#endif
+#ifndef ADC_MULTIPLIER
+  #define  ADC_MULTIPLIER   (3 * 1.73 * 1.187 * 1000)
+#endif
 
 class RAK4631Board : public NRF52BoardDCDC {
 protected:

The same treatment goes to variants/rak4631/variant.h, which pins the LPCOMP channel used to wake the board back up from SYSTEMOFF when the battery recovers. That comparator watches a specific ADC channel, so it has to follow the sense pin:

--- a/variants/rak4631/variant.h
+++ b/variants/rak4631/variant.h
@@ -109,8 +109,12 @@ extern "C"
 #define PWRMGT_VOLTAGE_BOOTLOCK 3300   // Won't boot below this voltage (mV)
 // LPCOMP wake configuration (voltage recovery from SYSTEMOFF)
 // AIN3 = P0.05 = PIN_A0 / PIN_VBAT_READ
-#define PWRMGT_LPCOMP_AIN 3
-#define PWRMGT_LPCOMP_REFSEL 4  // 5/8 VDD (~3.13-3.44V)
+#ifndef PWRMGT_LPCOMP_AIN
+  #define PWRMGT_LPCOMP_AIN 3
+#endif
+#ifndef PWRMGT_LPCOMP_REFSEL
+  #define PWRMGT_LPCOMP_REFSEL 4  // 5/8 VDD (~3.13-3.44V)
+#endif
 
 // Other pins
 #define PIN_AREF (2)

With those guards in place, everything moves into platformio.local.ini.

We want P0.31 (WB_A1, which is AIN1 on J11). The Voltaic pack has its own divider so the multiplier has to change too. MeshCore PR #3328 wraps them in #ifndef guards.

Here is an example for the RAK4631 on the RAK19007 main board:

[env:RAK_4631_repeater_vbat_wb_a1]
extends = rak4631
build_flags =
  ${rak4631.build_flags}
  -D PIN_VBAT_READ=WB_A1
  -D PWRMGT_LPCOMP_AIN=7
  -D ADC_MULTIPLIER='(2 * 3.6 * 1000)'
  -D DISPLAY_CLASS=SSD1306Display
  -D ADVERT_NAME='"RAK4631 Repeater"'
  -D ADVERT_LAT=0.0
  -D ADVERT_LON=0.0
  -D ADMIN_PASSWORD='"password"'
  -D MAX_NEIGHBOURS=50
build_src_filter = ${rak4631.build_src_filter}
  +<helpers/ui/SSD1306Display.cpp>
  +<../examples/simple_repeater>

Everything from DISPLAY_CLASS down is just normal repeater configuration. Change the advert name, coordinates, and admin password to yours. The three that matter here are PIN_VBAT_READ, PWRMGT_LPCOMP_AIN, and ADC_MULTIPLIER. Save this to platformio.local.ini as a new file in the git clone root then build it like any other target:

pio run -e RAK_4631_repeater_vbat_wb_a1 -t upload

The repeater now reports the pack voltage instead of the onboard battery pin!

MeshCore app showing voltageMeshCore telemetry channels showing voltage

If you want to get fancy you can wrap up the adapter in a 3D printed case and some wire loom.

Adapter with case and wire loom

Thanks for reading!