2
0

monitor.c 9.1 KB

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