monitor.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  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 "monitor.h"
  16. #include "driver/gpio.h"
  17. #include "buttons.h"
  18. #include "led.h"
  19. #include "globdefs.h"
  20. #include "config.h"
  21. #include "accessors.h"
  22. #include "messaging.h"
  23. #include "cJSON.h"
  24. #include "trace.h"
  25. #define MONITOR_TIMER (10*1000)
  26. static const char *TAG = "monitor";
  27. static TimerHandle_t monitor_timer;
  28. static struct {
  29. int gpio;
  30. int active;
  31. } jack = { CONFIG_JACK_GPIO, 0 },
  32. spkfault = { CONFIG_SPKFAULT_GPIO, 0 };
  33. void (*jack_handler_svc)(bool inserted);
  34. bool jack_inserted_svc(void);
  35. void (*spkfault_handler_svc)(bool inserted);
  36. bool spkfault_svc(void);
  37. /****************************************************************************************
  38. *
  39. */
  40. static void task_stats( void ) {
  41. #ifdef CONFIG_FREERTOS_USE_TRACE_FACILITY
  42. static struct {
  43. TaskStatus_t *tasks;
  44. uint32_t total, n;
  45. } current, previous;
  46. cJSON * top=cJSON_CreateObject();
  47. cJSON * tlist=cJSON_CreateArray();
  48. current.n = uxTaskGetNumberOfTasks();
  49. current.tasks = malloc( current.n * sizeof( TaskStatus_t ) );
  50. current.n = uxTaskGetSystemState( current.tasks, current.n, &current.total );
  51. cJSON_AddNumberToObject(top,"ntasks",current.n);
  52. static EXT_RAM_ATTR char scratch[128+1];
  53. *scratch = '\0';
  54. #ifdef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS
  55. uint32_t elapsed = current.total - previous.total;
  56. for(int i = 0, n = 0; i < current.n; i++ ) {
  57. for (int j = 0; j < previous.n; j++) {
  58. if (current.tasks[i].xTaskNumber == previous.tasks[j].xTaskNumber) {
  59. n += sprintf(scratch + n, "%16s %2u%% s:%5u", current.tasks[i].pcTaskName,
  60. 100 * (current.tasks[i].ulRunTimeCounter - previous.tasks[j].ulRunTimeCounter) / elapsed,
  61. current.tasks[i].usStackHighWaterMark);
  62. cJSON * t=cJSON_CreateObject();
  63. cJSON_AddNumberToObject(t,"cpu",100 * (current.tasks[i].ulRunTimeCounter - previous.tasks[j].ulRunTimeCounter) / elapsed);
  64. cJSON_AddNumberToObject(t,"minstk",current.tasks[i].usStackHighWaterMark);
  65. cJSON_AddNumberToObject(t,"bprio",current.tasks[i].uxBasePriority);
  66. cJSON_AddNumberToObject(t,"cprio",current.tasks[i].uxCurrentPriority);
  67. cJSON_AddStringToObject(t,"nme",current.tasks[i].pcTaskName);
  68. cJSON_AddNumberToObject(t,"st",current.tasks[i].eCurrentState);
  69. cJSON_AddNumberToObject(t,"num",current.tasks[i].xTaskNumber);
  70. cJSON_AddItemToArray(tlist,t);
  71. char * topsts = cJSON_PrintUnformatted(t);
  72. if(topsts){
  73. ESP_LOGI(TAG,"task detail: %s",topsts);
  74. FREE_AND_NULL(topsts);
  75. }
  76. if (i % 3 == 2 || i == current.n - 1) {
  77. ESP_LOGI(TAG, "%s", scratch);
  78. n = 0;
  79. }
  80. break;
  81. }
  82. }
  83. }
  84. #else
  85. for (int i = 0, n = 0; i < current.n; i ++) {
  86. n += sprintf(scratch + n, "%16s s:%5u\t", current.tasks[i].pcTaskName, current.tasks[i].usStackHighWaterMark);
  87. cJSON * t=cJSON_CreateObject();
  88. cJSON_AddNumberToObject(t,"minstk",current.tasks[i].usStackHighWaterMark);
  89. cJSON_AddNumberToObject(t,"bprio",current.tasks[i].uxBasePriority);
  90. cJSON_AddNumberToObject(t,"cprio",current.tasks[i].uxCurrentPriority);
  91. cJSON_AddStringToObject(t,"nme",current.tasks[i].pcTaskName);
  92. cJSON_AddStringToObject(t,"st",current.tasks[i].eCurrentState);
  93. cJSON_AddNumberToObject(t,"num",current.tasks[i].xTaskNumber);
  94. cJSON_AddItemToArray(tlist,t);
  95. char * topsts = cJSON_PrintUnformatted(t);
  96. if(topsts){
  97. ESP_LOGI(TAG,"task detail: %s",topsts);
  98. FREE_AND_NULL(topsts);
  99. }
  100. if (i % 3 == 2 || i == current.n - 1) {
  101. ESP_LOGI(TAG, "%s", scratch);
  102. n = 0;
  103. }
  104. }
  105. #endif
  106. cJSON_AddItemToObject(top,"tasks",tlist);
  107. char * top_a= cJSON_PrintUnformatted(top);
  108. if(top_a){
  109. messaging_post_message(MESSAGING_INFO, MESSAGING_CLASS_STATS,top_a);
  110. FREE_AND_NULL(top_a);
  111. }
  112. cJSON_free(top);
  113. if (previous.tasks) free(previous.tasks);
  114. previous = current;
  115. #endif
  116. }
  117. /****************************************************************************************
  118. *
  119. */
  120. static void monitor_callback(TimerHandle_t xTimer) {
  121. ESP_LOGI(TAG, "Heap internal:%zu (min:%zu) external:%zu (min:%zu)",
  122. heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
  123. heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
  124. heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
  125. heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM));
  126. task_stats();
  127. }
  128. /****************************************************************************************
  129. *
  130. */
  131. static void jack_handler_default(void *id, button_event_e event, button_press_e mode, bool long_press) {
  132. ESP_LOGD(TAG, "Jack %s", event == BUTTON_PRESSED ? "inserted" : "removed");
  133. if (jack_handler_svc) (*jack_handler_svc)(event == BUTTON_PRESSED);
  134. }
  135. /****************************************************************************************
  136. *
  137. */
  138. bool jack_inserted_svc (void) {
  139. return button_is_pressed(jack.gpio, NULL);
  140. }
  141. /****************************************************************************************
  142. *
  143. */
  144. static void spkfault_handler_default(void *id, button_event_e event, button_press_e mode, bool long_press) {
  145. ESP_LOGD(TAG, "Speaker status %s", event == BUTTON_PRESSED ? "faulty" : "normal");
  146. if (event == BUTTON_PRESSED) led_on(LED_RED);
  147. else led_off(LED_RED);
  148. if (spkfault_handler_svc) (*spkfault_handler_svc)(event == BUTTON_PRESSED);
  149. }
  150. /****************************************************************************************
  151. *
  152. */
  153. bool spkfault_svc (void) {
  154. return button_is_pressed(spkfault.gpio, NULL);
  155. }
  156. /****************************************************************************************
  157. *
  158. */
  159. void set_jack_gpio(int gpio, char *value) {
  160. if (strcasestr(value, "jack")) {
  161. char *p;
  162. jack.gpio = gpio;
  163. if ((p = strchr(value, ':')) != NULL) jack.active = atoi(p + 1);
  164. }
  165. }
  166. /****************************************************************************************
  167. *
  168. */
  169. void set_spkfault_gpio(int gpio, char *value) {
  170. if (strcasestr(value, "spkfault")) {
  171. char *p;
  172. spkfault.gpio = gpio;
  173. if ((p = strchr(value, ':')) != NULL) spkfault.active = atoi(p + 1);
  174. }
  175. }
  176. /****************************************************************************************
  177. *
  178. */
  179. void monitor_svc_init(void) {
  180. ESP_LOGI(TAG, "Initializing monitoring");
  181. #ifdef CONFIG_JACK_GPIO_LEVEL
  182. jack.active = CONFIG_JACK_GPIO_LEVEL;
  183. #endif
  184. #ifndef CONFIG_JACK_LOCKED
  185. parse_set_GPIO(set_jack_gpio);
  186. #endif
  187. // re-use button management for jack handler, it's a GPIO after all
  188. if (jack.gpio != -1) {
  189. ESP_LOGI(TAG,"Adding jack (%s) detection GPIO %d", jack.active ? "high" : "low", jack.gpio);
  190. button_create(NULL, jack.gpio, jack.active ? BUTTON_HIGH : BUTTON_LOW, false, 250, jack_handler_default, 0, -1);
  191. }
  192. #ifdef CONFIG_SPKFAULT_GPIO_LEVEL
  193. spkfault.active = CONFIG_SPKFAULT_GPIO_LEVEL;
  194. #endif
  195. #ifndef CONFIG_SPKFAULT_LOCKED
  196. parse_set_GPIO(set_spkfault_gpio);
  197. #endif
  198. // re-use button management for speaker fault handler, it's a GPIO after all
  199. if (spkfault.gpio != -1) {
  200. ESP_LOGI(TAG,"Adding speaker fault (%s) detection GPIO %d", spkfault.active ? "high" : "low", spkfault.gpio);
  201. button_create(NULL, spkfault.gpio, spkfault.active ? BUTTON_HIGH : BUTTON_LOW, false, 0, spkfault_handler_default, 0, -1);
  202. }
  203. // do we want stats
  204. char *p = config_alloc_get_default(NVS_TYPE_STR, "stats", "n", 0);
  205. if (p && (*p == '1' || *p == 'Y' || *p == 'y')) {
  206. monitor_timer = xTimerCreate("monitor", MONITOR_TIMER / portTICK_RATE_MS, pdTRUE, NULL, monitor_callback);
  207. xTimerStart(monitor_timer, portMAX_DELAY);
  208. }
  209. free(p);
  210. ESP_LOGI(TAG, "Heap internal:%zu (min:%zu) external:%zu (min:%zu)",
  211. heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
  212. heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
  213. heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
  214. heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM));
  215. }