diff --git a/Arduino/Makefile b/Arduino/Makefile index a1f4a20b5bfb796689a71bfd5977c36a4db194d8..5a15097fe19f3d5af5457400dc2d672e0ba7e849 100644 --- a/Arduino/Makefile +++ b/Arduino/Makefile @@ -1,9 +1,4 @@ -#NO_CORE= 1 -#MCU= m328p -#F_CPU= 16000000 -#VARIANT= standard TARGET= PowerSensor -BOARD_TAG= uno -#ARDUINO_PORT= /dev/ttyACM0 -ARDUINO_PORT= /dev/ttyUSB0 +BOARD_TAG= leonardo +ARDUINO_PORT= /dev/ttyACM0 include /usr/share/arduino/Arduino.mk diff --git a/Arduino/PowerSensor.ino b/Arduino/PowerSensor.ino index b0385ee013976e8d6a2f9dab8976d9ec73d3f6f4..8908824ed058aa24e17f1e4b298723522cbe65a1 100644 --- a/Arduino/PowerSensor.ino +++ b/Arduino/PowerSensor.ino @@ -32,79 +32,75 @@ struct EEPROM struct Sensor { double volt; double type; - int16_t nullLevels; + double nullLevel; } sensors[MAX_SENSORS]; } eeprom __attribute__((section(".eeprom"))); -int64_t accumulated_values[MAX_SENSORS]; -int16_t levels[MAX_SENSORS]; -long previous_time; -double weights[MAX_SENSORS]; -int16_t nullLevels[MAX_SENSORS]; -bool inUse[MAX_SENSORS]; +struct Sensor +{ + bool inUse; + uint16_t count; + int32_t total; + double weight; + double nullLevel; + double power() const { return weight * total / count - nullLevel; } -int64_t total_accumulated_value() -{ - int64_t sum = 0; +} sensors[MAX_SENSORS]; - for (uint8_t sensor = 0; sensor < MAX_SENSORS; sensor ++) - if (inUse[sensor]) - sum += weights[sensor] * accumulated_values[sensor]; - return sum; -} +bool streamValues = false; -inline uint8_t next_sensor(uint8_t current_sensor) +inline uint8_t nextSensor(uint8_t currentSensor) { do - if (++ current_sensor == MAX_SENSORS) - current_sensor = 0; - while (!inUse[current_sensor]); + if (++ currentSensor == MAX_SENSORS) + currentSensor = 0; + while (!sensors[currentSensor].inUse); + + return currentSensor; +} - return current_sensor; + +inline void setADMUX(uint8_t sensor) +{ +#if defined __AVR_ATmega32U4__ + ADMUX = _BV(REFS0) | ((sensor <= 2 ? 6 : 4) - sensor); +#else + ADMUX = _BV(REFS0) | (sensor + 1); // ADC0 reads the LCD buttons; skip it +#endif } ISR(ADC_vect) { - static uint8_t current_sensor = 0; + static uint8_t currentSensor = 0; uint8_t low = ADCL, high = ADCH; // read in this order - levels[current_sensor] = (high << 8) | low; // start next ADC ASAP. - uint8_t previous_sensor = current_sensor; - current_sensor = next_sensor(current_sensor); + uint8_t previousSensor = currentSensor; + currentSensor = nextSensor(currentSensor); - ADMUX = _BV(REFS0) | (current_sensor + 1); // ADC0 reads the LCD buttons; skip it + setADMUX(currentSensor); ADCSRA |= _BV(ADSC); // start ADC conversion - // handle measured value - - if (current_sensor <= previous_sensor) { - // we had one round of all sensors. now handle them in one go - long measured_time = micros(); - long time_difference = measured_time - previous_time; - previous_time = measured_time; + int16_t level = (high << 8) | low; + sensors[previousSensor].total += level - 512; + sensors[previousSensor].count ++; - for (uint8_t sensor = 0; sensor < MAX_SENSORS; sensor ++) - // compiler will generate 16x16 bit multiply producing 32-bit result - accumulated_values[sensor] += (long) (int) time_difference * (levels[sensor] - nullLevels[sensor]); - } -} + if (streamValues) { + Serial.write((previousSensor << 5) | (level >> 5)); + Serial.write(0xE0 | (level & 0x1F)); +#if defined __AVR_ATmega32U4__ + Serial.flush(); +#endif -void readState() -{ - noInterrupts(); - - int32_t total = total_accumulated_value() >> 16; - Serial.write((const uint8_t *) &total, sizeof total); - Serial.write((const uint8_t *) &previous_time, sizeof previous_time); - - interrupts(); + //for (int i = 2; i < 16; i ++) + //Serial.write(0); + } } @@ -114,9 +110,9 @@ void configureFromEEPROM() eeprom_read_block(©, &eeprom, sizeof copy); for (unsigned i = 0; i < MAX_SENSORS; i ++) { - inUse[i] = copy.sensors[i].volt != 0; - weights[i] = inUse[i] ? copy.sensors[i].volt * 2.5 / copy.sensors[i].type : 0; - nullLevels[i] = copy.sensors[i].nullLevels; + sensors[i].inUse = copy.sensors[i].volt != 0; + sensors[i].weight = sensors[i].inUse ? (2.5 / 512) * (copy.sensors[i].volt / copy.sensors[i].type) : 0; + sensors[i].nullLevel = copy.sensors[i].nullLevel; } } @@ -131,7 +127,10 @@ void readConfig() void writeConfig() { +#if !defined __AVR_ATmega32U4__ Serial.begin(115200); // serial comm from host to Arduino seems less reliable --> reduce baud rate +#endif + EEPROM copy; for (unsigned i = 0; i < sizeof copy; i ++) { @@ -141,31 +140,32 @@ void writeConfig() ((uint8_t *) ©)[i] = Serial.read(); } - Serial.begin(2000000); // probably only neecssary on Leonardo, that does not automatically reset after creating a new connection +#if !defined __AVR_ATmega32U4__ + Serial.begin(2000000); +#endif + eeprom_update_block(©, &eeprom, sizeof copy); configureFromEEPROM(); } -void readLevels() -{ - Serial.write((const uint8_t *) levels, sizeof levels); -} - - void serialEventRun() { switch (Serial.read()) { - case 'l': readLevels(); + case 'r': readConfig(); break; - case 'r': readConfig(); + case 'w': writeConfig(); break; - case 's': readState(); + case 'S': streamValues = true; break; - case 'w': writeConfig(); + case 'T': streamValues = false; + break; + + case 'X': streamValues = false; + Serial.write((const uint8_t []) { 0xFF, 0xE0 }, 2); break; } } @@ -182,7 +182,7 @@ void setup() Serial.begin(2000000); - ADMUX = _BV(REFS0) + 1; + setADMUX(0); ADCSRA |= _BV(ADIE); // enable ADC interrupts ADCSRA |= _BV(ADSC); // start ADC conversion } @@ -190,51 +190,52 @@ void setup() void loop() { - static unsigned long previous_print_time; - static int64_t previous_value_of_next_sensor, previous_value_total; - static uint8_t count, current_sensor = next_sensor(MAX_SENSORS - 1), current_line; - - noInterrupts(); + static unsigned long previousPrintTime; + static uint8_t count, currentSensor = nextSensor(MAX_SENSORS - 1), currentLine; - unsigned long current_time = micros(), time_difference = current_time - previous_print_time; + uint16_t currentTime = millis(), timeDifference = currentTime - previousPrintTime; - if (time_difference > 333333) { + if (timeDifference > 333) { double power; - if (current_line == 0) { + if (currentLine == 0) { // print top line; rotate among the used sensors - int64_t total = weights[current_sensor] * accumulated_values[current_sensor]; - - if ((++ count & 7) == 0) - current_sensor = next_sensor(current_sensor); - - power = (double) (total - previous_value_of_next_sensor) / (512 * time_difference); - previous_value_of_next_sensor = weights[current_sensor] * accumulated_values[current_sensor]; + noInterrupts(); + power = sensors[currentSensor].power(); interrupts(); + if ((++ count & 7) == 0) + currentSensor = nextSensor(currentSensor); + if ((count & 7) == 1) { lcd.setCursor(7, 0); - lcd.print((char) ('0' + current_sensor)); + lcd.print((char) ('0' + currentSensor)); } - } else { // current_line == 1 + } else { // currentLine == 1 // print bottom line; the sum of all sensors - int64_t total = total_accumulated_value(); + power = 0; + + noInterrupts(); + + for (uint8_t sensor = 0; sensor < MAX_SENSORS; sensor ++) { + if (sensors[sensor].inUse) { + power += sensors[sensor].power(); + sensors[sensor].total = 0; + sensors[sensor].count = 0; + } + } interrupts(); - power = (double) (total - previous_value_total) / (512 * time_difference); - previous_value_total = total; - previous_print_time = current_time; + previousPrintTime = currentTime; } char buffer[16]; dtostrf(power, 6, 1, buffer); - lcd.setCursor(9, current_line); + lcd.setCursor(9, currentLine); lcd.print(buffer); - current_line ^= 1; + currentLine ^= 1; } - - interrupts(); } diff --git a/Semaphore.h b/Semaphore.h new file mode 100644 index 0000000000000000000000000000000000000000..adb1a3d9645258c18cb84fb1c2ee2a55947c42b5 --- /dev/null +++ b/Semaphore.h @@ -0,0 +1,46 @@ +#if !defined SEMAPHORE_H +#define SEMAPHORE_H + +#include <condition_variable> +#include <mutex> + + +namespace PowerSensor { + + +class Semaphore +{ + public: + Semaphore(unsigned initialLevel = 0) + : + level(initialLevel) + { + } + + void up(unsigned count = 1) + { + std::unique_lock<std::mutex> lock(mutex); + level += count; + + if (count == 1) + cv.notify_one(); + else + cv.notify_all(); + } + + void down(unsigned count = 1) + { + std::unique_lock<std::mutex> lock(mutex); + cv.wait(lock, [this, count] { return level >= count; }); + level -= count; + } + + private: + std::mutex mutex; + std::condition_variable cv; + unsigned level; +}; + +} + +#endif