text: /*
   *
   */
  #ifndef __MJD_H__
  #define __MJD_H__
  
  #include <float.h>
  #include <inttypes.h>
  #include <limits.h>
  #include <stdbool.h>
  #include <stddef.h>
  #include <stdint.h>
  #include <stdlib.h>
  #include <stdio.h>
  #include <string.h>
  #include <time.h>
  
  #include "freertos/FreeRTOS.h"
  #include "freertos/event_groups.h"
  #include "freertos/task.h"
  
  // This header file is generated by 'make menuconfig' and derived from the config file 'sdkconfig'
  #include "sdkconfig.h"
  
  #include "esp_event_loop.h"
  #include "esp_clk.h"
  #include "esp_log.h"
  #include "esp_sleep.h"
  #include "esp_spi_flash.h"
  #include "esp_system.h"
  
  
  #include "cJSON.h"
  #include "driver/i2c.h"
  #include "driver/rmt.h"
  #include "driver/uart.h"
  #include "esp_http_client.h"
  #include "mbedtls/base64.h"
  #include "nvs_flash.h"
  #include "soc/rmt_reg.h"
  #include "soc/soc.h"
  
  #ifdef __cplusplus
  extern "C" {
  #endif
  
  /**********
   *  Definitions for errors
   *  @deprecated Use esp_err_t instead of mjd_err_t
   *  @deprecated Use ESP_OK instead of MJD_OK
   *  @deprecated Use ESP_FAIL instead of MJD_ERROR
   */
  /////typedef int32_t mjd_err_t;
  /////#define MJD_OK     (0)
  /////#define MJD_ERROR  (-1)
  #define MJD_ERR_CHECKSUM            (0x101)
  #define MJD_ERR_INVALID_ARG         (0x102)
  #define MJD_ERR_INVALID_DATA        (0x103)
  #define MJD_ERR_INVALID_RESPONSE    (0x104)
  #define MJD_ERR_INVALID_STATE       (0x105)
  #define MJD_ERR_NOT_FOUND           (0x106)
  #define MJD_ERR_NOT_SUPPORTED       (0x107)
  #define MJD_ERR_REGEXP              (0x108)
  #define MJD_ERR_TIMEOUT             (0x109)
  #define MJD_ERR_IO                  (0x110)
  
  #define MJD_ERR_ESP_GPIO            (0x201)
  #define MJD_ERR_ESP_I2C             (0x202)
  #define MJD_ERR_ESP_RMT             (0x203)
  #define MJD_ERR_ESP_RTOS            (0x204)
  #define MJD_ERR_ESP_SNTP            (0x205)
  #define MJD_ERR_ESP_WIFI            (0x206)
  
  #define MJD_ERR_LWIP                (0x301)
  #define MJD_ERR_NETCONN             (0x302)
  
  /**********
   * C Language: utilities, stdlib, etc.
   */
  /*
   * ARRAY_SIZE - get the number of elements in array @arr
   * @arr: array to be sized
   */
  #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
  
  /**********
   * BOOLEAN: printf
   */
  #define MJDBOOLEANFMT "%s"
  #define MJDBOOLEAN2STR(a) (a ? "true" : "false")
  
  /**********
   * INTEGERS: BINARY SEARCH
   */
  int mjd_compare_ints(const void * a, const void * b);
  
  /**********
   * BYTES/WORDS and BINARY REPRESENTATION
   */
  #define MJD_HIBYTE(x) ((uint8_t)((uint16_t)(x) >> 8))
  #define MJD_LOBYTE(x) ((uint8_t)(x))
  #define MJD_HIWORD(x) ((uint16_t)((uint32_t)(x) >> 16))
  #define MJD_LOWORD(x) ((uint16_t)((uint32_t)(x)))
  
  uint8_t mjd_byte_to_bcd(uint8_t val);
  uint8_t mjd_bcd_to_byte(uint8_t val);
  esp_err_t mjd_byte_to_binary_string(uint8_t input_byte, char * output_string);
  esp_err_t mjd_word_to_binary_string(uint16_t input_word, char * output_string);
  
  /**********
   * STRINGS
   */
  bool mjd_string_starts_with(const char *str, const char *pre);
  bool mjd_string_ends_with(const char *str, const char *post);
  char * mjd_string_repeat(const char * s, int n);
  void mjd_string_prepend(char* param_ptr_string, const char* param_ptr_part);
  
  /**********
   * HEX STRINGS
   */
  
  /*
   * @example [0 0x00, 1 0x01, 14 0x0e, 15 0x0f, 240 0xf0, 241 0xf1, 254 0xfe, 255 0xff] => "00010E0FF0F1FEFF"
   * @dep param_ptr_output must point to an array of chars TWICE the size of the param_ptr_input buffer PLUS 1
   */
  esp_err_t mjd_uint8s_to_hexstring(const uint8_t * param_ptr_input, size_t param_len_input, char * param_ptr_output);
  
  /*
   * @example "00010E0FF0F1FEFF" = [0 0x00, 1 0x01, 14 0x0e, 15 0x0f, 240 0xf0, 241 0xf1, 254 0xfe, 255 0xff] => "00010E0FF0F1FEFF"
   * @dep param_ptr_output must point to an array of uint8 at least HALF the size of the param_ptr_input buffer PLUS 1
   */
  esp_err_t mjd_hexstring_to_uint8s(const char * param_ptr_input, size_t param_len_input, uint8_t * param_ptr_output);
  
  /*
   * @example "ABC" => "414243"
   * @dep param_ptr_output must point to an array of chars TWICE the size of the param_ptr_input buffer PLUS 1
   */
  esp_err_t mjd_string_to_hexstring(const char * param_ptr_input, size_t param_len_input, char * param_ptr_output);
  
  /*
   * @example "414243" => "ABC"
   * @dep param_ptr_output must point to an array of chars at least HALF the size of the param_ptr_input buffer PLUS 1
   */
  esp_err_t mjd_hexstring_to_string(const char * param_ptr_input, size_t param_len_input, char * param_ptr_output);
  
  /**********
   * DATE TIME
   * @doc unsigned int (uint32_t on ESP32) Maximum value: 4294967295
   */
  #define SECONDS_PER_DAY               86400
  #define SECONDS_FROM_1970_TO_2000 946684800
  
  uint32_t mjd_seconds_to_milliseconds(uint32_t seconds);
  uint32_t mjd_seconds_to_microseconds(uint32_t seconds);
  void mjd_log_time();
  void mjd_set_timezone_utc();
  void mjd_set_timezone_amsterdam();
  void mjd_get_current_time_yyyymmddhhmmss(char *ptr_buffer);
  
  /**********
   * RTOS
   */
  #define RTOS_DELAY_0             (0)
  #define RTOS_DELAY_1MILLISEC     (   1 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_5MILLISEC     (   5 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_10MILLISEC    (  10 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_25MILLISEC    (  25 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_50MILLISEC    (  50 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_75MILLISEC    (  50 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_100MILLISEC   ( 100 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_125MILLISEC   ( 125 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_150MILLISEC   ( 150 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_200MILLISEC   ( 200 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_250MILLISEC   ( 250 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_500MILLISEC   ( 500 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_1SEC          ( 1 * 1000 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_2SEC          ( 2 * 1000 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_3SEC          ( 3 * 1000 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_5SEC          ( 5 * 1000 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_10SEC         (10 * 1000 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_15SEC         (15 * 1000 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_30SEC         (30 * 1000 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_1MINUTE       ( 1 * 60 * 1000 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_5MINUTES      ( 5 * 60 * 1000 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_15MINUTES     (15 * 60 * 1000 / portTICK_PERIOD_MS)
  #define RTOS_DELAY_MAX           (portMAX_DELAY)
  
  // Use this priority to create all your RTOS tasks so all tasks are ginve equal cputime using the Round Robin method
  //      @doc https://www.freertos.org/RTOS-task-priority.html
  #define RTOS_TASK_PRIORITY_NORMAL (5)
  
  void mjd_rtos_wait_forever();
  
  /**********
   * ESP32 SYSTEM
   */
  void mjd_log_clanguage_details();
  void mjd_log_chip_info();
  void mjd_log_wakeup_details();
  
  typedef struct {
      uint32_t free_esp_heap;
      uint32_t free_rtos_stack;
  } mjd_meminfo_t;
  
  /**********
   * ESP32 SYSTEM & RTOS: Memory
   *
   * @doc uxTaskGetStackHighWaterMark()
   *   Set xTask to NULL to check the stack of the calling RTOS task.
   *   High water mark is the minimum free stack space there has been (***in bytes rather than words as found in vanilla FreeRTOS***)
   *   since the task started. The smaller the returned number the closer the task has come to overflowing its stack.
   *
   * @doc esp_get_free_heap_size() returns available heap size, in bytes.
   *   Note that the returned value may be larger than the maximum contiguous block which can be allocated.
   */
  esp_err_t mjd_get_memory_statistics(mjd_meminfo_t* data);
  esp_err_t mjd_log_memory_statistics();
  
  /**********
   * ESP32: BOOT INFO, DEEP SLEEP and WAKE UP
   */
  uint32_t mjd_increment_mcu_boot_count();
  void mjd_log_mcu_boot_count();
  uint32_t mjd_get_mcu_boot_count();
  void mjd_log_wakeup_details();
  
  /**********
   * ESP32 cJSON
   */
  /*
   * cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
   *     @note cJSON is not always 100% accurate in estimating how much memory it will use, so be safe and allocate 5 bytes more than you actually need.
   */
  #define MJD_CJSON_PRINT_FORMATTED true
  #define MJD_CJSON_PRINT_UNFORMATTED false
  
  
  /**********
   * ESP32: LED
   */
  #define HUZZAH32_GPIO_NUM_LED (GPIO_NUM_13)
  #define HUZZAH32_GPIO_BITPIN_LED (1ULL<<HUZZAH32_GPIO_NUM_LED)
  
  typedef enum {
      LED_WIRING_TYPE_DEFAULT = 1, /*!< Default */
      LED_WIRING_TYPE_DIODE_TO_GND = 1, /*!< Resistor LED-DIODE=> GND (MCU Adafruit Huzzah32) */
      LED_WIRING_TYPE_DIODE_FROM_VCC = 2, /*!< LED-DIODE<= Resistor VCC (MCU Lolin32Lite) */
  } mjd_led_wiring_type_t;
  
  typedef struct {
      uint32_t is_initialized; /*!< Helper to know if an element was initialized, or not. Mark 1. */
      uint64_t gpio_num; /*!< GPIO num pin */
      mjd_led_wiring_type_t wiring_type; /*!< Wiring Type */
  } mjd_led_config_t;
  
  void mjd_led_config(const mjd_led_config_t *led_config);
  void mjd_led_on(int gpio_nr);
  void mjd_led_off(int gpio_nr);
  void mjd_led_blink_times(int gpio_nr, int times);
  void mjd_led_mark_error(int gpio_nr);
  
  #ifdef __cplusplus
  }
  #endif
  
  #endif /* __MJD_H__ */
  