services.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. /*
  2. This example code is in the Public Domain (or CC0 licensed, at your option.)
  3. Unless required by applicable law or agreed to in writing, this
  4. software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  5. CONDITIONS OF ANY KIND, either express or implied.
  6. */
  7. #include "freertos/FreeRTOS.h"
  8. #include "freertos/timers.h"
  9. #include <stdio.h>
  10. #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
  11. #include "Config.h"
  12. #include "accessors.h"
  13. #include "battery.h"
  14. #include "buttons.h"
  15. #include "driver/i2c.h"
  16. #include "driver/ledc.h"
  17. #include "driver/rmt.h"
  18. #include "driver/rtc_io.h"
  19. #include "esp_log.h"
  20. #include "esp_sleep.h"
  21. #include "globdefs.h"
  22. #include "gpio_exp.h"
  23. #include "led.h"
  24. #include "messaging.h"
  25. #include "monitor.h"
  26. #include "services.h"
  27. #include "tools.h"
  28. extern void battery_svc_init(void);
  29. extern void monitor_svc_init(void);
  30. extern void led_svc_init(void);
  31. int spi_system_host = SPI_SYSTEM_HOST;
  32. int spi_system_dc_gpio = -1;
  33. int rmt_system_base_tx_channel = RMT_CHANNEL_0;
  34. int rmt_system_base_rx_channel = RMT_CHANNEL_MAX - 1;
  35. pwm_system_t pwm_system = {
  36. .timer = LEDC_TIMER_0,
  37. .base_channel = LEDC_CHANNEL_0,
  38. .max = (1 << LEDC_TIMER_13_BIT),
  39. };
  40. static sys_sleep_config* sleep_config;
  41. static EXT_RAM_ATTR uint8_t gpio_exp_count = 0;
  42. static EXT_RAM_ATTR bool spi_configured = false;
  43. static EXT_RAM_ATTR bool i2c_configured = false;
  44. static EXT_RAM_ATTR struct {
  45. uint64_t wake_gpio, wake_level;
  46. uint64_t rtc_gpio, rtc_level;
  47. uint32_t delay, spurious;
  48. float battery_level;
  49. int battery_count;
  50. void (*idle_chain)(uint32_t now);
  51. void (*battery_chain)(float level, int cells);
  52. void (*suspend[10])(void);
  53. uint32_t (*sleeper[10])(void);
  54. } sleep_context;
  55. static const char* TAG = "services";
  56. bool are_GPIOExp_equal(const sys_exp_config* exp1, const sys_exp_config* exp2) {
  57. if (exp1 == NULL || exp2 == NULL) {
  58. return false; // Safeguard against NULL pointers
  59. }
  60. // Check if model, address, and base are the same
  61. if (exp1->model != exp2->model || exp1->addr != exp2->addr || exp1->base != exp2->base) {
  62. return false;
  63. }
  64. // Check if intr structure (pin and level) are the same
  65. if (exp1->intr != exp2->intr) {
  66. return false;
  67. }
  68. return true;
  69. }
  70. bool sys_dev_config_callback(pb_istream_t* istream, pb_ostream_t* ostream, const pb_field_iter_t* field) {
  71. ESP_LOGV(TAG, "Decoding/Encoding Devices, tag: %d", field->tag);
  72. sys_exp_config** pExp = (sys_exp_config**)field->pData;
  73. sys_exp_config* exp = NULL;
  74. if (istream != NULL && field->tag == sys_dev_config_gpio_exp_tag) {
  75. ESP_LOGD(TAG, "Decoding GPIO Expander #%d", gpio_exp_count + 1);
  76. sys_exp_config entry = sys_exp_config_init_default;
  77. if (!pb_decode(istream, &sys_exp_config_msg, &entry)) {
  78. return false;
  79. }
  80. if (entry.model == sys_exp_models_UNSPECIFIED_EXP) {
  81. ESP_LOGD(TAG, "Skipping GPIO Expander model %s", sys_exp_models_name(entry.model));
  82. return true;
  83. }
  84. // Don't add the expander if it was already decoded. This could
  85. // happen if both the configuration and the platform configuration
  86. // contain the definition.
  87. for (int i = 0; i < gpio_exp_count; i++) {
  88. if (are_GPIOExp_equal(&(*pExp)[i], &entry)) {
  89. ESP_LOGW(TAG, "GPIO Expander entry already exists, skipping addition.");
  90. return true; // Skip adding as it already exists
  91. }
  92. }
  93. gpio_exp_count++;
  94. *pExp = heap_caps_realloc(*pExp, sizeof(sys_exp_config) * gpio_exp_count, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
  95. // Assert after realloc to ensure memory allocation was successful
  96. assert(*pExp != NULL);
  97. exp = (*pExp) + gpio_exp_count - 1; // Simplified pointer arithmetic
  98. memcpy(exp, &entry, sizeof(entry));
  99. ESP_LOGD(TAG, "GPIO Expander #%d model %s", gpio_exp_count, sys_exp_models_name(entry.model));
  100. } else if (ostream != NULL && field->tag == sys_dev_config_gpio_exp_tag) {
  101. ESP_LOGV(TAG, "Encoding %d GPIO Expanders", gpio_exp_count);
  102. for (int i = 0; i < gpio_exp_count; i++) {
  103. if (!pb_encode_tag_for_field(ostream, field)) {
  104. return false;
  105. }
  106. if (!pb_encode_submessage(ostream, &sys_exp_config_msg, &(*pExp)[i])) {
  107. return false;
  108. }
  109. }
  110. ESP_LOGV(TAG, "GPIO Expander encoding completed");
  111. }
  112. return true;
  113. }
  114. void set_gpio_level(sys_gpio_config* gpio, const char* name, gpio_mode_t mode) {
  115. ESP_LOGI(TAG, "set GPIO %u to %s, level %d", gpio->pin, name, gpio->level);
  116. if (gpio->pin < 0) {
  117. ESP_LOGW(TAG, "Invalid gpio %d for %s", gpio->pin, name);
  118. return;
  119. }
  120. gpio_pad_select_gpio(gpio->pin);
  121. gpio_set_direction(gpio->pin, mode);
  122. gpio_set_level(gpio->pin, gpio->level);
  123. }
  124. /****************************************************************************************
  125. *
  126. */
  127. static void sleep_gpio_handler(void* id, button_event_e event, button_press_e mode, bool long_press) {
  128. if (event == BUTTON_PRESSED) services_sleep_activate(SLEEP_ONGPIO);
  129. }
  130. /****************************************************************************************
  131. *
  132. */
  133. static void sleep_timer(uint32_t now) {
  134. static EXT_RAM_ATTR uint32_t last, first;
  135. // first chain the calls to pseudo_idle function
  136. if (sleep_context.idle_chain) sleep_context.idle_chain(now);
  137. // we need boot time for spurious timeout calculation
  138. if (!first) first = now;
  139. // only query callbacks every 30s if we have at least one sleeper
  140. if (!*sleep_context.sleeper || now < last + 30 * 1000) return;
  141. last = now;
  142. // time to evaluate if we had spurious wake-up
  143. if (sleep_context.spurious && now > sleep_context.spurious + first) {
  144. bool spurious = true;
  145. // see if at least one sleeper has been awake since we started
  146. for (uint32_t (**sleeper)(void) = sleep_context.sleeper; *sleeper && spurious; sleeper++) {
  147. spurious &= (*sleeper)() >= now - first;
  148. }
  149. // no activity since we woke-up, this was a spurious one
  150. if (spurious) {
  151. ESP_LOGI(TAG, "spurious wake of %d sec, going back to sleep", (now - first) / 1000);
  152. services_sleep_activate(SLEEP_ONTIMER);
  153. }
  154. // resume normal work but we might have no "regular" inactivity delay
  155. sleep_context.spurious = 0;
  156. if (!sleep_context.delay) *sleep_context.sleeper = NULL;
  157. ESP_LOGI(TAG, "wake-up was not spurious after %d sec", (now - first) / 1000);
  158. }
  159. // we might be here because we are waiting for spurious
  160. if (sleep_context.delay) {
  161. // call all sleepers to know how long for how long they have been inactive
  162. for (uint32_t (**sleeper)(void) = sleep_context.sleeper; sleep_context.delay && *sleeper; sleeper++) {
  163. if ((*sleeper)() < sleep_context.delay) return;
  164. }
  165. // if we are here, we are ready to sleep;
  166. services_sleep_activate(SLEEP_ONTIMER);
  167. }
  168. }
  169. /****************************************************************************************
  170. *
  171. */
  172. static void sleep_battery(float level, int cells) {
  173. // chain if any
  174. if (sleep_context.battery_chain) sleep_context.battery_chain(level, cells);
  175. // then assess if we have to stop because of low batt
  176. if (level < sleep_context.battery_level) {
  177. if (sleep_context.battery_count++ == 2) services_sleep_activate(SLEEP_ONBATTERY);
  178. } else {
  179. sleep_context.battery_count = 0;
  180. }
  181. }
  182. /****************************************************************************************
  183. *
  184. */
  185. void services_sleep_init(void) {
  186. ESP_LOGD(TAG, "Initializing sleep services");
  187. if (!sys_services_config_SLEEP(sleep_config)) {
  188. ESP_LOGD(TAG, "No sleep service configured");
  189. return;
  190. }
  191. // get the wake criteria
  192. for (int i = 0; i < sleep_config->wake_count; i++) {
  193. if (!rtc_gpio_is_valid_gpio(sleep_config->wake[i].pin)) {
  194. ESP_LOGE(TAG, "invalid wake GPIO %d (not in RTC domain)", sleep_config->wake[i].pin);
  195. } else {
  196. sleep_context.wake_gpio |= 1LL << sleep_config->wake[i].pin;
  197. sleep_context.wake_gpio |= 1LL << sleep_config->wake[i].pin;
  198. sleep_context.wake_level |= sleep_config->wake[i].level << sleep_config->wake[i].pin;
  199. }
  200. }
  201. // when moving to esp-idf more recent than 4.4.x, multiple gpio wake-up with level specific can
  202. // be done
  203. if (sleep_context.wake_gpio) {
  204. ESP_LOGI(TAG, "Sleep wake-up gpio bitmap 0x%llx (active 0x%llx)", sleep_context.wake_gpio, sleep_context.wake_level);
  205. }
  206. // do we want battery safety
  207. sleep_context.battery_level = sleep_config->batt;
  208. if (sleep_context.battery_level != 0.0) {
  209. sleep_context.battery_chain = battery_handler_svc;
  210. battery_handler_svc = sleep_battery;
  211. ESP_LOGI(TAG, "Sleep on battery level of %.2f", sleep_context.battery_level);
  212. }
  213. for (int i = 0; i < sleep_config->rtc_count; i++) {
  214. if (!rtc_gpio_is_valid_gpio(sleep_config->rtc[i].pin)) {
  215. ESP_LOGE(TAG, "invalid rtc GPIO %d", sleep_config->rtc[i].pin);
  216. } else {
  217. sleep_context.rtc_gpio |= 1LL << sleep_config->rtc[i].pin;
  218. sleep_context.rtc_level |= sleep_config->rtc[i].level << sleep_config->rtc[i].pin;
  219. }
  220. }
  221. // when moving to esp-idf more recent than 4.4.x, multiple gpio wake-up with level specific can
  222. // be done
  223. if (sleep_context.rtc_gpio) {
  224. ESP_LOGI(TAG, "RTC forced gpio bitmap 0x%llx (active 0x%llx)", sleep_context.rtc_gpio, sleep_context.rtc_level);
  225. }
  226. // get the GPIOs that activate sleep (we could check that we have a valid wake)
  227. if (sleep_config->has_sleep && sleep_config->sleep.pin >= 0) {
  228. ESP_LOGI(TAG, "Sleep activation gpio %d (active %d)", sleep_config->sleep.pin, sleep_config->sleep.level);
  229. button_create(NULL, sleep_config->sleep.pin, sleep_config->sleep.level ? BUTTON_HIGH : BUTTON_LOW, true, 0, sleep_gpio_handler, 0, -1);
  230. }
  231. // do we want delay sleep
  232. sleep_context.delay = sleep_config->delay * 60 * 1000;
  233. // now check why we woke-up
  234. esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
  235. if (cause == ESP_SLEEP_WAKEUP_EXT0 || cause == ESP_SLEEP_WAKEUP_EXT1) {
  236. ESP_LOGI(TAG, "waking-up from deep sleep with cause %d", cause);
  237. // find the type of wake-up
  238. uint64_t wake_gpio;
  239. if (cause == ESP_SLEEP_WAKEUP_EXT0)
  240. wake_gpio = sleep_context.wake_gpio;
  241. else
  242. wake_gpio = esp_sleep_get_ext1_wakeup_status();
  243. // we might be woken up by infrared in which case we want a short sleep
  244. if (infrared_gpio() >= 0 && ((1LL << infrared_gpio()) & wake_gpio)) {
  245. sleep_context.spurious = 1;
  246. if (sleep_config->spurious > 0) {
  247. sleep_context.spurious = sleep_config->spurious;
  248. }
  249. sleep_context.spurious *= 60 * 1000;
  250. ESP_LOGI(TAG, "spurious wake-up detection during %d sec", sleep_context.spurious / 1000);
  251. }
  252. }
  253. // if we have inactivity timer (user-set or because of IR wake) then active counters
  254. if (sleep_context.delay || sleep_context.spurious) {
  255. sleep_context.idle_chain = pseudo_idle_svc;
  256. pseudo_idle_svc = sleep_timer;
  257. if (sleep_context.delay) ESP_LOGI(TAG, "inactivity timer of %d minute(s)", sleep_context.delay / (60 * 1000));
  258. }
  259. }
  260. /****************************************************************************************
  261. *
  262. */
  263. void services_sleep_activate(sleep_cause_e cause) {
  264. // call all sleep hooks that might want to do something
  265. for (void (**suspend)(void) = sleep_context.suspend; *suspend; suspend++)
  266. (*suspend)();
  267. // isolate all possible GPIOs, except the wake-up and RTC-maintaines ones
  268. esp_sleep_config_gpio_isolate();
  269. // keep RTC domain up if we need to maintain pull-up/down of some GPIO from RTC
  270. if (sleep_context.rtc_gpio) esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
  271. for (int i = 0; i < GPIO_NUM_MAX; i++) {
  272. // must be a RTC GPIO
  273. if (!rtc_gpio_is_valid_gpio(i)) continue;
  274. // do we need to maintain a pull-up or down of that GPIO
  275. if ((1LL << i) & sleep_context.rtc_gpio) {
  276. if ((sleep_context.rtc_level >> i) & 0x01)
  277. rtc_gpio_pullup_en(i);
  278. else
  279. rtc_gpio_pulldown_en(i);
  280. // or is this not wake-up GPIO, just isolate it
  281. } else if (!((1LL << i) & sleep_context.wake_gpio)) {
  282. rtc_gpio_isolate(i);
  283. }
  284. }
  285. // is there just one GPIO
  286. if (sleep_context.wake_gpio & (sleep_context.wake_gpio - 1)) {
  287. ESP_LOGI(TAG, "going to sleep cause %d, wake-up on multiple GPIO, any '1' wakes up 0x%llx", cause, sleep_context.wake_gpio);
  288. #if defined(CONFIG_IDF_TARGET_ESP32S3) && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0)
  289. if (!sleep_context.wake_level)
  290. esp_sleep_enable_ext1_wakeup(sleep_context.wake_gpio, ESP_EXT1_WAKEUP_ANY_LOW);
  291. else
  292. #endif
  293. esp_sleep_enable_ext1_wakeup(sleep_context.wake_gpio, ESP_EXT1_WAKEUP_ANY_HIGH);
  294. } else if (sleep_context.wake_gpio) {
  295. int gpio = __builtin_ctzll(sleep_context.wake_gpio);
  296. int level = (sleep_context.wake_level >> gpio) & 0x01;
  297. ESP_LOGI(TAG, "going to sleep cause %d, wake-up on GPIO %d level %d", cause, gpio, level);
  298. esp_sleep_enable_ext0_wakeup(gpio, level);
  299. } else {
  300. ESP_LOGW(TAG, "going to sleep cause %d, no wake-up option", cause);
  301. }
  302. // we need to use a timer in case the same button is used for sleep and wake-up and it's
  303. // "pressed" vs "released" selected
  304. if (cause == SLEEP_ONKEY)
  305. xTimerStart(xTimerCreate("sleepTimer", pdMS_TO_TICKS(1000), pdFALSE, NULL, (void (*)(void*))esp_deep_sleep_start), 0);
  306. else
  307. esp_deep_sleep_start();
  308. }
  309. /****************************************************************************************
  310. *
  311. */
  312. static void register_method(void** store, size_t size, void* method) {
  313. for (int i = 0; i < size; i++, *store++)
  314. if (!*store) {
  315. *store = method;
  316. return;
  317. }
  318. }
  319. /****************************************************************************************
  320. *
  321. */
  322. void services_sleep_setsuspend(void (*hook)(void)) {
  323. register_method((void**)sleep_context.suspend, sizeof(sleep_context.suspend) / sizeof(*sleep_context.suspend), (void*)hook);
  324. }
  325. /****************************************************************************************
  326. *
  327. */
  328. void services_sleep_setsleeper(uint32_t (*sleeper)(void)) {
  329. register_method((void**)sleep_context.sleeper, sizeof(sleep_context.sleeper) / sizeof(*sleep_context.sleeper), (void*)sleeper);
  330. }
  331. void services_ports_init(void) {
  332. esp_err_t err = ESP_OK;
  333. ESP_LOGI(TAG, "Initializing ports");
  334. gpio_install_isr_service(0);
  335. ESP_LOGD(TAG, "Checking i2c port usage");
  336. if (platform->dev.dac.has_i2c && platform->dev.has_i2c && platform->dev.dac.i2c.port != sys_i2c_port_UNSPECIFIED &&
  337. platform->dev.dac.i2c.port == platform->dev.i2c.port) {
  338. ESP_LOGE(TAG, "Port %s is used for internal DAC use. Switching to ", sys_i2c_port_name(platform->dev.dac.i2c.port));
  339. platform->dev.i2c.port = platform->dev.i2c.port == sys_i2c_port_PORT0 ? sys_i2c_port_PORT1 : sys_i2c_port_PORT0;
  340. config_raise_changed(false);
  341. }
  342. // shared I2C bus
  343. ESP_LOGD(TAG, "Configuring I2C");
  344. const i2c_config_t* i2c_config = config_i2c_get(&platform->dev.i2c);
  345. ESP_LOGD(TAG, "Stored I2C configuration [sda:%d scl:%d port:%s speed:%u]", i2c_config->sda_io_num, i2c_config->scl_io_num,
  346. sys_i2c_port_name(platform->dev.i2c.port), i2c_config->master.clk_speed);
  347. if (i2c_config->sda_io_num != -1 && i2c_config->scl_io_num != -1) {
  348. ESP_LOGI(TAG, "Configuring I2C sda:%d scl:%d port:%s speed:%u", i2c_config->sda_io_num, i2c_config->scl_io_num,
  349. sys_i2c_port_name(platform->dev.i2c.port), i2c_config->master.clk_speed);
  350. i2c_param_config(platform->dev.i2c.port - sys_i2c_port_PORT0, i2c_config);
  351. if ((err = i2c_driver_install(platform->dev.i2c.port - sys_i2c_port_PORT0, i2c_config->mode, 0, 0, 0)) != ESP_OK) {
  352. ESP_LOGE(TAG, "Error setting up i2c: %s", esp_err_to_name(err));
  353. } else {
  354. i2c_configured = true;
  355. }
  356. } else {
  357. if (platform->dev.has_display && platform->dev.display.has_common && platform->dev.display.which_dispType == sys_display_config_i2c_tag) {
  358. ESP_LOGE(TAG, "I2C configuration missing for display %s", sys_display_drivers_name(platform->dev.display.common.driver));
  359. } else {
  360. ESP_LOGI(TAG, "Shared I2C not configured");
  361. }
  362. }
  363. const spi_bus_config_t* spi_config = config_spi_get((spi_host_device_t*)&spi_system_host);
  364. ESP_LOGD(TAG, "Stored SPI configuration[mosi:%d miso:%d clk:%d host:%u dc:%d]", spi_config->mosi_io_num, spi_config->miso_io_num,
  365. spi_config->sclk_io_num, spi_system_host, spi_system_dc_gpio);
  366. if (spi_config->mosi_io_num != -1 && spi_config->sclk_io_num != -1) {
  367. ESP_LOGI(TAG, "Configuring SPI mosi:%d miso:%d clk:%d host:%u dc:%d", spi_config->mosi_io_num, spi_config->miso_io_num,
  368. spi_config->sclk_io_num, spi_system_host, spi_system_dc_gpio);
  369. if ((err = spi_bus_initialize(spi_system_host, spi_config, SPI_DMA_CH_AUTO)) != ESP_OK) {
  370. ESP_LOGE(TAG, "Error setting up SPI bus: %s", esp_err_to_name(err));
  371. } else {
  372. spi_configured = true;
  373. }
  374. if (spi_system_dc_gpio != -1) {
  375. gpio_reset_pin(spi_system_dc_gpio);
  376. gpio_set_direction(spi_system_dc_gpio, GPIO_MODE_OUTPUT);
  377. gpio_set_level(spi_system_dc_gpio, 0);
  378. } else {
  379. ESP_LOGW(TAG, "No DC GPIO set, SPI display will not work");
  380. }
  381. } else {
  382. spi_system_host = -1;
  383. if (platform->dev.has_display && platform->dev.display.has_common &&
  384. platform->dev.display.common.driver != sys_display_drivers_UNSPECIFIED &&
  385. platform->dev.display.which_dispType == sys_display_config_spi_tag) {
  386. ESP_LOGE(TAG, "SPI bus configuration missing for display %s", sys_display_drivers_name(platform->dev.display.common.driver));
  387. } else {
  388. ESP_LOGI(TAG, "SPI bus not configured");
  389. }
  390. }
  391. }
  392. void services_gpio_init(void) {
  393. ESP_LOGI(TAG, "Initializing GPIOs");
  394. // set potential power GPIO on chip first in case expanders are power using these
  395. sys_gpio_config* gpio = NULL;
  396. if (SYS_GPIOS_NAME(power, gpio) && gpio->pin >= 0) {
  397. ESP_LOGD(TAG, "Handling power gpio");
  398. gpio->level = sys_gpio_lvl_HIGH;
  399. set_gpio_level(gpio, "power", GPIO_MODE_OUTPUT);
  400. } else {
  401. ESP_LOGD(TAG, "No power GPIO defined");
  402. }
  403. if (SYS_GPIOS_NAME(GND, gpio) && gpio->pin >= 0) {
  404. ESP_LOGD(TAG, "Handling GND gpio");
  405. gpio->level = sys_gpio_lvl_LOW;
  406. set_gpio_level(gpio, "GND", GPIO_MODE_OUTPUT);
  407. } else {
  408. ESP_LOGD(TAG, "No GND gpio defined");
  409. }
  410. // create GPIO expanders
  411. gpio_exp_config_t gpio_exp_config;
  412. if (platform->has_dev && gpio_exp_count > 0 && platform->dev.gpio_exp[0].model != sys_exp_models_UNSPECIFIED_EXP) {
  413. ESP_LOGI(TAG, "Initializing %d GPIO Expander(s)", gpio_exp_count);
  414. for (int count = 0; count < gpio_exp_count; count++) {
  415. sys_exp_config* exp = &platform->dev.gpio_exp[count];
  416. if (exp->model == sys_exp_models_UNSPECIFIED_EXP) {
  417. ESP_LOGD(TAG, "Skipping unknown model");
  418. continue;
  419. }
  420. gpio_exp_config.phy.ena_pin = -1;
  421. gpio_exp_config.base = exp->base;
  422. gpio_exp_config.count = exp->count;
  423. gpio_exp_config.phy.addr = exp->addr;
  424. gpio_exp_config.intr = exp->intr;
  425. if (exp->has_ena && exp->ena.pin >= 0) {
  426. gpio_exp_config.phy.ena_pin = exp->ena.pin;
  427. gpio_exp_config.phy.ena_lvl = exp->ena.level;
  428. }
  429. if (exp->which_ExpType == sys_exp_config_spi_tag) {
  430. if (!spi_configured) {
  431. ESP_LOGE(TAG, "SPI bus not configured for GPIO Expander index %d (%s)", count, sys_exp_models_name(exp->model));
  432. continue;
  433. }
  434. gpio_exp_config.phy.cs_pin = exp->ExpType.spi.cs;
  435. gpio_exp_config.phy.host =
  436. (!platform->dev.has_spi || (platform->dev.has_spi && platform->dev.spi.host == sys_dev_common_hosts_NONE) ? sys_dev_common_hosts_Host0 : platform->dev.spi.host) - sys_dev_common_hosts_Host0;
  437. gpio_exp_config.phy.speed = exp->ExpType.spi.speed > 0 ? exp->ExpType.spi.speed : 0;
  438. } else {
  439. if (!i2c_configured) {
  440. ESP_LOGE(TAG, "I2C bus not configured for GPIO Expander index %d (%s)", count, sys_exp_models_name(exp->model));
  441. continue;
  442. }
  443. gpio_exp_config.phy.port =
  444. ((!platform->dev.has_i2c || (platform->dev.has_i2c && platform->dev.i2c.port == sys_i2c_port_UNSPECIFIED) )? sys_dev_common_ports_SYSTEM : platform->dev.i2c.port) - sys_dev_common_ports_SYSTEM ;
  445. }
  446. strncpy(gpio_exp_config.model, sys_exp_models_name(exp->model), sizeof(gpio_exp_config.model) - 1);
  447. gpio_exp_create(&gpio_exp_config);
  448. }
  449. } else if (gpio_exp_count > 0) {
  450. ESP_LOGW(TAG, "GPIO Expander count %d but none is valid", gpio_exp_count);
  451. }
  452. }
  453. /****************************************************************************************
  454. *
  455. */
  456. void services_init(void) {
  457. ESP_LOGI(TAG, "Initializing services");
  458. esp_err_t err = ESP_OK;
  459. // system-wide PWM timer configuration
  460. ledc_timer_config_t pwm_timer = {
  461. .duty_resolution = LEDC_TIMER_13_BIT,
  462. .freq_hz = 5000,
  463. #ifdef CONFIG_IDF_TARGET_ESP32S3
  464. .speed_mode = LEDC_LOW_SPEED_MODE,
  465. #else
  466. .speed_mode = LEDC_HIGH_SPEED_MODE,
  467. #endif
  468. .timer_num = pwm_system.timer,
  469. };
  470. ledc_timer_config(&pwm_timer);
  471. led_svc_init();
  472. battery_svc_init();
  473. monitor_svc_init();
  474. }