monitor.c 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  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 <stdlib.h>
  9. #include <unistd.h>
  10. #include <string.h>
  11. #include "freertos/FreeRTOS.h"
  12. #include "freertos/timers.h"
  13. #include "esp_system.h"
  14. #include "esp_log.h"
  15. #include "esp_task.h"
  16. #include "monitor.h"
  17. #include "driver/gpio.h"
  18. #include "buttons.h"
  19. #include "led.h"
  20. #include "globdefs.h"
  21. #include "platform_config.h"
  22. #include "accessors.h"
  23. #include "messaging.h"
  24. #include "cJSON.h"
  25. #include "tools.h"
  26. #define PSEUDO_IDLE_STACK_SIZE (6*1024)
  27. #define MONITOR_TIMER (10*1000)
  28. #define SCRATCH_SIZE 256
  29. static const char *TAG = "monitor";
  30. void (*pseudo_idle_svc)(uint32_t now);
  31. void (*jack_handler_svc)(bool inserted);
  32. bool jack_inserted_svc(void);
  33. void (*spkfault_handler_svc)(bool inserted);
  34. bool spkfault_svc(void);
  35. static monitor_gpio_t jack = { CONFIG_JACK_GPIO, 0 };
  36. static monitor_gpio_t spkfault = { CONFIG_SPKFAULT_GPIO, 0 };
  37. static bool monitor_stats;
  38. /****************************************************************************************
  39. *
  40. */
  41. static void task_stats( cJSON* top ) {
  42. #ifdef CONFIG_FREERTOS_USE_TRACE_FACILITY
  43. #pragma message("Compiled with trace facility")
  44. static struct {
  45. TaskStatus_t *tasks;
  46. uint32_t total, n;
  47. } current, previous;
  48. cJSON * tlist=cJSON_CreateArray();
  49. current.n = uxTaskGetNumberOfTasks();
  50. current.tasks = malloc_init_external( current.n * sizeof( TaskStatus_t ) );
  51. current.n = uxTaskGetSystemState( current.tasks, current.n, &current.total );
  52. cJSON_AddNumberToObject(top,"ntasks",current.n);
  53. char scratch[SCRATCH_SIZE] = {0};
  54. #ifdef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS
  55. #pragma message("Compiled with runtime stats")
  56. uint32_t elapsed = current.total - previous.total;
  57. for(int i = 0, n = 0; i < current.n; i++ ) {
  58. for (int j = 0; j < previous.n; j++) {
  59. if (current.tasks[i].xTaskNumber == previous.tasks[j].xTaskNumber) {
  60. n += snprintf(scratch + n, SCRATCH_SIZE - n, "%16s (%u) %2u%% s:%5u", current.tasks[i].pcTaskName,
  61. current.tasks[i].eCurrentState,
  62. 100 * (current.tasks[i].ulRunTimeCounter - previous.tasks[j].ulRunTimeCounter) / elapsed,
  63. current.tasks[i].usStackHighWaterMark);
  64. cJSON * t=cJSON_CreateObject();
  65. cJSON_AddNumberToObject(t,"cpu",100 * (current.tasks[i].ulRunTimeCounter - previous.tasks[j].ulRunTimeCounter) / elapsed);
  66. cJSON_AddNumberToObject(t,"minstk",current.tasks[i].usStackHighWaterMark);
  67. cJSON_AddNumberToObject(t,"bprio",current.tasks[i].uxBasePriority);
  68. cJSON_AddNumberToObject(t,"cprio",current.tasks[i].uxCurrentPriority);
  69. cJSON_AddStringToObject(t,"nme",current.tasks[i].pcTaskName);
  70. cJSON_AddNumberToObject(t,"st",current.tasks[i].eCurrentState);
  71. cJSON_AddNumberToObject(t,"num",current.tasks[i].xTaskNumber);
  72. cJSON_AddItemToArray(tlist,t);
  73. if (i % 3 == 2 || i == current.n - 1) {
  74. ESP_LOGI(TAG, "%s", scratch);
  75. n = 0;
  76. }
  77. break;
  78. }
  79. }
  80. }
  81. #else
  82. #pragma message("Compiled WITHOUT runtime stats")
  83. for (int i = 0, n = 0; i < current.n; i ++) {
  84. n += sprintf(scratch + n, "%16s s:%5u\t", current.tasks[i].pcTaskName, current.tasks[i].usStackHighWaterMark);
  85. cJSON * t=cJSON_CreateObject();
  86. cJSON_AddNumberToObject(t,"minstk",current.tasks[i].usStackHighWaterMark);
  87. cJSON_AddNumberToObject(t,"bprio",current.tasks[i].uxBasePriority);
  88. cJSON_AddNumberToObject(t,"cprio",current.tasks[i].uxCurrentPriority);
  89. cJSON_AddStringToObject(t,"nme",current.tasks[i].pcTaskName);
  90. cJSON_AddNumberToObject(t,"st",current.tasks[i].eCurrentState);
  91. cJSON_AddNumberToObject(t,"num",current.tasks[i].xTaskNumber);
  92. cJSON_AddItemToArray(tlist,t);
  93. if (i % 3 == 2 || i == current.n - 1) {
  94. ESP_LOGI(TAG, "%s", scratch);
  95. n = 0;
  96. }
  97. }
  98. #endif
  99. cJSON_AddItemToObject(top,"tasks",tlist);
  100. if (previous.tasks) free(previous.tasks);
  101. previous = current;
  102. #else
  103. #pragma message("Compiled WITHOUT trace facility")
  104. #endif
  105. }
  106. /****************************************************************************************
  107. *
  108. */
  109. static void monitor_trace(uint32_t now) {
  110. static uint32_t last;
  111. if (now < last + MONITOR_TIMER) return;
  112. last = now;
  113. cJSON * top=cJSON_CreateObject();
  114. cJSON_AddNumberToObject(top,"free_iram",heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
  115. cJSON_AddNumberToObject(top,"min_free_iram",heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL));
  116. cJSON_AddNumberToObject(top,"free_spiram",heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
  117. cJSON_AddNumberToObject(top,"min_free_spiram",heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM));
  118. ESP_LOGI(TAG, "Heap internal:%zu (min:%zu) external:%zu (min:%zu) dma:%zu (min:%zu)",
  119. heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
  120. heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
  121. heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
  122. heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM),
  123. heap_caps_get_free_size(MALLOC_CAP_DMA),
  124. heap_caps_get_minimum_free_size(MALLOC_CAP_DMA));
  125. task_stats(top);
  126. char * top_a= cJSON_PrintUnformatted(top);
  127. if(top_a){
  128. messaging_post_message(MESSAGING_INFO, MESSAGING_CLASS_STATS,top_a);
  129. FREE_AND_NULL(top_a);
  130. }
  131. cJSON_Delete(top);
  132. }
  133. /****************************************************************************************
  134. *
  135. */
  136. static void jack_handler_default(void *id, button_event_e event, button_press_e mode, bool long_press) {
  137. ESP_LOGI(TAG, "Jack %s", event == BUTTON_PRESSED ? "inserted" : "removed");
  138. if (jack_handler_svc) (*jack_handler_svc)(event == BUTTON_PRESSED);
  139. }
  140. /****************************************************************************************
  141. *
  142. */
  143. bool jack_inserted_svc (void) {
  144. if (jack.gpio != -1) return button_is_pressed(jack.gpio, NULL);
  145. else return true;
  146. }
  147. /****************************************************************************************
  148. *
  149. */
  150. static void spkfault_handler_default(void *id, button_event_e event, button_press_e mode, bool long_press) {
  151. ESP_LOGD(TAG, "Speaker status %s", event == BUTTON_PRESSED ? "faulty" : "normal");
  152. if (event == BUTTON_PRESSED) led_on(LED_RED);
  153. else led_off(LED_RED);
  154. if (spkfault_handler_svc) (*spkfault_handler_svc)(event == BUTTON_PRESSED);
  155. }
  156. /****************************************************************************************
  157. *
  158. */
  159. bool spkfault_svc (void) {
  160. return button_is_pressed(spkfault.gpio, NULL);
  161. }
  162. /****************************************************************************************
  163. *
  164. */
  165. #ifndef CONFIG_JACK_LOCKED
  166. static void set_jack_gpio(int gpio, char *value) {
  167. if (strcasestr(value, "jack")) {
  168. char *p;
  169. jack.gpio = gpio;
  170. if ((p = strchr(value, ':')) != NULL) jack.active = atoi(p + 1);
  171. }
  172. else {
  173. jack.gpio = -1;
  174. }
  175. }
  176. #endif
  177. /****************************************************************************************
  178. *
  179. */
  180. #ifndef CONFIG_SPKFAULT_LOCKED
  181. static void set_spkfault_gpio(int gpio, char *value) {
  182. if (strcasestr(value, "spkfault")) {
  183. char *p;
  184. spkfault.gpio = gpio;
  185. if ((p = strchr(value, ':')) != NULL) spkfault.active = atoi(p + 1);
  186. }
  187. else {
  188. spkfault.gpio = -1;
  189. }
  190. }
  191. #endif
  192. /****************************************************************************************
  193. *
  194. */
  195. static void pseudo_idle(void *arg) {
  196. while (1) {
  197. vTaskDelay(pdMS_TO_TICKS(1000));
  198. uint32_t now = pdTICKS_TO_MS(xTaskGetTickCount());
  199. if (monitor_stats) monitor_trace(now);
  200. if (pseudo_idle_svc) pseudo_idle_svc(now);
  201. }
  202. }
  203. /****************************************************************************************
  204. *
  205. */
  206. void monitor_svc_init(void) {
  207. ESP_LOGI(TAG, "Initializing monitoring");
  208. #ifdef CONFIG_JACK_GPIO_LEVEL
  209. jack.active = CONFIG_JACK_GPIO_LEVEL;
  210. #endif
  211. #ifndef CONFIG_JACK_LOCKED
  212. parse_set_GPIO(set_jack_gpio);
  213. #endif
  214. // re-use button management for jack handler, it's a GPIO after all
  215. if (jack.gpio != -1) {
  216. ESP_LOGI(TAG,"Adding jack (%s) detection GPIO %d", jack.active ? "high" : "low", jack.gpio);
  217. button_create(NULL, jack.gpio, jack.active ? BUTTON_HIGH : BUTTON_LOW, false, 250, jack_handler_default, 0, -1);
  218. }
  219. #ifdef CONFIG_SPKFAULT_GPIO_LEVEL
  220. spkfault.active = CONFIG_SPKFAULT_GPIO_LEVEL;
  221. #endif
  222. #ifndef CONFIG_SPKFAULT_LOCKED
  223. parse_set_GPIO(set_spkfault_gpio);
  224. #endif
  225. // re-use button management for speaker fault handler, it's a GPIO after all
  226. if (spkfault.gpio != -1) {
  227. ESP_LOGI(TAG,"Adding speaker fault (%s) detection GPIO %d", spkfault.active ? "high" : "low", spkfault.gpio);
  228. button_create(NULL, spkfault.gpio, spkfault.active ? BUTTON_HIGH : BUTTON_LOW, false, 0, spkfault_handler_default, 0, -1);
  229. }
  230. // do we want stats
  231. char *p = config_alloc_get_default(NVS_TYPE_STR, "stats", "n", 0);
  232. monitor_stats = p && (*p == '1' || *p == 'Y' || *p == 'y');
  233. FREE_AND_NULL(p);
  234. ESP_LOGI(TAG, "Heap internal:%zu (min:%zu) external:%zu (min:%zu) dma:%zu (min:%zu)",
  235. heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
  236. heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
  237. heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
  238. heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM),
  239. heap_caps_get_free_size(MALLOC_CAP_DMA),
  240. heap_caps_get_minimum_free_size(MALLOC_CAP_DMA));
  241. // pseudo-idle callback => don't use FreeRTOS idle callbacks so we can block (should not but ...)
  242. StaticTask_t* xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
  243. static EXT_RAM_ATTR StackType_t xStack[PSEUDO_IDLE_STACK_SIZE] __attribute__ ((aligned (4)));
  244. xTaskCreateStatic( (TaskFunction_t) pseudo_idle, "pseudo_idle", PSEUDO_IDLE_STACK_SIZE,
  245. NULL, ESP_TASK_PRIO_MIN, xStack, xTaskBuffer );
  246. }
  247. /****************************************************************************************
  248. *
  249. */
  250. monitor_gpio_t * get_spkfault_gpio(){
  251. return &spkfault ;
  252. }
  253. /****************************************************************************************
  254. *
  255. */
  256. monitor_gpio_t * get_jack_insertion_gpio(){
  257. return &jack;
  258. }