Compare commits

..

5 Commits

5 changed files with 107 additions and 69 deletions

View File

@@ -7,19 +7,28 @@ double Controller::m_adcSample;
double Controller::m_resistance; double Controller::m_resistance;
double Controller::m_temperature; double Controller::m_temperature;
uint8_t Controller::m_fanSpeed; uint8_t Controller::m_fanSpeed;
volatile uint32_t Controller::m_adcSampleSum;
volatile bool Controller::m_adcSampleReady = false;
void Controller::init() void Controller::init()
{ {
m_adcPin.init(); m_adcPin.init(sampleCallback);
pwm::init(); pwm::init();
pwm::setDuty(100); pwm::setDuty(100);
} }
void Controller::callback() void Controller::callback()
{ {
sample(); if (m_adcSampleReady) {
m_adcSample = static_cast<double>(m_adcSampleSum) / NUM_ADC_SAMPLES;
m_adcSampleReady = false;
m_resistance = m_thermistor.getResistance(m_adcSample);
m_temperature = m_thermistor.getTemperature(m_resistance);
m_fanSpeed = mapTemperature(m_temperature); m_fanSpeed = mapTemperature(m_temperature);
pwm::setDuty(m_fanSpeed); pwm::setDuty(m_fanSpeed);
}
} }
uint8_t Controller::mapTemperature(double temperature) uint8_t Controller::mapTemperature(double temperature)
@@ -35,9 +44,20 @@ uint8_t Controller::mapTemperature(double temperature)
return clamp<uint8_t>(fanSpeed, 0, 100); return clamp<uint8_t>(fanSpeed, 0, 100);
} }
void Controller::sample() void Controller::sampleCallback(const uint16_t &adcSample)
{ {
m_adcSample = m_thermistor.sampleAdc(m_adcPin, 1000); static uint32_t s_sampleSum = 0;
m_resistance = m_thermistor.getResistance(m_adcSample); static auto s_sampleCounter = NUM_ADC_SAMPLES;
m_temperature = m_thermistor.getTemperature(m_resistance);
s_sampleSum += adcSample;
if (--s_sampleCounter <= 0) {
if (!m_adcSampleReady) {
m_adcSampleSum = s_sampleSum;
m_adcSampleReady = true;
}
// else lose this sample, which happens during long running commands like "curve", but has no impact
s_sampleSum = 0;
s_sampleCounter = NUM_ADC_SAMPLES;
}
} }

View File

@@ -20,12 +20,17 @@ class Controller {
static uint8_t mapTemperature(double temperature); static uint8_t mapTemperature(double temperature);
private: private:
using adc_conf = adc::Config<adc::SingleMode>; using adc_conf = adc::Config<adc::FreeRunningMode>;
static adc::Adc<adc_conf, io::P, io::P::C0> m_adcPin; static adc::Adc<adc_conf, io::P, io::P::C0> m_adcPin;
static constexpr auto NUM_ADC_SAMPLES = 1000;
static volatile uint32_t m_adcSampleSum;
static volatile bool m_adcSampleReady;
static Thermistor m_thermistor; static Thermistor m_thermistor;
static void sample(); static void sampleCallback(const uint16_t &adcSample);
template <typename T> template <typename T>
static T clamp(double value, T lower, T upper) static T clamp(double value, T lower, T upper)

View File

@@ -10,6 +10,8 @@
#include "controller.hpp" #include "controller.hpp"
namespace detail {
GF(ENDL, "\r\n"); GF(ENDL, "\r\n");
GF(HELP_CMD, "help"); GF(HELP_CMD, "help");
GF(SHOW_CMD, "show"); GF(SHOW_CMD, "show");
@@ -17,10 +19,23 @@ GF(CURVE_CMD, "curve");
GF(MONITOR_CMD, "monitor"); GF(MONITOR_CMD, "monitor");
GF(BOOTLOADER_CMD, "bootloader"); GF(BOOTLOADER_CMD, "bootloader");
GF(VERSION_CMD, "version"); GF(VERSION_CMD, "version");
GF(VERSION, "1.1"); GF(VERSION, "1.2");
constexpr auto BACKSPACE = uint8_t{0x7f}; static inline bool substringEquals(const char *str, const ::detail::FlashString *flashStr, const size_t &size)
constexpr auto CTRL_C = uint8_t{0x03}; {
return (strncmp_P(str, reinterpret_cast<const char *>(flashStr), size) == 0);
}
static inline bool stringEquals(const char *str, const ::detail::FlashString *flashStr, const size_t &size)
{
if (size == strlen_P(reinterpret_cast<const char *>(flashStr))) {
return substringEquals(str, flashStr, size);
}
return false;
}
} // namespace detail
template <class Uart> template <class Uart>
class Terminal { class Terminal {
@@ -29,9 +44,9 @@ class Terminal {
{ {
m_serial.init(); m_serial.init();
m_serial << ENDL; m_serial << detail::ENDL;
printVersion(); printVersion();
m_serial << ENDL << F("$ "); m_serial << detail::ENDL << F("$ ");
} }
static void callback() static void callback()
@@ -45,7 +60,7 @@ class Terminal {
// Handle Ctrl + C // Handle Ctrl + C
if (inputByte == CTRL_C) { if (inputByte == CTRL_C) {
m_serial << F("^C") << ENDL; m_serial << F("^C") << detail::ENDL;
handleInput = true; handleInput = true;
break; break;
} }
@@ -66,13 +81,13 @@ class Terminal {
if (m_serial.peek(inputByte) && (inputByte == '\r' || inputByte == '\n')) { if (m_serial.peek(inputByte) && (inputByte == '\r' || inputByte == '\n')) {
m_serial.rxByte(inputByte); m_serial.rxByte(inputByte);
} }
m_serial << ENDL; m_serial << detail::ENDL;
handleInput = true; handleInput = true;
break; break;
} }
if (m_inputSize >= INPUT_BUFFER_SIZE) { if (m_inputSize >= INPUT_BUFFER_SIZE) {
m_serial << ENDL << F("WARNING: Terminal input buffer overflow!") << ENDL; m_serial << detail::ENDL << F("WARNING: Terminal input buffer overflow!") << detail::ENDL;
handleInput = true; handleInput = true;
break; break;
} }
@@ -80,16 +95,19 @@ class Terminal {
if (handleInput) { if (handleInput) {
parseInput(); parseInput();
m_inputSize = 0;
m_serial << F("$ ");
} }
if (m_state == State::MONITOR) if (m_state == State::MONITOR && --m_monitorDelayCounter == 0) {
showState(); showState();
m_monitorDelayCounter = MONITOR_DELAY;
}
} }
private: private:
static constexpr auto INPUT_BUFFER_SIZE = 128; static constexpr auto INPUT_BUFFER_SIZE = 128;
static constexpr auto BACKSPACE = uint8_t{0x7f};
static constexpr auto CTRL_C = uint8_t{0x03};
static constexpr auto MONITOR_DELAY = 60000;
enum class State { enum class State {
NONE, NONE,
@@ -100,23 +118,26 @@ class Terminal {
static char m_inputBuffer[INPUT_BUFFER_SIZE]; static char m_inputBuffer[INPUT_BUFFER_SIZE];
static uint16_t m_inputSize; static uint16_t m_inputSize;
static State m_state; static State m_state;
static uint16_t m_monitorDelayCounter;
static void parseInput() static void parseInput()
{ {
if (m_inputSize) { if (m_inputSize) {
if (m_inputBuffer[m_inputSize - 1] == CTRL_C) { if (m_inputBuffer[m_inputSize - 1] == CTRL_C) {
handleCtrlC(); handleCtrlC();
} else if (strncmp_P(m_inputBuffer, reinterpret_cast<const char *>(HELP_CMD), m_inputSize) == 0) { } else if (m_state == State::NONE) {
if (substringEquals(m_inputBuffer, detail::HELP_CMD, m_inputSize)) {
printHelp(); printHelp();
} else if (strncmp_P(m_inputBuffer, reinterpret_cast<const char *>(SHOW_CMD), m_inputSize) == 0) { } else if (substringEquals(m_inputBuffer, detail::SHOW_CMD, m_inputSize)) {
showState(); showState();
} else if (strncmp_P(m_inputBuffer, reinterpret_cast<const char *>(CURVE_CMD), m_inputSize) == 0) { } else if (substringEquals(m_inputBuffer, detail::CURVE_CMD, m_inputSize)) {
printCurve(); printCurve();
} else if (strncmp_P(m_inputBuffer, reinterpret_cast<const char *>(MONITOR_CMD), m_inputSize) == 0) { } else if (substringEquals(m_inputBuffer, detail::MONITOR_CMD, m_inputSize)) {
m_state = State::MONITOR; m_state = State::MONITOR;
} else if (strncmp_P(m_inputBuffer, reinterpret_cast<const char *>(BOOTLOADER_CMD), m_inputSize) == 0) { m_monitorDelayCounter = MONITOR_DELAY;
} else if (substringEquals(m_inputBuffer, detail::BOOTLOADER_CMD, m_inputSize)) {
handleBootloader(); handleBootloader();
} else if (strncmp_P(m_inputBuffer, reinterpret_cast<const char *>(VERSION_CMD), m_inputSize) == 0) { } else if (substringEquals(m_inputBuffer, detail::VERSION_CMD, m_inputSize)) {
printVersion(); printVersion();
} else { } else {
printUnknown(); printUnknown();
@@ -124,21 +145,26 @@ class Terminal {
} }
} }
m_inputSize = 0;
if (m_state == State::NONE)
m_serial << F("$ ");
}
static void handleCtrlC() static void handleCtrlC()
{ {
m_serial << F("Abort!") << ENDL; m_serial << F("Abort!") << detail::ENDL;
m_state = State::NONE; m_state = State::NONE;
} }
static void printHelp() static void printHelp()
{ {
m_serial << F("FanTemp command overview: ") << ENDL; m_serial << F("FanTemp command overview: ") << detail::ENDL;
m_serial << HELP_CMD << F(" .......: print this help message") << ENDL; m_serial << detail::HELP_CMD << F(" .......: print this help message") << detail::ENDL;
m_serial << SHOW_CMD << F(" .......: shows current temperature and fan speed") << ENDL; m_serial << detail::SHOW_CMD << F(" .......: shows current temperature and fan speed") << detail::ENDL;
m_serial << CURVE_CMD << F(" ......: shows the curve used to map temperature to fan speed") << ENDL; m_serial << detail::CURVE_CMD << F(" ......: shows mapping from temperature to fan speed") << detail::ENDL;
m_serial << MONITOR_CMD << F(" ....: loops the show command until Ctrl + C is pressed") << ENDL; m_serial << detail::MONITOR_CMD << F(" ....: loops the show command until Ctrl + C is pressed") << detail::ENDL;
m_serial << BOOTLOADER_CMD << F(" .: enters the bootloader after 10 seconds") << ENDL; m_serial << detail::BOOTLOADER_CMD << F(" .: enters the bootloader after 3 seconds") << detail::ENDL;
m_serial << VERSION_CMD << F(" ....: displays firmware version") << ENDL; m_serial << detail::VERSION_CMD << F(" ....: displays firmware version") << detail::ENDL;
} }
static void showState() static void showState()
@@ -146,12 +172,12 @@ class Terminal {
char floatBuffer[16]; char floatBuffer[16];
sprintf(floatBuffer, "%.2f", Controller::m_adcSample); sprintf(floatBuffer, "%.2f", Controller::m_adcSample);
m_serial << F("ADC value ...: ") << floatBuffer << F(" / 1023") << ENDL; m_serial << F("ADC value ...: ") << floatBuffer << F(" / 1023") << detail::ENDL;
sprintf(floatBuffer, "%.2f", Controller::m_resistance); sprintf(floatBuffer, "%.2f", Controller::m_resistance);
m_serial << F("Resistance ..: ") << floatBuffer << F(" Ohm") << ENDL; m_serial << F("Resistance ..: ") << floatBuffer << F(" Ohm") << detail::ENDL;
sprintf(floatBuffer, "%.2f", Controller::m_temperature); sprintf(floatBuffer, "%.2f", Controller::m_temperature);
m_serial << F("Temperature .: ") << floatBuffer << F(" C") << ENDL; m_serial << F("Temperature .: ") << floatBuffer << F(" C") << detail::ENDL;
m_serial << F("Fan speed ...: ") << Controller::m_fanSpeed << F("%") << ENDL; m_serial << F("Fan speed ...: ") << Controller::m_fanSpeed << F("%") << detail::ENDL;
} }
static void printCurve() static void printCurve()
@@ -163,28 +189,23 @@ class Terminal {
for (uint8_t s = 0; s < Controller::mapTemperature(i); ++s) { for (uint8_t s = 0; s < Controller::mapTemperature(i); ++s) {
m_serial << "#"; m_serial << "#";
} }
m_serial << ENDL; m_serial << detail::ENDL;
} }
} }
static void handleBootloader() static void handleBootloader()
{ {
for (int8_t i = 10; i >= 0; --i) { m_serial << F("Entering bootloader...") << detail::ENDL;
m_serial << i << ENDL;
_delay_ms(1000);
}
m_serial << F("Entering bootloader...") << ENDL;
m_serial.flushTx(); m_serial.flushTx();
_delay_ms(1000); _delay_ms(3000);
Bootloader::enter(); Bootloader::enter();
} }
static void printVersion() static void printVersion()
{ {
m_serial << F("FanTemp v") << VERSION << ENDL; m_serial << F("FanTemp v") << detail::VERSION << detail::ENDL;
} }
static void printUnknown() static void printUnknown()
@@ -192,7 +213,7 @@ class Terminal {
m_serial << F("Unknown command \""); m_serial << F("Unknown command \"");
for (uint16_t i = 0; i < m_inputSize; ++i) for (uint16_t i = 0; i < m_inputSize; ++i)
m_serial << static_cast<char>(m_inputBuffer[i]); m_serial << static_cast<char>(m_inputBuffer[i]);
m_serial << F("\"") << ENDL; m_serial << F("\"") << detail::ENDL;
} }
}; };
@@ -204,3 +225,6 @@ uint16_t Terminal<Uart>::m_inputSize = 0;
template <class Uart> template <class Uart>
typename Terminal<Uart>::State Terminal<Uart>::m_state = State::NONE; typename Terminal<Uart>::State Terminal<Uart>::m_state = State::NONE;
template <class Uart>
uint16_t Terminal<Uart>::m_monitorDelayCounter;

View File

@@ -4,17 +4,6 @@
class Thermistor { class Thermistor {
public: public:
template <typename Adc>
static double sampleAdc(Adc &adcPin, uint16_t numSamples = 100)
{
double samples = 0;
for (uint16_t i = 0; i < numSamples; ++i)
samples += adcPin.read();
return samples / numSamples;
}
static double getResistance(double adcSample); static double getResistance(double adcSample);
static double getTemperature(double resistance); static double getTemperature(double resistance);