services.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  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 <stdio.h>
  8. #include "freertos/FreeRTOS.h"
  9. #include "freertos/timers.h"
  10. #include "esp_log.h"
  11. #include "esp_sleep.h"
  12. #include "driver/rtc_io.h"
  13. #include "driver/gpio.h"
  14. #include "driver/ledc.h"
  15. #include "driver/i2c.h"
  16. #include "driver/rmt.h"
  17. #include "platform_config.h"
  18. #include "gpio_exp.h"
  19. #include "battery.h"
  20. #include "led.h"
  21. #include "monitor.h"
  22. #include "globdefs.h"
  23. #include "accessors.h"
  24. #include "messaging.h"
  25. #include "buttons.h"
  26. #include "services.h"
  27. extern void battery_svc_init(void);
  28. extern void monitor_svc_init(void);
  29. extern void led_svc_init(void);
  30. int i2c_system_port = I2C_SYSTEM_PORT;
  31. int i2c_system_speed = 400000;
  32. int spi_system_host = SPI_SYSTEM_HOST;
  33. int spi_system_dc_gpio = -1;
  34. int rmt_system_base_channel = RMT_CHANNEL_0;
  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 EXT_RAM_ATTR struct {
  41. uint64_t wake_gpio, wake_level;
  42. uint32_t delay;
  43. } sleep_config;
  44. static EXT_RAM_ATTR void (*sleep_hooks[16])(void);
  45. static const char *TAG = "services";
  46. /****************************************************************************************
  47. *
  48. */
  49. void set_chip_power_gpio(int gpio, char *value) {
  50. bool parsed = true;
  51. // we only parse on-chip GPIOs
  52. if (gpio >= GPIO_NUM_MAX) return;
  53. if (!strcasecmp(value, "vcc") ) {
  54. gpio_pad_select_gpio(gpio);
  55. gpio_set_direction(gpio, GPIO_MODE_OUTPUT);
  56. gpio_set_level(gpio, 1);
  57. } else if (!strcasecmp(value, "gnd")) {
  58. gpio_pad_select_gpio(gpio);
  59. gpio_set_direction(gpio, GPIO_MODE_OUTPUT);
  60. gpio_set_level(gpio, 0);
  61. } else parsed = false;
  62. if (parsed) ESP_LOGI(TAG, "set GPIO %u to %s", gpio, value);
  63. }
  64. /****************************************************************************************
  65. *
  66. */
  67. void set_exp_power_gpio(int gpio, char *value) {
  68. bool parsed = true;
  69. // we only parse on-chip GPIOs
  70. if (gpio < GPIO_NUM_MAX) return;
  71. if (!strcasecmp(value, "vcc") ) {
  72. gpio_exp_set_direction(gpio, GPIO_MODE_OUTPUT, NULL);
  73. gpio_exp_set_level(gpio, 1, true, NULL);
  74. } else if (!strcasecmp(value, "gnd")) {
  75. gpio_exp_set_direction(gpio, GPIO_MODE_OUTPUT, NULL);
  76. gpio_exp_set_level(gpio, 0, true, NULL);
  77. } else parsed = false;
  78. if (parsed) ESP_LOGI(TAG, "set expanded GPIO %u to %s", gpio, value);
  79. }
  80. /****************************************************************************************
  81. *
  82. */
  83. static void sleep_gpio_handler(void *id, button_event_e event, button_press_e mode, bool long_press) {
  84. if (event == BUTTON_PRESSED) services_sleep_activate(SLEEP_ONGPIO);
  85. }
  86. /****************************************************************************************
  87. *
  88. */
  89. static void sleep_init(void) {
  90. char *config = config_alloc_get(NVS_TYPE_STR, "sleep_config");
  91. char *p;
  92. // do we want delay sleep
  93. PARSE_PARAM(config, "delay", '=', sleep_config.delay);
  94. sleep_config.delay *= 60*1000;
  95. if (sleep_config.delay) {
  96. ESP_LOGI(TAG, "Sleep inactivity of %d minute(s)", sleep_config.delay / (60*1000));
  97. }
  98. // get the wake criteria
  99. if ((p = strcasestr(config, "wake"))) {
  100. char list[32] = "", item[8];
  101. sscanf(p, "%*[^=]=%31[^,]", list);
  102. p = list - 1;
  103. while (p++ && sscanf(p, "%7[^|]", item)) {
  104. int level = 0, gpio = atoi(item);
  105. if (!rtc_gpio_is_valid_gpio(gpio)) {
  106. ESP_LOGE(TAG, "invalid wake GPIO %d (not in RTC domain)", gpio);
  107. } else {
  108. sleep_config.wake_gpio |= 1LL << gpio;
  109. }
  110. if (sscanf(item, "%*[^:]:%d", &level)) sleep_config.wake_level |= level << gpio;
  111. p = strchr(p, '|');
  112. }
  113. // when moving to esp-idf more recent than 4.4.x, multiple gpio wake-up with level specific can be done
  114. if (sleep_config.wake_gpio) {
  115. ESP_LOGI(TAG, "Sleep wake-up gpio bitmap 0x%llx (active 0x%llx)", sleep_config.wake_gpio, sleep_config.wake_level);
  116. }
  117. }
  118. // then get the gpio that activate sleep (we could check that we have a valid wake)
  119. if ((p = strcasestr(config, "sleep"))) {
  120. int gpio, level = 0;
  121. char sleep[8] = "";
  122. sscanf(p, "%*[^=]=%7[^,]", sleep);
  123. gpio = atoi(sleep);
  124. if ((p = strchr(sleep, ':')) != NULL) level = atoi(p + 1);
  125. ESP_LOGI(TAG, "Sleep activation gpio %d (active %d)", gpio, level);
  126. button_create(NULL, gpio, level ? BUTTON_HIGH : BUTTON_LOW, true, 0, sleep_gpio_handler, 0, -1);
  127. }
  128. }
  129. /****************************************************************************************
  130. *
  131. */
  132. void services_sleep_callback(uint32_t elapsed) {
  133. if (sleep_config.delay && elapsed >= sleep_config.delay) {
  134. services_sleep_activate(SLEEP_ONTIMER);
  135. }
  136. }
  137. /****************************************************************************************
  138. *
  139. */
  140. void services_sleep_activate(sleep_cause_e cause) {
  141. // call all sleep hooks that might want to do something
  142. for (void (**hook)(void) = sleep_hooks; *hook; hook++) (*hook)();
  143. // isolate all possible GPIOs, except the wake-up ones
  144. esp_sleep_config_gpio_isolate();
  145. for (int i = 0; i < GPIO_NUM_MAX; i++) {
  146. if (!rtc_gpio_is_valid_gpio(i) || ((1LL << i) & sleep_config.wake_gpio)) continue;
  147. rtc_gpio_isolate(i);
  148. }
  149. // is there just one GPIO
  150. if (sleep_config.wake_gpio & (sleep_config.wake_gpio - 1)) {
  151. ESP_LOGI(TAG, "going to sleep cause %d, wake-up on multiple GPIO, any '1' wakes up 0x%llx", cause, sleep_config.wake_gpio);
  152. esp_sleep_enable_ext1_wakeup(sleep_config.wake_gpio, ESP_EXT1_WAKEUP_ANY_HIGH);
  153. } else {
  154. int gpio = __builtin_ctz(sleep_config.wake_gpio);
  155. int level = (sleep_config.wake_level >> gpio) & 0x01;
  156. ESP_LOGI(TAG, "going to sleep cause %d, wake-up on GPIO %d level %d", cause, gpio, level);
  157. esp_sleep_enable_ext0_wakeup(gpio, level);
  158. }
  159. // we need to use a timer in case the same button is used for sleep and wake-up and it's "pressed" vs "released" selected
  160. if (cause == SLEEP_ONKEY) xTimerStart(xTimerCreate("sleepTimer", pdMS_TO_TICKS(1000), pdFALSE, NULL, (void (*)(void*)) esp_deep_sleep_start), 0);
  161. else esp_deep_sleep_start();
  162. }
  163. /****************************************************************************************
  164. *
  165. */
  166. void services_sleep_sethook(void (*hook)(void)) {
  167. for (int i = 0; i < sizeof(sleep_hooks)/sizeof(void(*)(void)); i++) {
  168. if (!sleep_hooks[i]) {
  169. sleep_hooks[i] = hook;
  170. return;
  171. }
  172. }
  173. }
  174. /****************************************************************************************
  175. *
  176. */
  177. void services_init(void) {
  178. messaging_service_init();
  179. gpio_install_isr_service(0);
  180. #ifdef CONFIG_I2C_LOCKED
  181. if (i2c_system_port == 0) {
  182. i2c_system_port = 1;
  183. ESP_LOGE(TAG, "Port 0 is reserved for internal DAC use");
  184. }
  185. #endif
  186. // set potential power GPIO on chip first in case expanders are power using these
  187. parse_set_GPIO(set_chip_power_gpio);
  188. // shared I2C bus
  189. const i2c_config_t * i2c_config = config_i2c_get(&i2c_system_port);
  190. ESP_LOGI(TAG,"Configuring I2C sda:%d scl:%d port:%u speed:%u", i2c_config->sda_io_num, i2c_config->scl_io_num, i2c_system_port, i2c_config->master.clk_speed);
  191. if (i2c_config->sda_io_num != -1 && i2c_config->scl_io_num != -1) {
  192. i2c_param_config(i2c_system_port, i2c_config);
  193. i2c_driver_install(i2c_system_port, i2c_config->mode, 0, 0, 0 );
  194. } else {
  195. i2c_system_port = -1;
  196. ESP_LOGW(TAG, "no I2C configured");
  197. }
  198. const spi_bus_config_t * spi_config = config_spi_get((spi_host_device_t*) &spi_system_host);
  199. 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, spi_config->sclk_io_num, spi_system_host, spi_system_dc_gpio);
  200. if (spi_config->mosi_io_num != -1 && spi_config->sclk_io_num != -1) {
  201. spi_bus_initialize( spi_system_host, spi_config, 1 );
  202. if (spi_system_dc_gpio != -1) {
  203. gpio_reset_pin(spi_system_dc_gpio);
  204. gpio_set_direction( spi_system_dc_gpio, GPIO_MODE_OUTPUT );
  205. gpio_set_level( spi_system_dc_gpio, 0 );
  206. } else {
  207. ESP_LOGW(TAG, "No DC GPIO set, SPI display will not work");
  208. }
  209. } else {
  210. spi_system_host = -1;
  211. ESP_LOGW(TAG, "no SPI configured");
  212. }
  213. // create GPIO expanders
  214. const gpio_exp_config_t* gpio_exp_config;
  215. for (int count = 0; (gpio_exp_config = config_gpio_exp_get(count)); count++) gpio_exp_create(gpio_exp_config);
  216. // now set potential power GPIO on expander
  217. parse_set_GPIO(set_exp_power_gpio);
  218. // system-wide PWM timer configuration
  219. ledc_timer_config_t pwm_timer = {
  220. .duty_resolution = LEDC_TIMER_13_BIT,
  221. .freq_hz = 5000,
  222. #ifdef CONFIG_IDF_TARGET_ESP32S3
  223. .speed_mode = LEDC_LOW_SPEED_MODE,
  224. #else
  225. .speed_mode = LEDC_HIGH_SPEED_MODE,
  226. #endif
  227. .timer_num = pwm_system.timer,
  228. };
  229. ledc_timer_config(&pwm_timer);
  230. led_svc_init();
  231. battery_svc_init();
  232. monitor_svc_init();
  233. sleep_init();
  234. }