Skip to content
Snippets Groups Projects
Commit f3dd552c authored by Bram Veenboer's avatar Bram Veenboer
Browse files

Update library files and Makefile

parent 047fe715
Branches
Tags
No related merge requests found
ARCH=$(shell arch)
CXX= g++
CXXFLAGS= -O2 -g -pthread -fopenmp
CXXFLAGS= -std=c++11 -O2 -g -pthread -fopenmp
obj/$(ARCH)/%.o: %.cc
@mkdir -p obj/$(ARCH)
$(CXX) -c $(CXXFLAGS) $< -o $@
all:: lib/$(ARCH)/libPowerSensor.a bin/$(ARCH)/testPowerSensor arduino
all:: lib/$(ARCH)/libPowerSensor.a\
bin/$(ARCH)/psconfig\
bin/$(ARCH)/psrun\
bin/$(ARCH)/pstest\
arduino
lib/$(ARCH)/libPowerSensor.a: obj/$(ARCH)/PowerSensor.o lib/$(ARCH)
lib/$(ARCH)/libPowerSensor.a: obj/$(ARCH)/PowerSensor.o
-mkdir -p lib/$(ARCH)
$(AR) cr $@ $<
bin/$(ARCH)/testPowerSensor: obj/$(ARCH)/testPowerSensor.o lib/$(ARCH)/libPowerSensor.a bin/$(ARCH)
$(CXX) $(CXXFLAGS) obj/$(ARCH)/testPowerSensor.o -Llib/$(ARCH) -lPowerSensor -o $@
bin/$(ARCH)/psconfig: obj/$(ARCH)/psconfig.o lib/$(ARCH)/libPowerSensor.a
-mkdir -p bin/$(ARCH)
$(CXX) $(CXXFLAGS) obj/$(ARCH)/psconfig.o -Llib/$(ARCH) -lPowerSensor -o $@
obj/$(ARCH)/testPowerSensor.o: testPowerSensor.cc PowerSensor.h obj/$(ARCH)
bin/$(ARCH)/psrun: obj/$(ARCH)/psrun.o lib/$(ARCH)/libPowerSensor.a
-mkdir -p bin/$(ARCH)
$(CXX) $(CXXFLAGS) obj/$(ARCH)/psrun.o -Llib/$(ARCH) -lPowerSensor -o $@
obj/$(ARCH)/PowerSensor.o: PowerSensor.cc PowerSensor.h obj/$(ARCH)
bin/$(ARCH)/pstest: obj/$(ARCH)/pstest.o lib/$(ARCH)/libPowerSensor.a
-mkdir -p bin/$(ARCH)
$(CXX) $(CXXFLAGS) obj/$(ARCH)/pstest.o -Llib/$(ARCH) -lPowerSensor -o $@
obj/$(ARCH) lib/$(ARCH) bin/$(ARCH):
mkdir -p $@
obj/$(ARCH)/psconfig.o: psconfig.cc PowerSensor.h
obj/$(ARCH)/psrun.o: psrun.cc PowerSensor.h
obj/$(ARCH)/pstest.o: pstest.cc PowerSensor.h
obj/$(ARCH)/PowerSensor.o: PowerSensor.cc PowerSensor.h Semaphore.h
arduino::
+make -C Arduino
......@@ -28,3 +44,4 @@ upload:: all
clean::
make -C Arduino clean
$(RM) -r bin/$(ARCH) lib/$(ARCH) obj/$(ARCH)
......@@ -22,44 +22,130 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <byteswap.h>
#include <errno.h>
#include <fcntl.h>
#include <omp.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>
namespace PowerSensor {
PowerSensor::PowerSensor(const char *device, const char *dumpFileName)
:
dumpFile(dumpFileName == 0 ? 0 : new std::ofstream(dumpFileName)),
stop(false)
void PowerSensor::Sensor::readFromEEPROM(int fd)
{
lastState.microSeconds = 0;
struct EEPROM eeprom;
ssize_t retval, bytesRead = 0;
do {
if ((retval = ::read(fd, (char *) &eeprom + bytesRead, sizeof eeprom - bytesRead)) < 0) {
perror("read device");
exit(1);
}
} while ((bytesRead += retval) < sizeof eeprom);
#if defined __BIG_ENDIAN__
eeprom.volt = __bswap_32(eeprom.volt);
eeprom.type = __bswap_32(eeprom.type);
eeprom.nullLevel = __bswap_32(eeprom.nullLevel);
#endif
setVolt(eeprom.volt);
setType(eeprom.type);
setNullLevel(eeprom.nullLevel);
}
void PowerSensor::Sensor::writeToEEPROM(int fd) const
{
struct EEPROM eeprom;
eeprom.volt = volt;
eeprom.type = type;
eeprom.nullLevel = nullLevel;
#if defined __BIG_ENDIAN__
eeprom.volt = __bswap_32(eeprom.volt);
eeprom.type = __bswap_32(eeprom.type);
eeprom.nullLevel = __bswap_32(eeprom.nullLevel);
#endif
ssize_t retval, bytesWritten = 0;
do {
if ((retval = ::write(fd, (char *) &eeprom + bytesWritten, sizeof eeprom - bytesWritten)) < 0) {
perror("write device");
exit(1);
}
} while ((bytesWritten += retval) < sizeof eeprom);
}
void PowerSensor::Sensor::updateDerivedValues()
{
weight = volt != 0 ? 2.5 / 512 * volt / type : 0;
consumedEnergy = 0;
wattAtlastMeasurement = 0;
timeAtLastMeasurement = omp_get_wtime();
}
void PowerSensor::Sensor::setVolt(float volt)
{
this->volt = volt;
updateDerivedValues();
}
void PowerSensor::Sensor::setType(float type)
{
this->type = type;
updateDerivedValues();
}
void PowerSensor::Sensor::setNullLevel(float nullLevel)
{
this->nullLevel = nullLevel;
updateDerivedValues();
}
int PowerSensor::openDevice(const char *device)
{
int fd;
if ((fd = open(device, O_RDWR)) < 0) {
perror("open device");
exit(1);
}
if (flock(fd, LOCK_EX) < 0) {
perror("flock");
exit(1);
}
//Configure port for 8N1 transmission
struct termios options;
tcgetattr(fd, &options); //Gets the current options for the port
cfsetispeed(&options, B2000000); //Sets the Input Baud Rate
cfsetospeed(&options, B2000000); //Sets the Output Baud Rate
cfsetispeed(&options, B9600); //Sets the Input Baud Rate
cfsetospeed(&options, B9600); //Sets the Output Baud Rate
options.c_cflag = (options.c_cflag & ~CSIZE) | CS8;
options.c_iflag = IGNBRK;
options.c_lflag = 0;
options.c_oflag = 0;
options.c_cflag |= CLOCAL | CREAD;
options.c_cc[VMIN] = sizeof(State::MC_State);
options.c_cc[VMIN] = 2;
options.c_cc[VTIME] = 0;
options.c_iflag &= ~(IXON | IXOFF | IXANY);
options.c_cflag &= ~(PARENB | PARODD);
......@@ -67,173 +153,368 @@ PowerSensor::PowerSensor(const char *device, const char *dumpFileName)
/* commit the options */
tcsetattr(fd, TCSANOW, &options);
#if defined UNO
/* Wait for the Arduino to reset */
sleep(2);
#endif
/* Flush anything already in the serial buffer */
tcflush(fd, TCIFLUSH);
if ((errno = pthread_mutex_init(&mutex, 0)) != 0) {
perror("pthread_mutex_init");
exit(1);
return fd;
}
doMeasurement(); // initialise
if ((errno = pthread_create(&thread, 0, &PowerSensor::IOthread, this)) != 0) {
perror("pthread_create");
exit(1);
}
PowerSensor::PowerSensor(const char *device)
:
startTime(omp_get_wtime()),
fd(openDevice(device)),
thread(nullptr)
{
startCleanupProcess();
readSensorsFromEEPROM();
startIOthread();
}
PowerSensor::~PowerSensor()
{
stop = true;
if ((errno = pthread_join(thread, 0)) != 0)
perror("pthread_join");
if ((errno = pthread_mutex_destroy(&mutex)) != 0)
perror("pthread_mutex_destroy");
stopIOthread();
if (close(fd))
perror("close device");
}
if (dumpFile != 0)
delete dumpFile;
void PowerSensor::readSensorsFromEEPROM()
{
if (write(fd, "r", 1) != 1) {
perror("write device");
exit(1);
}
for (Sensor &sensor : sensors)
sensor.readFromEEPROM(fd);
}
void PowerSensor::lock()
void PowerSensor::writeSensorsToEEPROM()
{
if ((errno = pthread_mutex_lock(&mutex)) != 0) {
perror("pthread_mutex_lock");
stopIOthread();
if (write(fd, "w", 1) != 1) {
perror("write device");
exit(1);
}
#if defined UNO
struct termios options;
usleep(200000);
tcgetattr(fd, &options);
cfsetospeed(&options, B115200);
tcsetattr(fd, TCSANOW, &options);
#endif
for (const Sensor &sensor : sensors)
sensor.writeToEEPROM(fd);
#if defined UNO
usleep(200000);
tcgetattr(fd, &options);
cfsetospeed(&options, B2000000);
tcsetattr(fd, TCSANOW, &options);
#endif
startIOthread();
}
void PowerSensor::unlock()
void PowerSensor::startCleanupProcess()
{
if ((errno = pthread_mutex_unlock(&mutex)) != 0) {
perror("pthread_mutex_unlock");
// spawn child process to make sure that the Arduino receives a 'T', no matter how the application terminates
int pipe_fds[2];
if (pipe(pipe_fds) < 0) {
perror("pipe");
exit(1);
}
switch (fork())
{
ssize_t retval;
case -1 : perror("fork");
exit(1);
case 0 : // detach from the parent process, so signals to the parent are not caught by the child
setsid();
// close all file descriptors, except pipe read end and Arduino fd
for (int i = 3, last = getdtablesize(); i < last; i ++)
if (i != fd && i != pipe_fds[0])
close(i);
// wait until parent closes pipe_fds[1] so that read fails
char byte;
retval = ::read(pipe_fds[0], &byte, sizeof byte);
// tell Arduino to stop sending data
retval = write(fd, "T", 1);
// drain garbage
usleep(100000);
tcflush(fd, TCIFLUSH);
exit(0);
default: close(pipe_fds[0]);
}
}
void *PowerSensor::IOthread(void *arg)
bool PowerSensor::Sensor::inUse() const
{
return static_cast<PowerSensor *>(arg)->IOthread();
return volt != 0;
}
void *PowerSensor::IOthread()
void PowerSensor::Sensor::updateLevel(int16_t level)
{
while (!stop)
doMeasurement();
double now = omp_get_wtime();
return 0;
wattAtlastMeasurement = (level - 512) * weight - nullLevel;
consumedEnergy += wattAtlastMeasurement * (now - timeAtLastMeasurement);
timeAtLastMeasurement = now;
}
void PowerSensor::doMeasurement()
double PowerSensor::Sensor::totalEnergy(double now) const
{
State::MC_State currentState;
return /* weight == 0 ? 0 : */ consumedEnergy + wattAtlastMeasurement * (now - timeAtLastMeasurement);
}
ssize_t retval, bytesRead = 0;
if (write(fd, "s", 1) != 1) {
perror("write device");
exit(1);
double PowerSensor::Sensor::currentWatt() const
{
return /* weight == 0 ? 0 : */ wattAtlastMeasurement;
}
do {
if ((retval = ::read(fd, (char *) &currentState.consumedEnergy + bytesRead, sizeof(State::MC_State) - bytesRead)) < 0) {
bool PowerSensor::readLevelFromDevice(unsigned &sensorNumber, unsigned &level)
{
uint8_t msg[2];
ssize_t retval, bytesRead = 0;
while (true) {
if ((retval = ::read(fd, (char *) &msg + bytesRead, sizeof msg - bytesRead)) < 0) {
perror("read device");
exit(1);
} else if ((bytesRead += retval) == sizeof msg) {
if (msg[0] == 0xFF && msg[1] == 0xE0) {
return false;
} else if ((sensorNumber = msg[0] >> 5) < MAX_SENSORS && (msg[1] & 0xE0) == 0xE0) {
level = ((msg[0] & 0x1F) << 5) | (msg[1] & 0x1F);
return true;
} else {
msg[0] = msg[1]; // byte lost? drop first byte and try again
bytesRead = 1;
}
}
}
}
void PowerSensor::dumpCurrentWattToFile()
{
std::unique_lock<std::mutex> lock(dumpFileMutex);
double totalWatt = 0;
double time = omp_get_wtime();
*dumpFile << "S " << time - startTime;
#if 1
static double previousTime;
*dumpFile << ' ' << 1e6 * (time - previousTime);
previousTime = time;
#endif
for (const Sensor &sensor : sensors)
if (sensor.inUse()) {
*dumpFile << ' ' << sensor.currentWatt();
totalWatt += sensor.currentWatt();
}
*dumpFile << ' ' << totalWatt << std::endl;
}
void PowerSensor::IOthread()
{
threadStarted.up();
unsigned sensorNumber, level;
while (readLevelFromDevice(sensorNumber, level)) {
std::unique_lock<std::mutex> lock(mutex);
sensors[sensorNumber].updateLevel(level);
if (dumpFile != nullptr)
dumpCurrentWattToFile();
}
}
} while ((bytesRead += retval) < sizeof(State::MC_State));
lock();
if (lastState.microSeconds != currentState.microSeconds) {
previousState = lastState;
lastState = currentState;
void PowerSensor::startIOthread()
{
if (thread == nullptr) {
thread = new std::thread(&PowerSensor::IOthread, this);
if (dumpFile != 0)
*dumpFile << "S " << currentState.microSeconds / 1e6 << ' ' << (currentState.consumedEnergy - previousState.consumedEnergy) * (65536.0 / 512) / (currentState.microSeconds - previousState.microSeconds) << std::endl;
if (write(fd, "S", 1) != 1) {
perror("write device");
exit(1);
}
}
#if 0
static double lastTime;
static unsigned count;
++ count;
threadStarted.down(); // wait for the IOthread to run smoothly
}
if (lastTime + 1 < omp_get_wtime()) {
#pragma omp critical (cout)
std::cout << "nr times read = " << count << std::endl;
lastTime = omp_get_wtime();
count = 0;
void PowerSensor::stopIOthread()
{
if (thread != nullptr) {
if (write(fd, "X", 1) < 0) {
perror("write");
exit(1);
}
#endif
unlock();
thread->join();
delete thread;
thread = 0;
}
}
void PowerSensor::dump(const char *dumpFileName)
{
dumpFile = std::unique_ptr<std::ofstream>(dumpFileName != nullptr ? new std::ofstream(dumpFileName) : nullptr);
}
void PowerSensor::mark(const State &state, const char *name, unsigned tag)
void PowerSensor::mark(const State &state, const char *name, unsigned tag) const
{
if (dumpFile != 0) {
lock();
*dumpFile << "M " << state.lastState.microSeconds * 1e-6 << ' ' << tag << " \"" << (name == 0 ? "" : name) << '"' << std::endl;
unlock();
if (dumpFile != nullptr) {
std::unique_lock<std::mutex> lock(dumpFileMutex);
*dumpFile << "M " << state.timeAtRead - startTime << ' ' << tag << " \"" << (name == nullptr ? "" : name) << '"' << std::endl;
}
}
void PowerSensor::mark(const State &startState, const State &stopState, const char *name, unsigned tag)
void PowerSensor::mark(const State &startState, const State &stopState, const char *name, unsigned tag) const
{
if (dumpFile != 0) {
lock();
*dumpFile << "M " << startState.lastState.microSeconds * 1e-6 << ' ' << stopState.lastState.microSeconds * 1e-6 << ' ' << tag << " \"" << (name == 0 ? "" : name) << '"' << std::endl;
unlock();
if (dumpFile != nullptr) {
std::unique_lock<std::mutex> lock(dumpFileMutex);
*dumpFile << "M " << startState.timeAtRead - startTime << ' ' << stopState.timeAtRead << ' ' << tag << " \"" << (name == nullptr ? "" : name) << '"' << std::endl;
}
}
PowerSensor::State PowerSensor::read()
State PowerSensor::read() const
{
State state;
lock();
state.previousState = previousState;
state.lastState = lastState;
std::unique_lock<std::mutex> lock(mutex);
state.timeAtRead = omp_get_wtime();
unlock();
for (unsigned sensorID = 0; sensorID < MAX_SENSORS; sensorID ++)
state.consumedEnergy[sensorID] = sensors[sensorID].totalEnergy(state.timeAtRead);
return state;
}
double PowerSensor::Joules(const State &firstState, const State &secondState)
double Joules(const State &firstState, const State &secondState, int sensorID)
{
return Watt(firstState, secondState) * seconds(firstState, secondState);
if (sensorID >= (signed) MAX_SENSORS)
return 0;
if (sensorID >= 0)
return secondState.consumedEnergy[sensorID] - firstState.consumedEnergy[sensorID];
double joules = 0;
for (double consumedEnergy : secondState.consumedEnergy)
joules += consumedEnergy;
for (double consumedEnergy : firstState.consumedEnergy)
joules -= consumedEnergy;
return joules;
}
double PowerSensor::seconds(const State &firstState, const State &secondState)
double seconds(const State &firstState, const State &secondState)
{
return secondState.timeAtRead - firstState.timeAtRead;
}
double PowerSensor::Watt(const State &firstState, const State &secondState)
double Watt(const State &firstState, const State &secondState, int sensorID)
{
return Joules(firstState, secondState, sensorID) / seconds(firstState, secondState);
}
float PowerSensor::getVolt(unsigned sensorID) const
{
return sensorID < MAX_SENSORS ? sensors[sensorID].volt : 0;
}
void PowerSensor::setVolt(unsigned sensorID, float volt)
{
if (sensorID < MAX_SENSORS) {
sensors[sensorID].setVolt(volt);
writeSensorsToEEPROM();
}
}
float PowerSensor::getType(unsigned sensorID) const
{
return sensorID < MAX_SENSORS ? sensors[sensorID].type : 0;
}
void PowerSensor::setType(unsigned sensorID, float type)
{
if (sensorID < MAX_SENSORS) {
sensors[sensorID].setType(type);
writeSensorsToEEPROM();
}
}
float PowerSensor::getNullLevel(unsigned sensorID) const
{
uint32_t microSeconds = secondState.lastState.microSeconds - firstState.lastState.microSeconds;
return sensorID < MAX_SENSORS ? sensors[sensorID].nullLevel : 0;
}
void PowerSensor::setNullLevel(unsigned sensorID, float nullLevel)
{
if (sensorID < MAX_SENSORS) {
sensors[sensorID].setNullLevel(nullLevel);
writeSensorsToEEPROM();
}
}
bool PowerSensor::inUse(unsigned sensorID) const
{
return sensorID < MAX_SENSORS && sensors[sensorID].inUse();
}
if (microSeconds != 0)
return (secondState.lastState.consumedEnergy - firstState.lastState.consumedEnergy) * (65536.0 / 512) / microSeconds;
else // very short time
return (secondState.lastState.consumedEnergy - secondState.previousState.consumedEnergy) * (65536.0 / 512) / (secondState.lastState.microSeconds - secondState.previousState.microSeconds);
}
......@@ -21,50 +21,98 @@
#if !defined POWER_SENSOR_H
#define POWER_SENSOR_H
#include "Semaphore.h"
#include <inttypes.h>
#include <pthread.h>
#include <fstream>
#include <mutex>
#include <thread>
namespace PowerSensor {
const static unsigned MAX_SENSORS = 5;
class PowerSensor
{
public:
struct State
{
struct MC_State
{
int32_t consumedEnergy;
uint32_t microSeconds;
} previousState, lastState;
double consumedEnergy[MAX_SENSORS];
double timeAtRead;
};
PowerSensor(const char *device = "/dev/ttyACM0", const char *dumpFileName = 0);
class PowerSensor
{
public:
PowerSensor(const char *device = "/dev/ttyACM0");
~PowerSensor();
State read();
void mark(const State &, const char *name = 0, unsigned tag = 0);
void mark(const State &start, const State &stop, const char *name = 0, unsigned tag = 0);
State read() const;
static double Joules(const State &firstState, const State &secondState);
static double seconds(const State &firstState, const State &secondState);
static double Watt(const State &firstState, const State &secondState);
void dump(const char *dumpFileName); // dumpFileName == 0 --> stop dumping
void mark(const State &, const char *name = 0, unsigned tag = 0) const;
void mark(const State &start, const State &stop, const char *name = 0, unsigned tag = 0) const;
float getVolt(unsigned sensorID) const;
void setVolt(unsigned sensorID, float volt);
float getType(unsigned sensorID) const;
void setType(unsigned sensorID, float type);
float getNullLevel(unsigned sensorID) const;
void setNullLevel(unsigned sensorID, float nullLevel);
bool inUse(unsigned sensorID) const;
private:
struct Sensor
{
struct EEPROM
{
float volt __attribute__((packed));
float type __attribute__((packed));
float nullLevel __attribute__((packed));
};
float volt, type, nullLevel;
double weight;
double consumedEnergy;
double wattAtlastMeasurement;
double timeAtLastMeasurement;
bool inUse() const;
void readFromEEPROM(int fd), writeToEEPROM(int fd) const;
void setVolt(float), setType(float), setNullLevel(float);
void updateDerivedValues();
void updateLevel(int16_t level);
double totalEnergy(double now) const;
double currentWatt() const;
} sensors[MAX_SENSORS];
double startTime;
int fd;
std::ofstream *dumpFile;
pthread_t thread;
pthread_mutex_t mutex;
volatile bool stop;
State::MC_State previousState, lastState;
std::unique_ptr<std::ofstream> dumpFile;
//volatile bool stop;
mutable std::mutex mutex, dumpFileMutex;
Semaphore threadStarted;
std::thread *thread;
void startIOthread(), stopIOthread();
int openDevice(const char *device);
double totalEnergy() const;
void readSensorsFromEEPROM(), writeSensorsToEEPROM();
bool readLevelFromDevice(unsigned &sensorNumber, unsigned &level);
void dumpCurrentWattToFile();
void IOthread();
void startCleanupProcess();
};
void lock(), unlock();
static void *IOthread(void *);
void *IOthread();
void doMeasurement();
};
double Joules(const State &firstState, const State &secondState, int sensorID = -1 /* default: all sensors */);
double seconds(const State &firstState, const State &secondState);
double Watt(const State &firstState, const State &secondState, int sensorID = -1 /* default: all sensors */);
}
#endif
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment