Refactor thermistor components and update board dependencies

- Remove heating and NTC functionality from communication module
- Convert Infrared from template to runtime array-based implementation
- Refactor Led to support multiple LEDs with compile-time name
  validation
- Simplify main to remove direct hardware initialization
- Update west manifest for dr2501a board and add ch9438 module
This commit is contained in:
zhangyisong 2026-08-02 21:59:50 +08:00
parent e0908bc8f0
commit a9e8885ec8
5 changed files with 155 additions and 127 deletions

View File

@ -1,9 +1,7 @@
#ifndef __THER_COM_HPP__ #ifndef __THER_COM_HPP__
#define __THER_COM_HPP__ #define __THER_COM_HPP__
#include "heating.hpp"
#include "led.hpp" #include "led.hpp"
#include "ntc.hpp"
#include <led_strip_indicator/led_strip_indicator.hpp> #include <led_strip_indicator/led_strip_indicator.hpp>
#include <uart_com/simple_protocal.hpp> #include <uart_com/simple_protocal.hpp>
#include <zephyr/drivers/hwinfo.h> #include <zephyr/drivers/hwinfo.h>
@ -50,9 +48,7 @@ private:
return; return;
} }
if (data[0] == RUNNING) { if (data[0] == RUNNING) {
InfLed::On();
} else { } else {
InfLed::Off();
} }
printk("set ledsrtip to %d", data[0]); printk("set ledsrtip to %d", data[0]);
const uint8_t id = data[0]; const uint8_t id = data[0];
@ -61,40 +57,33 @@ private:
}); });
} }
static auto WHeatingState(uart_com::DataType data) -> void { static auto WHeatingState(uart_com::DataType data) -> void {
if (data.size() != 1) { // if (data.size() != 1) {
return; // return;
} // }
const bool state = (data[0] > 0); // const bool state = (data[0] > 0);
if (state) { // if (state) {
HeatingPad::Start(sensor_value{data[0], 0}); // HeatingPad::Start(sensor_value{data[0], 0});
} else { // } else {
HeatingPad::Stop(); // HeatingPad::Stop();
} // }
} }
static auto RHeatingState(uart_com::DataType data) -> void { static auto RHeatingState(uart_com::DataType data) -> void {
if (data.size() != 0) { // if (data.size() != 0) {
return; // return;
} // }
const sensor_value temp = HeatingPad::CurrentTemp(); // const sensor_value temp = HeatingPad::CurrentTemp();
const uint8_t state[] = {s_heating_state, (uint8_t)temp.val1, // const uint8_t state[] = {s_heating_state, (uint8_t)temp.val1,
(uint8_t)temp.val2}; // (uint8_t)temp.val2};
s_proto->Send(R_HEATING_STATE, state); // s_proto->Send(R_HEATING_STATE, state);
} }
/// Read one pole NTC by index (0~7). /// Read one pole NTC by index (0~7).
static auto ReadPoleNtcTemperature(uart_com::DataType data) -> void { static auto ReadPoleNtcTemperature(uart_com::DataType data) -> void {
if (data.size() != 1) { if (data.size() != 1) {
return; return;
} }
const sensor_value temp = NtcGroup::GetSensorValue(data[0]);
const uint8_t payload[] = {(uint8_t)temp.val1, (uint8_t)temp.val2};
s_proto->Send(R_TEMP_POLE_NTC, payload);
} }
/// Read heating pad NTC (onboard ADC). /// Read heating pad NTC (onboard ADC).
static auto ReadHeatingPadNtcTemperature(uart_com::DataType data) -> void { static auto ReadHeatingPadNtcTemperature(uart_com::DataType data) -> void {}
const sensor_value temp = HeatingPad::CurrentTemp();
const uint8_t payload[] = {(uint8_t)temp.val1, (uint8_t)temp.val2};
s_proto->Send(R_TEMP_HETING_PAD_NTC, payload);
}
}; };
constexpr static std::pair<const uint8_t, constexpr static std::pair<const uint8_t,
uart_com::SimpleProtocal::CallbackType> uart_com::SimpleProtocal::CallbackType>
@ -113,7 +102,6 @@ private:
inline static auto s_proto = inline static auto s_proto =
(uart_com::SimpleProtocal *)(DEVICE_DT_GET(DT_NODELABEL(pm_protocal))); (uart_com::SimpleProtocal *)(DEVICE_DT_GET(DT_NODELABEL(pm_protocal)));
inline static auto s_heating_state = false; inline static auto s_heating_state = false;
using InfLed = ther::Led<LED_DT_SPEC_GET(DT_NODELABEL(inf_led))>;
}; };
} // namespace ther } // namespace ther

View File

@ -1,5 +1,8 @@
#ifndef __THER_INFRARED_HPP__ #ifndef __THER_INFRARED_HPP__
#define __THER_INFRARED_HPP__ #define __THER_INFRARED_HPP__
#include <etl/algorithm.h>
#include <etl/bitset.h>
#include <etl/tuple.h>
#include <zephyr/device.h> #include <zephyr/device.h>
#include <zephyr/drivers/sensor.h> #include <zephyr/drivers/sensor.h>
#include <zpp/result.hpp> #include <zpp/result.hpp>
@ -7,41 +10,64 @@
#include <zpp/work_queue.hpp> #include <zpp/work_queue.hpp>
namespace ther { namespace ther {
template <device *kDev> class Infrared { class Infrared {
public: public:
static auto Init() -> zpp::error { static auto Init() -> zpp::error {
static sensor_trigger tri{.type = SENSOR_TRIG_DATA_READY, static sensor_trigger tri{.type = SENSOR_TRIG_DATA_READY,
.chan = SENSOR_CHAN_AMBIENT_TEMP}; .chan = SENSOR_CHAN_AMBIENT_TEMP};
if (auto r = sensor_trigger_set( for (auto dev : s_dev) {
kDev, &tri, if (auto r = sensor_trigger_set(
[](const device *dev, const sensor_trigger *trig) { dev, &tri,
s_is_data_ready = true; [](const device *dev, const sensor_trigger *trig) {
}); const size_t id = etl::distance(
r != 0) { s_dev.begin(), etl::find(s_dev.begin(), s_dev.end(), dev));
return -ENODEV; s_dev_ready[id] = 1;
}; });
r != 0) {
return -ENODEV;
}
}
return zpp::ok();
} }
static auto GetSensorValue() /* One-shot fetch of ALL sensors, return the hottest one. */
-> zpp::result<zpp::value<zpp::value_type::AMBIENT_TEMP>> { static auto GetMaxSensorValue() -> zpp::result<std::pair<int, sensor_value>> {
if (!s_is_data_ready) { bool found = false;
int max_id = 0;
sensor_value max_val{};
for (size_t id = 0; id < s_dev.size(); ++id) {
/* -ENODATA just means no new sample for this channel; keep going. */
if (0 != sensor_sample_fetch(s_dev[id])) {
continue;
}
sensor_value val;
if (0 != sensor_channel_get(s_dev[id], SENSOR_CHAN_AMBIENT_TEMP, &val)) {
s_dev_ready[id] = 0; /* sample consumed, drop the ready flag */
continue;
}
s_dev_ready[id] = 0;
if (!found || val.val1 > max_val.val1 ||
(val.val1 == max_val.val1 && val.val2 > max_val.val2)) {
found = true;
max_id = static_cast<int>(id);
max_val = val;
}
}
if (!found) {
return zpp::error_code::k_nodata; return zpp::error_code::k_nodata;
} }
return std::make_pair(max_id, max_val);
if (0 != sensor_sample_fetch(kDev)) {
return zpp::error_code::k_io;
}
zpp::value<zpp::value_type::AMBIENT_TEMP> val;
if (0 != sensor_channel_get(kDev, SENSOR_CHAN_AMBIENT_TEMP, &val)) {
return zpp::error_code::k_io;
}
s_is_data_ready = false;
return val;
} }
private: private:
inline static bool s_is_data_ready{false}; #define INFRARED_DEV(node) DEVICE_DT_GET(node),
inline static etl::array s_dev{
DT_FOREACH_STATUS_OKAY(godtek_temp_uart, INFRARED_DEV)};
inline static etl::bitset<s_dev.size()> s_dev_ready;
}; };
} // namespace ther } // namespace ther

View File

@ -1,28 +1,95 @@
#ifndef __THER_LED_HPP__ #ifndef __THER_LED_HPP__
#define __THER_LED_HPP__ #define __THER_LED_HPP__
#include <etl/algorithm.h>
#include <etl/array.h>
#include <zephyr/drivers/led.h> #include <zephyr/drivers/led.h>
#include <zpp/ct_string.hpp>
#include <zpp/work_queue.hpp> #include <zpp/work_queue.hpp>
namespace ther { namespace ther {
template <led_dt_spec kSpec> class Led { /* Compile-time check: every LED name must be distinct so that
* Led::On/Off/Flash<kName> name lookup stays unambiguous. */
template <typename T, std::size_t N>
consteval auto AreUniqueNames(const etl::array<T, N> &names) -> bool {
for (std::size_t i = 0; i < N; ++i) {
for (std::size_t j = i + 1; j < N; ++j) {
if (names[i] == names[j]) {
return false;
}
}
}
return true;
}
#define LED_SPEC(node) led_dt_spec LED_DT_SPEC_GET(node)
#define LED_DEV_CHILD_SPEC(node) DT_FOREACH_CHILD_SEP(node, LED_SPEC, (, )),
#define LED_NODE_NAME(node) DT_FOREACH_CHILD_SEP(node, DEVICE_DT_NAME, (, ))
class Led {
private:
template <led_dt_spec> struct Periodic {
static auto Flash(led_dt_spec *spec) -> void {
bool flag = false;
if (flag) {
led_on_dt(spec);
} else {
led_off_dt(spec);
}
flag = !flag;
}
static inline zpp::periodic_work<led_dt_spec *> work{Flash};
};
public: public:
static auto On() { led_on_dt(&kSpec); } template <zpp::ct_string kName> static auto On() -> zpp::error {
static auto Off() { led_off_dt(&kSpec); } static_assert(IsDeviceName<kName>());
template <typename TRep, typename TPeriod> const auto id = etl::find(s_led_node_name.begin(), s_led_node_name.end(),
std::string_view(kName)) -
s_led_node_name.begin();
return led_on_dt(&s_led_dev[id]);
}
template <zpp::ct_string kName> static auto Off() -> zpp::error {
static_assert(IsDeviceName<kName>());
const auto id = etl::find(s_led_node_name.begin(), s_led_node_name.end(),
std::string_view(kName)) -
s_led_node_name.begin();
return led_off_dt(&s_led_dev[id]);
}
template <zpp::ct_string kName, typename TRep, typename TPeriod>
static auto Flash(std::chrono::duration<TRep, TPeriod> period) { static auto Flash(std::chrono::duration<TRep, TPeriod> period) {
s_work.submit(period); static_assert(IsDeviceName<kName>());
const auto id = etl::find(s_led_node_name.begin(), s_led_node_name.end(),
std::string_view(kName)) -
s_led_node_name.begin();
Periodic<s_led_dev[id]>::work.submit(period);
} }
private: private:
inline static zpp::periodic_work<> s_work{[]() { template <zpp::ct_string kName> static consteval auto IsDeviceName() {
static bool is_on = false; for (size_t i = 0; i < s_led_node_name.size(); ++i) {
if (is_on) { if (s_led_node_name[i] == std::string_view(kName)) {
Off(); return true;
} else { }
On();
} }
is_on = !is_on; return false;
}}; }
static auto Flash(led_dt_spec *spec) -> void {
static bool flag = false;
if (flag) {
led_off_dt(spec);
} else {
led_on_dt(spec);
}
flag = !flag;
}
inline static constexpr etl::array s_led_dev{
DT_FOREACH_STATUS_OKAY(gpio_leds, LED_DEV_CHILD_SPEC)};
inline static constexpr auto s_led_node_name =
etl::make_array<std::string_view>(
DT_FOREACH_STATUS_OKAY(gpio_leds, LED_NODE_NAME));
static_assert(AreUniqueNames(s_led_node_name),
"LED node names must be unique");
}; };
} // namespace ther } // namespace ther

View File

@ -1,65 +1,8 @@
#include <com.hpp>
#include <heating.hpp>
#include <infrared.hpp>
#include <led.hpp>
#include <ntc.hpp>
#include <optional>
#include <watchdog.hpp>
namespace {
using StatusLed = ther::Led<LED_DT_SPEC_GET(DT_NODELABEL(status_led))>;
bool is_infrared_ready = false;
auto infrared = DEVICE_DT_GET(DT_NODELABEL(godtek));
auto GetmaxTemp() -> std::optional<sensor_value> {
if (is_infrared_ready) {
sensor_value max_val{};
bool found = false;
// NTC 8 路最大值 #include <zephyr/kernel.h>
auto ntc_result = ther::NtcGroup::GetSensorValue();
if (ntc_result) {
max_val = *ntc_result;
found = true;
}
// 红外数据就绪时参与比较
sensor_value ir_val{};
if (0 == sensor_sample_fetch_chan(infrared, SENSOR_CHAN_AMBIENT_TEMP)) {
if (0 ==
sensor_channel_get(infrared, SENSOR_CHAN_AMBIENT_TEMP, &ir_val)) {
double ir_t = sensor_value_to_double(&ir_val);
// printk("[ir] = %.2f°C\n", ir_t);
if (!found || ir_val.val1 > max_val.val1 ||
(ir_val.val1 == max_val.val1 && ir_val.val2 > max_val.val2)) {
max_val = ir_val;
}
max_val = ir_val;
// printk("[out] sent = %.2f°C\n", sensor_value_to_double(&max_val));
is_infrared_ready = false;
}
}
return max_val;
}
return std::nullopt;
}
} // namespace
auto main(void) -> int { auto main(void) -> int {
using namespace std::chrono_literals;
printk("app on board %s\n", CONFIG_BOARD);
// auto wdt = app::WatchDogConfig{};
StatusLed::Flash(1s);
ther::Com::Init();
ther::HeatingPad::Start(1.0f);
// ther::HeatingPad::Start(sensor_value{42, 0});
sensor_trigger tri{.type = SENSOR_TRIG_DATA_READY,
.chan = SENSOR_CHAN_AMBIENT_TEMP};
sensor_trigger_set(infrared, &tri,
[](const device *dev, const sensor_trigger *trig) {
is_infrared_ready = true;
});
while (1) { while (1) {
if (auto v = GetmaxTemp(); v.has_value()) {
ther::Com::Send(v.value());
}
k_sleep(K_MSEC(20)); k_sleep(K_MSEC(20));
} }

View File

@ -22,8 +22,8 @@ manifest:
path: modules/zpp path: modules/zpp
remote: robotstorm remote: robotstorm
revision: dev revision: dev
- name: dr2501a_g070rb - name: dr2501a
path: boards/dr2501a_g070rb path: boards/dr2501a
remote: robotstorm remote: robotstorm
revision: main revision: main
- name: led_strip_indicator - name: led_strip_indicator
@ -34,6 +34,10 @@ manifest:
path: modules/godtek_temp path: modules/godtek_temp
remote: robotstorm remote: robotstorm
revision: main revision: main
- name: ch9438
path: modules/ch9438
remote: robotstorm
revision: main
# - name: heading_pad # - name: heading_pad
# path: modules/heading_pad # path: modules/heading_pad
# revision: main # revision: main