diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 0000000..06a4a08 --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,10 @@ +.PHONY: all clean + + +all: + $(MAKE) -f Makefile.scan RELEASE=$(RELEASE) + $(MAKE) -f Makefile.connect RELEASE=$(RELEASE) + +clean: + $(MAKE) -f Makefile.scan clean + $(MAKE) -f Makefile.connect clean diff --git a/examples/Makefile.connect b/examples/Makefile.connect new file mode 100644 index 0000000..a7cfb49 --- /dev/null +++ b/examples/Makefile.connect @@ -0,0 +1,34 @@ +PROJECT_PATH := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))). + +TARGET = connect + +ifeq ($(RELEASE), yes) + +OPTLEVEL = 3 +DBGLEVEL = 0 +CPPDEFS = + +else + +OPTLEVEL = 0 +DBGLEVEL = 3 +CPPDEFS = DEBUG + +endif + + +#C++ Source files +CXXSRCS = $(PROJECT_PATH)/connect.cpp + + +#Include directories and macros +INCDIRS = $(PROJECT_PATH)/../inc + +CPPDEFS += + +CXXFLAGS += -Wno-unused-parameter +LDFLAGS += -lstdc++ -lbluetooth -ldbus-1 + +LIBS += $(PROJECT_PATH)/../powermon_lib.a + +include $(PROJECT_PATH)/build.mk diff --git a/examples/Makefile.scan b/examples/Makefile.scan new file mode 100644 index 0000000..0238184 --- /dev/null +++ b/examples/Makefile.scan @@ -0,0 +1,34 @@ +PROJECT_PATH := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))). + +TARGET = scan + +ifeq ($(RELEASE), yes) + +OPTLEVEL = 3 +DBGLEVEL = 0 +CPPDEFS = + +else + +OPTLEVEL = 0 +DBGLEVEL = 3 +CPPDEFS = DEBUG + +endif + + +#C++ Source files +CXXSRCS = $(PROJECT_PATH)/scan.cpp + + +#Include directories and macros +INCDIRS = $(PROJECT_PATH)/../inc + +CPPDEFS += + +CXXFLAGS += -Wno-unused-parameter +LDFLAGS += -lstdc++ -lbluetooth -ldbus-1 + +LIBS += $(PROJECT_PATH)/../powermon_lib.a + +include $(PROJECT_PATH)/build.mk diff --git a/examples/build.mk b/examples/build.mk new file mode 100644 index 0000000..bb6644a --- /dev/null +++ b/examples/build.mk @@ -0,0 +1,90 @@ +OUTDIR = bin +OBJDIR = $(OUTDIR)/obj + +TOOLCHAIN_PREFIX = +TOOLCHAIN_SUFFIX = + +CC = $(TOOLCHAIN_PREFIX)gcc$(TOOLCHAIN_SUFFIX) +CPP = $(TOOLCHAIN_PREFIX)cpp$(TOOLCHAIN_SUFFIX) +GDB = $(TOOLCHAIN_PREFIX)gdb$(TOOLCHAIN_SUFFIX) +SIZE = $(TOOLCHAIN_PREFIX)size$(TOOLCHAIN_SUFFIX) +OBJCOPY = $(TOOLCHAIN_PREFIX)objcopy$(TOOLCHAIN_SUFFIX) +OBJDUMP = $(TOOLCHAIN_PREFIX)objdump$(TOOLCHAIN_SUFFIX) +STRIP = $(TOOLCHAIN_PREFIX)strip$(TOOLCHAIN_SUFFIX) +READELF = $(TOOLCHAIN_PREFIX)readelf$(TOOLCHAIN_SUFFIX) +NM = $(TOOLCHAIN_PREFIX)nm$(TOOLCHAIN_SUFFIX) + +ECHO = echo +CP = cp +MKDIR = mkdir +SED = sed +MV = mv + + +ifeq ($(RELEASE), yes) + +$(info ******************************* Release build *******************************) + +else + +$(info ******************************* Debug build *******************************) + +endif + + +#update CFLAGS and LDFLAGS +CXXFLAGS += -fsigned-char -O$(OPTLEVEL) -g$(DBGLEVEL) $(addprefix -I, $(INCDIRS)) $(addprefix -D, $(CPPDEFS)) +LDFLAGS += -fsigned-char -O$(OPTLEVEL) -g$(DBGLEVEL) -Wl,-Map=$(OUTDIR)/$(TARGET).map,--gc-sections,--cref +#create list of all objects +ALLOBJS = $(addprefix $(OBJDIR)/, $(patsubst %.cpp, %.o, $(notdir $(CXXSRCS)))) +#create list of all object directories +NEWDIRS += $(sort $(dir $(ALLOBJS))) + + +#(1) = target +#(2) = source(s) +#(3) = extra flags +define COMPILE_CXX_SOURCE +$(1): $(MAKEFILE_LIST) $(2) | $(dir $(1)) + @$(ECHO) Compiling $(2) ...; + $(PREFIX)$(CC) -c $(3) -MD -pipe -o $(1) $(2); + $(EMPTY_LINE) + @$(CP) -R $(patsubst %.o, %.d, $(1)) $(patsubst %.o, %.dtemp, $(1)); + @$(SED) -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$$$//' -e '/^$$$$/ d' -e 's/$$$$/ :/' <$(patsubst %.o,%.d,$(1)) >>$(patsubst %.o,%.dtemp,$(1)); + @$(MV) $(patsubst %.o, %.dtemp, $(1)) $(patsubst %.o, %.d, $(1)); +endef + + +define CREATE_DIRECTORY +$(1)/: + @$(ECHO) Creating directory $(1) ... + @$(MKDIR) -p $(1) +endef + + +.PHONY: all clean + +all: $(OUTDIR)/$(TARGET) + +$(OUTDIR)/$(TARGET): $(OUTDIR)/$(TARGET).elf | $(OUTDIR)/ + @$(CP) -av $(OUTDIR)/$(TARGET).elf $(OUTDIR)/$(TARGET) + +clean: + @$(ECHO) Cleaning application files ... + -$(RM) -rf $(OUTDIR) + + +$(OUTDIR)/$(TARGET).elf: $(MAKEFILE_LIST) $(ALLOBJS) $(LIBS) | $(OUTDIR)/ + @$(ECHO) "Linking ..." + $(CC) -o $(OUTDIR)/$(TARGET).elf $(ALLOBJS) $(LIBS) $(LDFLAGS) +ifeq ($(RELEASE), yes) + $(STRIP) --strip-all $(OUTDIR)/$(TARGET).elf +endif + $(EMPTY_LINE) + + +$(foreach dir, $(OBJDIR), $(eval $(call CREATE_DIRECTORY, $(dir)))) +$(eval $(call CREATE_DIRECTORY, $(OUTDIR))) +$(foreach source, $(CXXSRCS), $(eval $(call COMPILE_CXX_SOURCE, $(OBJDIR)/$(notdir $(source:%.cpp=%.o)), $(source), $(CXXFLAGS)))) + +-include $(ALLOBJS:%.o=%.d) diff --git a/examples/connect.cpp b/examples/connect.cpp new file mode 100644 index 0000000..8c69b6c --- /dev/null +++ b/examples/connect.cpp @@ -0,0 +1,206 @@ +#include +#include + +#include +#include +#include +#include + +//this is the only file needed to be included +//also link the powermon_lib.a together with your application +//the following extra linker command line options are needed: +//-lstdc++ -lbluetooth -ldbus-1 +#include + + +volatile bool connected = false; +volatile bool disconnected = false; +volatile bool ready = false; + + + +static bool should_exit(void) +{ + pollfd pfd{}; + pfd.fd = STDIN_FILENO; + pfd.events = POLLIN; + + const int32_t ret = poll(&pfd, 1, 0); // timeout = 0 → non-blocking + if (ret > 0 && (pfd.revents & POLLIN)) + return true; + + return false; +} + + +int32_t main(int32_t argc, char** argv) +{ + printf("\nPowerMon Connect Example. Thornwave Labs Inc.\n"); + printf("\nPress ENTER to exit\n\n"); + + Powermon* const powermon = Powermon::createInstance(); + if (powermon == nullptr) + { + printf("\nCannot create the Powermon instance"); + return EXIT_FAILURE; + } + + + //Let's set our connect callback. This gets called when a connection is successfully established + powermon->setOnConnectCallback([]() + { + printf("\nDevice is connected\n"); fflush(stdout); + connected = true; + }); + + //... and our disconnect callback. This gets called when a device gets disconnected or a connection attempt fails. + powermon->setOnDisconnectCallback([](Powermon::DisconnectReason reason) + { + printf("\nDevice is disconnected, reason: %u\n", reason); fflush(stdout); + disconnected = true; + }); + + + + //Now we are ready to connect to a device + //Here are the options: + // - we can connect to a Bluetooth LE device (PowerMon or PowerMon-5S) + // - we can connect to a WiFi or Ethernet device located in the same network - we need an IP address + // - we can connect to a WiFi or Ethernet device located remotely - we need the Access Keys + + + + //WIFI / ETHERNET REMOTE CONNECTION + + //this is the URL needed to connect to a demo device + //we can use functions provided by the Powermon class to decode this URL into the access keys required to connect + const char* url = "https://applinks.thornwave.com/?n=DemoUnit&s=36129e86da98dda9&h=40&c=HzotUykUSvP/Ox0xdUpYVw%3D%3D&k=//////////////////////////////////////////8%3D"; + Powermon::DeviceIdentifier id; + if (id.fromURL(url)) + { + printf("\nURL decoded successfully"); + printf("\n Device name: %s", id.name.c_str()); + printf("\n Device model: %s", Powermon::getHardwareString(id.hardware_revision_bcd).c_str()); + printf("\n Device serial number: %016lX", id.serial); + + printf("\n Device channel ID: "); + for(uint32_t i = 0; i < CHANNEL_ID_SIZE; i++) + printf("%02X", id.access_key.channel_id[i]); + + printf("\n Device encryption key: "); + for(uint32_t i = 0; i < ENCRYPTION_KEY_SIZE; i++) + printf("%02X", id.access_key.encryption_key[i]); + + //let's connect + powermon->connectWifi(id.access_key); + } + else + { + printf("\nThe URL provided is invalid\n"); + return EXIT_FAILURE; + } + + + /* + //WIFI / ETHERNET LOCAL CONNECTION + struct in_addr ip_addr; + inet_pton(AF_INET, "192.168.1.230", &ip_addr); //use the IP address of your device. You can get that from the PowerMon advertisments. + powermon->connectWifi(ip_addr.s_addr); +*/ + + + /* + //BLE CONNECTION + const uint64_t ble_mac_address = (uint64_t)0x123456789ABC; //pay attention to big endian vs little endian + //use the MAC address of your device. You can get that from the PowerMon advertisments. + powermon->connectBle(ble_mac_address); +*/ + + + + //wait to either connect or fail + while (!connected && !disconnected) + usleep(10 * 1000); + + //as you noticed, the PowerMon API is asynchronous. This means, you make a request by calling a member function in + //the Powermon class and specifying a lambda as a parameter. That function returns immediately. The lambda will be + //called later when the response of the request arrives or the request times out. + //For this reason, in this example we need to keep the thread alive, hence the volatile bool connected, disconnected and ready. + //In your application, chances are that it's event driven and the Powermon library will fit nicely in that environment + + + if (connected) + { + //this is our first request sent to the device + //again, make abstraction of the ready flag. It's here because we need to keep this thread blocked until we get a response + //It is highly recommended to start any connection with this request. Also keep the DeviceInfo structure because + //you may need information about this device to make decisions about how to interpret the data + ready = false; + powermon->requestGetInfo([](uint16_t status, const Powermon::DeviceInfo& info) + { + if (status == Powermon::RSP_SUCCESS) + { + printf("\nDevice Information\n-----------------\n"); + + printf("\nDevice name: %s", info.name.c_str()); + printf("\nFirmware version: %x.%02x", info.firmware_version_bcd >> 8, info.firmware_version_bcd & 0xFF); + printf("\nHardware ID: %x.%x", info.hardware_revision_bcd >> 4, info.hardware_revision_bcd & 0xF); + + fflush(stdout); + } + else + { + printf("\r\nError retrieving device information"); + } + + ready = true; + }); + while(ready == false) usleep(50 * 1000); //wait until we get a response + + + printf("\n\nMonitor Data"); + + while(!should_exit()) + { + powermon->requestGetMonitorData([](Powermon::ResponseCode response, const Powermon::MonitorData& data) + { + if (response == Powermon::ResponseCode::RSP_SUCCESS) + { + printf("\nV1: %.3fV, V2: %.3fV, I: %.3fA, P: %.2fW, Coulombs: %.3fAh, Energy: %.3fWh, PS: %s", + data.voltage1, data.voltage2, data.current, data.power, + data.coulomb_meter / 1000.0, data.energy_meter / 1000.0, + Powermon::getPowerStatusString(data.power_status).c_str() + ); + } + else + { + printf("\nFailed to get monitor data. Response code: %u", response); + } + + fflush(stdout); + }); + + //delay 2 seconds and check if we should exit every 10ms. + //This is done so we can respond quickly to the user request of terminating the program. + for(uint32_t i = 0; i < 200; i++) + if (!should_exit()) + usleep(10 * 1000); + else + break; + } + + + powermon->disconnect(); + } + + + //wait for the device to disconnect. If the connection failed when trying to establish it, disconnected will already be true + while (!disconnected) + usleep(50 * 1000); + + delete powermon; + + printf("\n"); + + return EXIT_SUCCESS; +} diff --git a/examples/scan.cpp b/examples/scan.cpp new file mode 100644 index 0000000..ada1aa7 --- /dev/null +++ b/examples/scan.cpp @@ -0,0 +1,78 @@ +#include +#include + +#include +#include +#include + + +//this is the only file needed to be included +//also link the powermon_lib.a together with your application +//the following extra linker command line options are needed: +//-lstdc++ -lbluetooth -ldbus-1 +#include + + +static bool should_exit(void) +{ + pollfd pfd{}; + pfd.fd = STDIN_FILENO; + pfd.events = POLLIN; + + const int32_t ret = poll(&pfd, 1, 0); // timeout = 0 → non-blocking + if (ret > 0 && (pfd.revents & POLLIN)) + return true; + + return false; +} + + +int32_t main(int32_t argc, char** argv) +{ + printf("\nPowerMon Scanner Example. Thornwave Labs Inc.\n"); + printf("\nPress ENTER to exit\n\n"); + + PowermonScanner* scanner = PowermonScanner::createInstance(); + if (scanner == nullptr) + { + printf("\nCannot create the PowermonScanner instance"); + return EXIT_FAILURE; + } + + //the PowerMon access libraries make extensive use of lambdas (closures). + //here we set the lambda that will be called by the library when it received an advertisment from a PowerMon device + //PowermonScanner::Advertisement contains the advertisment + scanner->setCallback([](const PowermonScanner::Advertisement &adv) + { + printf("Name: %-16s, Model: %-12s, Serial: %016lX, Firmware: %X.%02X\n", + adv.name.c_str(), + Powermon::getHardwareString(adv.hardware_revision_bcd).c_str(), + adv.serial, + adv.firmware_version_bcd >> 8, adv.firmware_version_bcd & 0xFF); + + printf("\tVoltage1: %.3fV, Current: %.3fA, Power: %.2fW\n", adv.voltage1, adv.current, adv.power); + + fflush(stdout); + + //here we just print some of the information. There is more available in "PowermonScanner::Advertisement" + //you may chose to compile a list that you can present to the user. "PowermonScanner::Advertisement.serial" is unique among all PowerMon + //devices so you can use it as a primary key + }); + + scanner->startBleScan(); + scanner->startWifiScan(); + + for(;;) + { + if (should_exit()) + break; + usleep(10000); + } + + scanner->stopBleScan(); + scanner->stopWifiScan(); + + delete scanner; + + return EXIT_SUCCESS; +} diff --git a/inc/powermon.h b/inc/powermon.h new file mode 100644 index 0000000..2fe5a54 --- /dev/null +++ b/inc/powermon.h @@ -0,0 +1,882 @@ +/* Copyright (C) 2020 - 2024, Thornwave Labs Inc + * Written by Razvan Turiac + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the “Software”), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * Attribution shall be given to Thornwave Labs Inc. and shall be made visible to the final user. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef _POWERMON_H +#define _POWERMON_H + + +#include +#include + +#include +#include +#include + + +#include +#include +#include + + +#define MAX_WIFI_SSID_SIZE 32 +#define MAX_WIFI_PASSWORD_SIZE 64 + +#define CHANNEL_ID_SIZE 16 +#define ENCRYPTION_KEY_SIZE 32 + +#define MAX_BLE_NAME_LENGTH 8 +#define MAX_NAME_LENGTH 32 + +#define MAX_TIMER_NAME_LENGTH 16 +#define MAX_TIMER_COUNT 16 + +#define FG_SOC_DISABLED 0xFF +#define FG_SOC_UNKNOWN 0xFE + +#define FG_RUNTIME_DISABLED 0xFFFF +#define FG_RUNTIME_UNKNOWN 0xFFFE +#define FG_RUNTIME_MAX 0xFFF0 + + +/** + * \brief Powermon is a class representing one PowerMon device (BLE or WiFi) and offers a set of functions for accessing all features of PowerMon battery monitors. + */ +class Powermon +{ +public: + /** + * \brief HardwareRevision definitions (2 digit BCD format) + */ + enum HardwareRevision: uint8_t + { + FAMILY_MASK = 0xF0, /// &cb) = 0; + + + /** + * \brief Sets the callback to be called by the driver when a connection to the PowerMon device is disconnected + * \param cb Lambda of type `void(Powermmon::DisconnectReason)` + */ + virtual void setOnDisconnectCallback(const std::function &cb) = 0; + + + /** + * \brief Sets the callback to be called by the driver when new monitor data is received. This applies to the BLE devices only. + * For the WiFi devices, use the request to retrieve the monitor data. + * \param cb Lambda of type `void(Powermon::MonitorData&)` + */ + virtual void setOnMonitorDataCallback(const std::function &cb) = 0; + + + /** + * \brief Sets the callback to be called by the driver when a new WiFi scan result is received from the PowerMon. This applies to the WiFi devices only. + * WiFi scanning can be initiated using requestStartWifiScan(). + * \param cb Lambda of type `void(Powermon::WifiScanResult*)` + * \param result Pointer to a WiFiScanResult structure describing a WiFi network. The pointer is only valid inside the scope of the callback closure. + * Do not store this pointer. Result can be nullptr to signal the WiFi scan ending. + */ + virtual void setOnWifiScanReportCallback(const std::function &cb) = 0; + + + /** + * \brief Returns the last DeviceInfo retrieved from the PowerMon + * \return Reference to internal DeviceInfo structure. This is not valid before the first requestGetInfo() that returns success. + */ + virtual const DeviceInfo& getLastDeviceInfo(void) const = 0; + + + /** + * \brief Requests the device information + * \param cb Lambda of type void(ResponseCode, const DeviceInfo&) that will be called to signal the result of the request + */ + virtual void requestGetInfo(const std::function &cb) = 0; + + + /** + * \brief Requests the device monitor data. It only applies to the WiFi devices. + * \param cb Lambda of type void(ResponseCode, const MonitorData&) that will be called to signal the result of the request + */ + virtual void requestGetMonitorData(const std::function &cb) = 0; + + + /** + * \brief Requests the device power monitor statistics data + * \param cb Lambda of type void(ResponseCode, const MonitorStatistics&) that will be called to signal the result of the request + */ + virtual void requestGetStatistics(const std::function &cb) = 0; + + + /** + * \brief Requests the device battery statistics data + * \param cb Lambda of type void(ResponseCode, const FuelgaugeStatistics&) that will be called to signal the result of the request + */ + virtual void requestGetFgStatistics(const std::function &cb) = 0; + + + /** + * \brief Requests unlocking a password protected device + * \param key 32 bytes AuthKey type used to unlock the device. This is typically the SHA256 hash of a password. + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestUnlock(const AuthKey &key, std::function cb) = 0; + + + /** + * \brief Requests setting a user password lock + * \param key 32 bytes AuthKey type used to unlock the device. This is typically the SHA256 hash of a password. + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestSetUserPasswordLock(const AuthKey &key, std::function cb) = 0; + + + /** + * \brief Requests setting a master password lock on + * \param key 32 bytes AuthKey type used to unlock the device. This is typically the SHA256 hash of a password. + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestSetMasterPasswordLock(const AuthKey &key, std::function cb) = 0; + + + /** + * \brief Requests clearing of the user password lock + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestClearUserPasswordLock(std::function cb) = 0; + + + /** + * \brief Requests clearing of the master password lock + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestClearMasterPasswordLock(std::function cb) = 0; + + + /** + * \brief Requests the authentication key from the device. This key can be used to unlock a locked device. It acts the same way as the user password lock. + * \param cb Lambda of type void(ResponseCode, const AuthKey&) that will be called to signal the result of the request + */ + virtual void requestGetAuthKey(std::function cb) = 0; + + + /** + * \brief Requests the reset of the device authentication key + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestResetAuthKey(std::function cb) = 0; + + + /** + * \brief Requests the reset of the energy meter + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestResetEnergyMeter(const std::function &cb) = 0; + + + /** + * \brief Requests the reset of the coulomb meter + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestResetCoulombMeter(const std::function &cb) = 0; + + + /** + * \brief Requests the reset of the power meter statistics + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestResetStatistics(const std::function &cb) = 0; + + + /** + * \brief Requests changing the power state + * \param state New power state (ON / OFF) + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestSetPowerState(bool state, const std::function &cb) = 0; + + + /** + * \brief Retrieves the device configuration structure + * \param cb Lambda of type void(ResponseCode, const PowermonConfig&) that will be called to signal the result of the request + */ + virtual void requestGetConfig(const std::function &cb) = 0; + + + /** + * \brief Sends new configuration to the device + * \param config New configuration structure + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestSetConfig(const PowermonConfig &config, const std::function &cb) = 0; + + + /** + * \brief Resets the PowerMon configuration to factory settings + * This will also clear the data log, reset the name, authentication keys and access keys (for WiFi devices). + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request. + */ + virtual void requestResetConfig(const std::function &cb) = 0; + + + /** + * \brief Requests renaming the PowerMon device + * \param name New name. For Bluetooth PowerMons the name is limited to 8 characters. For WiFi PowerMons the name can be up to 32 characters in length. + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestRename(const char* name, const std::function &cb) = 0; + + + /** + * \brief Requests setting the internal clock of the PowerMon device + * \param time New clock in UNIX format (number of seconds since Jan 1st, 1970) in localtime (not UTC) + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestSetTime(uint32_t time, const std::function &cb) = 0; + + + /** + * \brief Requests forcing the SoC to 100% (SoC synchronize) + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestFgSynchronize(const std::function &cb) = 0; + + + /** + * \brief Requests starting the WiFi scan (only applies to the WiFi PowerMons). WiFi scanning will stop automatically after a max of 5 seconds. + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestStartWifiScan(const std::function &cb) = 0; + + + /** + * \brief Sends new WiFi network credentials to the PowerMon device. + * The new credentials will be saved by the device whether PowerMon can or cannot connect to that specified network. + * \param network WiFi network credentials + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestWifiConfigure(const WifiNetwork &network, const std::function &cb) = 0; + + + /** + * \brief Returns a list of all WiFi networks known by the device. + * \param cb Lambda of type void(ResponseCode, const std::vector&) that will be called to signal the result of the request + */ + virtual void requestGetWifiNetworks(const std::function&)> &cb) = 0; + + /** + * \brief Add a new WiFi network + * The new credentials will be saved by the device whether PowerMon can or cannot connect to that specified network. + * \param network WiFi network credentials + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestAddWifiNetwork(const WifiNetwork& network, const std::function &cb) = 0; + + /** + * \brief Removes a stored WiFi network + * \param index WiFi network index in the list returned by requestGetWifiNetworks() + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestRemoveWifiNetwork(uint8_t index, const std::function &cb) = 0; + + + /** + * \brief Requests the WiFi access keys (only applies to the WiFi PowerMons). The access keys are used to remotely access the device. + * \param cb Lambda of type void(ResponseCode, const WifiAccessKey&) that will be called to signal the result of the request + */ + virtual void requestGetAccessKeys(const std::function &cb) = 0; + + + /** + * \brief Requests resetting of the WiFi access keys (only applies to the WiFi PowerMons). This effectively severs the connection to all the paired clients. + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestResetAccessKeys(const std::function &cb) = 0; + + + /** + * \brief Requests zeroing of the current reading offset + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestZeroCurrentOffset(const std::function &cb) = 0; + + + /** + * \brief Requests calibration of the current reading + * \param value The actual current flowing through the shunt. An accurate multimeter is required to measure the current. + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestCalibrateCurrent(float value, const std::function &cb) = 0; + + + /** + * \brief Requests the list of all schedules stored in the device + * \param cb Lambda of type void(ResponseCode, const std::vector&) that will be called to signal the result of the request + */ + virtual void requestGetSchedules(const std::function&)> &cb) = 0; + + + /** + * \brief Requests adding new schedules + * \param schedules A list of schedules to add + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestAddSchedules(const std::vector &schedules, const std::function &cb) = 0; + + + /** + * \brief Requests updating an existing schedule + * \param old_schedule_descriptor The descriptor of the schedule to update + * \param new_schedule The new schedule + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestUpdateSchedule(uint64_t old_schedule_descriptor, const PowermonSchedule &new_schedule, const std::function &cb) = 0; + + + /** + * \brief Requests deleting an existing schedule + * \param schedule_descriptor The descriptor of the schedule to delete + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestDeleteSchedule(uint64_t schedule_descriptor, const std::function &cb) = 0; + + + /** + * \brief Requests clearing of all schedule + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestClearSchedules(const std::function &cb) = 0; + + + /** + * \brief Requests committing the schedules to non-volatile memory + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestCommitSchedules(const std::function &cb) = 0; + + + /** + * \brief Requests the list of log files + * \param cb Lambda of type void(ResponseCode, const std::vector&) that will be called to signal the result of the request + */ + virtual void requestGetLogFileList(const std::function&)> &cb) = 0; + + + /** + * \brief Requests reading of a log file + * \param file_id ID of the file to read (obtained from the LogFileDescriptor) + * \param offset Offset to read from + * \param read_size Read size in bytes + * \param cb Lambda of type void(ResponseCode, const uint8_t*, size_t) that will be called to signal the result of the request + */ + virtual void requestReadLogFile(uint32_t file_id, uint32_t offset, uint32_t read_size, const std::function &cb) = 0; + + + /** + * \brief Requests committing the schedules to non-volatile memory + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestClearLog(const std::function &cb) = 0; + + + /** + * \brief Requests firmware update + * \param firmware_image The firmware update image + * \param size Size of the firmware update image + * \param progress_cb Lambda of type bool(uint32_t progress, uint32_t total) that will be called regularly with updates about the progress. + * Returning false fronm the lambda will abort the update operation. + * \param done_cb Lambda of type void(ResponseCode) that will be called to signal the result of the request upon completion of the firmware update + */ + virtual void requestUpdateFirmware(const uint8_t* firmware_image, uint32_t size, const std::function &progress_cb, + const std::function &done_cb) = 0; + + + + virtual void requestReadDebug(uint32_t offset, uint32_t read_size, const std::function &cb) = 0; + virtual void requestEraseDebug(const std::function &cb) = 0; + + + virtual void requestReboot(const std::function &cb) = 0; + + + + /** + * \brief Returns the IP address as string + */ + static std::string getIpAddressString(uint32_t ip); + + + /** + * \brief Returns the Bluetooth MAC address as string + */ + static std::string getMacAddressString(uint64_t mac); + + + /** + * \brief Parses a MAC address string + */ + static uint64_t parseMacAddress(const char* address); + + + /** + * \brief Returns the hardware name based on the hardware revision in BCD format + */ + static std::string getHardwareString(uint8_t bcd); + + + /** + * \brief Returns the power status string representation + */ + static std::string getPowerStatusString(PowerStatus ps); + + + /** + * \brief Returns true if the PowerMon described by the BCD hardware revision has data logging capabilities + */ + static bool hasDataLog(uint8_t bcd); + + + /** + * \brief Returns true if the PowerMon described by the BCD hardware revision has V2 + */ + static bool hasVoltage2(uint8_t bcd); + + + /** + * \brief Returns true if the PowerMon described by the BCD hardware revision supports configurable shunts + */ + static bool hasConfigurableShunt(uint8_t bcd); + + + /** + * \brief Returns true if the PowerMon described by the BCD hardware revision has an initegrated shunt + */ + static bool hasIntegratedShunt(uint8_t bcd); + + + /** + * \brief Returns true if the PowerMon described by the BCD hardware revision has WiFi + */ + static bool hasWifi(uint8_t bcd); + + + /** + * \brief Returns true if the PowerMon described by the BCD hardware revision has Ethernet + */ + static bool hasEthernet(uint8_t bcd); + + + /** + * \brief Returns true if the PowerMon described by the BCD hardware revision has Ethernet + */ + static bool hasNetwork(uint8_t bcd); + + + /** + * \brief Returns true if the PowerMon described by the BCD hardware revision has Bluetooth + */ + static bool hasBluetooth(uint8_t bcd); + + + /** + * \brief Returns true if the parameter is a valid BCD number + */ + static inline bool checkBCD(uint16_t bcd) + { + for(uint32_t i = 0; i < 4; i++) + { + if ((bcd & 0xF) > 0x9) + return false; + bcd >>= 4; + } + + return true; + } + + /** + * \brief Generates the SHA256 hash of a password + * \param password C string containing the password + * \return Authentication key that can be used for the lock / unlock functions + */ + static AuthKey getAuthKeyFromPassword(const char* password); + + /** + * \brief Returns the update firmware image URL based on the hardware revision and firmware version. + */ + static std::string getUpdateFirmwareImageUrl(uint8_t hardware_revision_bcd, uint16_t firmware_revision_bcd); + + /** + * \brief Checks the validity of the firmware update image. + */ + static uint16_t checkFirmwareImage(const uint8_t* image, size_t size, uint8_t hardware_revision_bcd); +}; + + +#include + + +#endif diff --git a/inc/powermon_config.h b/inc/powermon_config.h new file mode 100644 index 0000000..bacf35c --- /dev/null +++ b/inc/powermon_config.h @@ -0,0 +1,303 @@ +/* Copyright (C) 2020 - 2024, Thornwave Labs Inc + * Written by Razvan Turiac + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the “Software”), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * Attribution shall be given to Thornwave Labs Inc. and shall be made visible to the final user. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef _POWERMON_CONFIG_H +#define _POWERMON_CONFIG_H + + +#include +#include + + +/** + * \brief PowermonConfig is a structure containing the PowerMon configuration + */ +struct PowermonConfig +{ +public: + + enum MultiFunctionPinMode: uint32_t + { + MF_DATA = 0, //data stream + MF_TEMP = 1, //DS18B20 temperature sensor + MF_BUTTON = 2, //button input + MF_COMM = 3 //half duplex communication + }; + + + enum FuelgaugeChemistry: uint32_t + { + FG_CHEM_LEAD_FLOODED = 0, + FG_CHEM_LEAD_AGM = 1, + FG_CHEM_LIFEPO = 2, + FG_CHEM_LIION = 3, + FG_CHEM_LIPOLY = 4, + }; + + + enum LogMode: uint32_t + { + LOG_MODE_DISABLED = 0, + LOG_MODE_1_SEC = 1, + LOG_MODE_2_SEC = 2, + LOG_MODE_5_SEC = 3, + LOG_MODE_10_SEC = 4, + LOG_MODE_20_SEC = 5, + LOG_MODE_30_SEC = 6, + LOG_MODE_60_SEC = 7, + }; + + + enum TimeZone: uint32_t + { + TZ_AMERICA_ATLANTIC = 0, + TZ_AMERICA_EASTERN = 1, + TZ_AMERICA_CENTRAL = 2, + TZ_AMERICA_MOUNTAIN = 3, + TZ_AMERICA_PACIFIC_NO_DST = 4, + TZ_AMERICA_PACIFIC = 5, + TZ_AMERICA_ALASKA = 6, + TZ_AMERICA_HAWAII = 7, + TZ_AMERICA_SAMOA = 8, + TZ_AMERICA_CHAMORO = 9, + TZ_EUROPE_WESTERN = 10, + TZ_EUROPE_CENTRAL = 11, + TZ_EUROPE_EASTERN = 12, + TZ_EUROPE_MOSCOW = 13 + }; + + static const char* timeZones[14]; + + void setMfMode(MultiFunctionPinMode mode); + MultiFunctionPinMode getMfMode(void) const; + + void setWiFiKeepAPOn(bool state); + bool getWiFiKeepAPOn(void) const; + + void setWiFiDisablePowerSaving(bool state); + bool getWiFiDisablePowerSaving(void) const; + + void setWiFiWatchdogEnable(bool state); + bool getWiFiWatchdogEnable(void) const; + + void setNoInternetEnable(bool state); + bool getNoInternetEnable(void) const; + + void setNtpEnable(bool state); + bool getNtpEnable(void) const; + + void setTimeZone(TimeZone tz); + TimeZone getTimeZone(void) const; + + void setEthKeepLinkLedOn(bool state); + bool getEthKeepLinkLedOn(void) const; + + void setEngineShuntVdropNom(uint8_t mv); + uint8_t getEngineShuntVdropNom(void) const; + + void setEngineShuntCurrentNom(uint16_t amperes); + uint16_t getEngineShuntCurrentNom(void) const; + + void setEngineShuntCurrentMax(uint16_t amperes); + uint16_t getEngineShuntCurrentMax(void) const; + + void setEngineDisableV2(bool state); + bool getEngineDisableV2(void) const; + + void setEngineCurrentSignFlip(bool state); + bool getEngineCurrentSignFlip(void) const; + + void setEngineMeterVoltageSource(uint8_t source); + uint8_t getEngineMeterVoltageSource(void) const; + + void setOcInitialState(bool state); + bool getOcInitialState(void) const; + + void setOcInvertOutput(bool state); + bool getOcInvertOutput(void) const; + + void setOcLatchRelayOn(bool state); + bool getOcLatchRelayOn(void) const; + + void setOcConnectFilter(uint32_t filter_ms); + uint32_t getOcConnectFilter(void) const; + + void setOcLvdEnable(bool state); + bool getOcLvdEnable(void) const; + + void setOcLvdSource(uint8_t source); + uint8_t getOcLvdSource(void) const; + + void setOcLvdDisconnectThreshold(float threshold); + float getOcLvdDisconnectThreshold(void) const; + + void setOcLvdConnectThreshold(float threshold); + float getOcLvdConnectThreshold(void) const; + + void setOcLvdDisconnectFilter(uint32_t filter_ms); + uint32_t getOcLvdDisconnectFilter(void) const; + + void setOcHvdEnable(bool state); + bool getOcHvdEnable(void) const; + + void setOcHvdSource(uint8_t source); + uint8_t getOcHvdSource(void) const; + + void setOcHvdDisconnectThreshold(float threshold); + float getOcHvdDisconnectThreshold(void) const; + + void setOcHvdConnectThreshold(float threshold); + float getOcHvdConnectThreshold(void) const; + + void setOcHvdDisconnectFilter(uint32_t filter_ms); + uint32_t getOcHvdDisconnectFilter(void) const; + + void setOcOcdEnable(bool state); + bool getOcOcdEnable(void) const; + + void setOcOcdSource(uint8_t source); + uint8_t getOcOcdSource(void) const; + + void setOcOcdTripThreshold(float threshold); + float getOcOcdTripThreshold(void) const; + + void setOcOcdTripFilter(uint32_t filter_ms); + uint32_t getOcOcdTripFilter(void) const; + + void setOcLtdEnable(bool state); + bool getOcLtdEnable(void) const; + + void setOcLtdDisconnectThreshold(int8_t threshold); + int8_t getOcLtdDisconnectThreshold(void) const; + + void setOcLtdConnectThreshold(int8_t threshold); + int8_t getOcLtdConnectThreshold(void) const; + + void setOcLtdDisconnectFilter(uint32_t filter_ms); + uint32_t getOcLtdDisconnectFilter(void) const; + + void setOcHtdEnable(bool state); + bool getOcHtdEnable(void) const; + + void setOcHtdDisconnectThreshold(int8_t threshold); + int8_t getOcHtdDisconnectThreshold(void) const; + + void setOcHtdConnectThreshold(int8_t threshold); + int8_t getOcHtdConnectThreshold(void) const; + + void setOcHtdDisconnectFilter(uint32_t filter_ms); + uint32_t getOcHtdDisconnectFilter(void) const; + + void setOcFgdConnectEnable(bool state); + bool getOcFgdConnectEnable(void) const; + + void setOcFgdDisconnectEnable(bool state); + bool getOcFgdDisconnectEnable(void) const; + + void setOcFgdConnectThreshold(uint8_t threshold); + uint8_t getOcFgdConnectThreshold(void) const; + + void setOcFgdDisconnectThreshold(uint8_t threshold); + uint8_t getOcFgdDisconnectThreshold(void) const; + + void setOcAutoOnTimer(uint32_t timer_sec); + uint32_t getOcAutoOnTimer(void) const; + + void setOcAutoOffTimer(uint32_t timer_sec); + uint32_t getOcAutoOffTimer(void) const; + + void setOcGenControlEnable(bool state); + bool getOcGenControlEnable(void) const; + + void setOcGenVonEnable(bool state); + bool getOcGenVonEnable(void) const; + + void setOcGenVoffEnable(bool state); + bool getOcGenVoffEnable(void) const; + + void setOcGenSoconEnable(bool state); + bool getOcGenSoconEnable(void) const; + + void setOcGenSocoffEnable(bool state); + bool getOcGenSocoffEnable(void) const; + + void setOcGenVoltageSource(uint8_t source); + uint8_t getOcGenVoltageSource(void) const; + + void setOcGenVonThreshold(float threshold); + float getOcGenVonThreshold(void) const; + + void setOcGenVoffThreshold(float threshold); + float getOcGenVoffThreshold(void) const; + + void setOcGenSoconThreshold(uint8_t threshold); + uint8_t getOcGenSoconThreshold(void) const; + + void setOcGenSocoffThreshold(uint8_t threshold); + uint8_t getOcGenSocoffThreshold(void) const; + + void setOcGenVonFilter(uint32_t filter_ms); + uint32_t getOcGenVonFilter(void) const; + + void setOcGenTurnOffDelay(uint16_t delay_min); + uint16_t getOcGenTurnOffDelay(void) const; + + void setOcLiFePOEnable(bool state); + bool getOcLiFePOEnable(void) const; + + void setOcLiFePODesignCapacity(float capacity); + float getOcLiFePODesignCapacity(void) const; + + void setOcLiFePOCellCount(uint8_t count); + uint8_t getOcLiFePOCellCount(void) const; + + void setFgEnable(bool state); + bool getFgEnable(void) const; + + void setFgChemistry(FuelgaugeChemistry chemistry); + FuelgaugeChemistry getFgChemistry(void) const; + + void setFgCellCount(uint8_t count); + uint8_t getFgCellCount(void) const; + + void setFgVoltageSource(uint8_t source); + uint8_t getFgVoltageSource(void) const; + + void setFgDesignCapacity(float capacity); + float getFgDesignCapacity(void) const; + + void setFgManualChargeDetectionEnable(bool state); + bool getFgManualChargeDetectionEnable(void) const; + + void setFgSyncVoltageThreshold(float threshold); + float getFgSyncVoltageThreshold(void) const; + + void setFgSyncCurrentThreshold(float threshold); + float getFgSyncCurrentThreshold(void) const; + + void setFgSyncFilter(uint32_t filter_ms); + uint32_t getFgSyncFilter(void) const; + + void setLogMode(LogMode mode); + LogMode getLogMode(void) const; + +private: + uint8_t mRawConfig[104] alignas(4); +}; + + +#endif diff --git a/inc/powermon_log.h b/inc/powermon_log.h new file mode 100644 index 0000000..1598638 --- /dev/null +++ b/inc/powermon_log.h @@ -0,0 +1,119 @@ +/* Copyright (C) 2020 - 2024, Thornwave Labs Inc + * Written by Razvan Turiac + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the “Software”), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * Attribution shall be given to Thornwave Labs Inc. and shall be made visible to the final user. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef _POWERMON_LOG_H +#define _POWERMON_LOG_H + + +#include + +#include +#include +#include + + +/** + * \brief PowermonLogFile is a class representing a PowerMon log file. It also contains support to decode the data points from a log file. + */ +class PowermonLogFile +{ +public: + enum Version + { + VER_FAMILY_MASK = 0xF0, + VER_POWERMON_WIFI_5W = 0x00, + }; + + struct Sample + { + uint32_t time; + float voltage1; + float voltage2; + float current; + float power; + float temperature; + uint8_t soc; + uint8_t ps; + }; + + static uint32_t decode(const std::vector &data, std::vector &samples); + +private: + enum Flags + { + POWER_VOLTAGE_SOURCE = (1 << 0) + }; + + enum Mask + { + V1 = (1 << 0), + V2 = (1 << 1), + V3 = (1 << 2), + V4 = (1 << 3), + V5 = (1 << 4), + V6 = (1 << 5), + + I1 = (1 << 6), + I2 = (1 << 7), + + P1 = (1 << 8), + P2 = (1 << 9), + + T1 = (1 << 10), + T2 = (1 << 11), + + SOC1 = (1 << 12), + SOC2 = (1 << 13), + + PS1 = (1 << 14), + PS2 = (1 << 15), + + VOLTAGE_SOURCE = (1 << 31) + }; + + struct Header + { + uint8_t magic[4]; + + uint8_t version; + uint8_t mode; + uint16_t reserved0; + + uint32_t time; + + uint32_t mask; + uint32_t flags; + }; + + static uint32_t getSamplePeriodInSeconds(uint8_t mode) + { + switch(mode) + { + case PowermonConfig::LOG_MODE_1_SEC: return 1; + case PowermonConfig::LOG_MODE_2_SEC: return 2; + case PowermonConfig::LOG_MODE_5_SEC: return 5; + case PowermonConfig::LOG_MODE_10_SEC: return 10; + case PowermonConfig::LOG_MODE_20_SEC: return 20; + case PowermonConfig::LOG_MODE_30_SEC: return 30; + case PowermonConfig::LOG_MODE_60_SEC: return 60; + } + + return 0; + } +}; + +#endif diff --git a/inc/powermon_scanner.h b/inc/powermon_scanner.h new file mode 100644 index 0000000..3210489 --- /dev/null +++ b/inc/powermon_scanner.h @@ -0,0 +1,107 @@ +/* Copyright (C) 2020 - 2024, Thornwave Labs Inc + * Written by Razvan Turiac + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the “Software”), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * Attribution shall be given to Thornwave Labs Inc. and shall be made visible to the final user. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef _POWERMON_SCANNER_H +#define _POWERMON_SCANNER_H + + +#include +#include +#include + +#include + + +/** + * \brief PowermonScanner offers support for scanning for PowerMon advertisements. Both BLE and WiFi PowerMon devices are supported. + */ +class PowermonScanner +{ +public: + /** + * \brief Advertisement is a structure representing an advertisement packet received from the PowerMon device. + */ + struct Advertisement + { + uint64_t serial; + uint64_t address; + + uint32_t time; + uint32_t flags; + + float voltage1; + float voltage2; + float current; + float power; + + float coulomb_meter; + float power_meter; + + float temperature; + + std::string name; + + uint16_t firmware_version_bcd; + uint8_t hardware_revision_bcd; + + Powermon::PowerStatus power_status; + + uint8_t soc; + uint16_t runtime; + + int16_t rssi; + + bool isExternalTemperature(void) const; + }; + + + /** + * \brief Creates an instance of the PowermonScanner class + * \return Pointer to the newly created PowermonScanner object. Must be freed using delete(). + */ + static PowermonScanner* createInstance(void); + virtual ~PowermonScanner(); + + /** + * \brief Sets the callback to be called by the Powermon scanner when a new advertisement has been received + * \param cb Lambda of type `void(const Advertisement&)` + */ + virtual void setCallback(const std::function &cb) = 0; + + /** + * \brief Starts scanning for WiFi device advertisements + */ + virtual void startWifiScan(void) = 0; + + /** + * \brief Stops scanning for WiFi device advertisements + */ + virtual void stopWifiScan(void) = 0; + + /** + * Starts scanning for BLE device advertisements + */ + virtual void startBleScan(void) = 0; + + /** + * Stops scanning for BLE device advertisements + */ + virtual void stopBleScan(void) = 0; +}; + + +#endif diff --git a/inc/powermon_schedule.h b/inc/powermon_schedule.h new file mode 100644 index 0000000..614bc01 --- /dev/null +++ b/inc/powermon_schedule.h @@ -0,0 +1,68 @@ +/* Copyright (C) 2020 - 2024, Thornwave Labs Inc + * Written by Razvan Turiac + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the “Software”), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * Attribution shall be given to Thornwave Labs Inc. and shall be made visible to the final user. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef _POWERMON_SCHEDULE_H +#define _POWERMON_SCHEDULE_H + + +#include +#include + + +/** + * \brief PowermonSchedule is a structure containing one PowerMon schedule (timer) + */ +struct PowermonSchedule +{ +public: + PowermonSchedule(); + + bool operator==(const PowermonSchedule &rhs) const; + + const char* getName(void) const; + void setName(const char* name); + + uint64_t getDescriptor(void) const; + void setDescriptor(uint64_t descriptor); + + uint32_t getStartHour(void) const; + void setStartHour(uint32_t value); + + uint32_t getStartMinute(void) const; + void setStartMinute(uint32_t value); + + uint32_t getEndHour(void) const; + void setEndHour(uint32_t value); + + uint32_t getEndMinute(void) const; + void setEndMinute(uint32_t value); + + bool isRepeatDOW(void) const; + void setRepeatDOW(void); + + bool isRepeatDOM(void) const; + void setRepeatDOM(void); + + uint8_t getRepeat(void) const; + void setRepeat(uint8_t value); + +private: + uint8_t mRawSchedule[24]; +}; + + +#endif diff --git a/powermon_lib.a b/powermon_lib.a new file mode 100644 index 0000000..adbea88 --- /dev/null +++ b/powermon_lib.a Binary files differ diff --git a/powermon_lib.lib b/powermon_lib.lib new file mode 100644 index 0000000..46755d9 --- /dev/null +++ b/powermon_lib.lib Binary files differ diff --git a/powermon_lib_rpi64.a b/powermon_lib_rpi64.a new file mode 100644 index 0000000..41dae45 --- /dev/null +++ b/powermon_lib_rpi64.a Binary files differ diff --git a/powermon_libd.lib b/powermon_libd.lib new file mode 100644 index 0000000..55d353c --- /dev/null +++ b/powermon_libd.lib Binary files differ