text: /**
   * Copyright (c) 2014 - 2020, Nordic Semiconductor ASA
   *
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without modification,
   * are permitted provided that the following conditions are met:
   *
   * 1. Redistributions of source code must retain the above copyright notice, this
   *    list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form, except as embedded into a Nordic
   *    Semiconductor ASA integrated circuit in a product or a software update for
   *    such product, must reproduce the above copyright notice, this list of
   *    conditions and the following disclaimer in the documentation and/or other
   *    materials provided with the distribution.
   *
   * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
   *    contributors may be used to endorse or promote products derived from this
   *    software without specific prior written permission.
   *
   * 4. This software, with or without modification, must only be used with a
   *    Nordic Semiconductor ASA integrated circuit.
   *
   * 5. Any software provided in binary form under this license must not be reverse
   *    engineered, decompiled, modified and/or disassembled.
   *
   * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
   * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
   * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   */
  #include "nrf_esb.h"
  
  #include <stdbool.h>
  #include <stdint.h>
  #include <string.h>
  #include "sdk_common.h"
  #include "nrf.h"
  #include "nrf_error.h"
  #include "nrf_esb_error_codes.h"
  #include "nrf_delay.h"
  #include "nrf_gpio.h"
  #include "boards.h"
  #include "app_util.h"
  
  #define RESET_MEMORY_TEST_BYTE  (0x0DUL)        /**< Known sequence written to a special register to check if this wake up is from System OFF. */
  #define RAM_RETENTION_OFF       (0x00000003UL)  /**< The flag used to turn off RAM retention on nRF52. */
  
  #define BTN_PRESSED     0                       /**< Value of a pressed button. */
  #define BTN_RELEASED    1                       /**< Value of a released button. */
  
  //#define NRF_ESB_LEGACY
  
  /*lint -save -esym(40, BUTTON_1) -esym(40, BUTTON_2) -esym(40, BUTTON_3) -esym(40, BUTTON_4) -esym(40, LED_1) -esym(40, LED_2) -esym(40, LED_3) -esym(40, LED_4) */
  
  static nrf_esb_payload_t tx_payload = NRF_ESB_CREATE_PAYLOAD(0, 0x01, 0x00);
  static nrf_esb_payload_t rx_payload;
  static uint32_t button_state_1;
  static uint32_t button_state_2;
  static uint32_t button_state_3;
  static uint32_t button_state_4;
  static volatile bool esb_completed = false;
  
  void system_off( void )
  {
  #ifdef NRF51
      NRF_POWER->RAMON |= (POWER_RAMON_OFFRAM0_RAM0Off << POWER_RAMON_OFFRAM0_Pos) |
                          (POWER_RAMON_OFFRAM1_RAM1Off << POWER_RAMON_OFFRAM1_Pos);
  #endif //NRF51
  #ifdef NRF52
      NRF_POWER->RAM[0].POWER = RAM_RETENTION_OFF;
      NRF_POWER->RAM[1].POWER = RAM_RETENTION_OFF;
      NRF_POWER->RAM[2].POWER = RAM_RETENTION_OFF;
      NRF_POWER->RAM[3].POWER = RAM_RETENTION_OFF;
      NRF_POWER->RAM[4].POWER = RAM_RETENTION_OFF;
      NRF_POWER->RAM[5].POWER = RAM_RETENTION_OFF;
      NRF_POWER->RAM[6].POWER = RAM_RETENTION_OFF;
      NRF_POWER->RAM[7].POWER = RAM_RETENTION_OFF;
  #endif //NRF52
  
      // Turn off LEDs before sleeping to conserve energy.
      bsp_board_leds_off();
  
      // Set nRF5 into System OFF. Reading out value and looping after setting the register
      // to guarantee System OFF in nRF52.
      NRF_POWER->SYSTEMOFF = 0x1;
      (void) NRF_POWER->SYSTEMOFF;
      while (true);
  }
  
  
  void nrf_esb_event_handler(nrf_esb_evt_t const * p_event)
  {
      switch (p_event->evt_id)
      {
          case NRF_ESB_EVENT_TX_SUCCESS:
              break;
          case NRF_ESB_EVENT_TX_FAILED:
              (void) nrf_esb_flush_tx();
              break;
          case NRF_ESB_EVENT_RX_RECEIVED:
              // Get the most recent element from the RX FIFO.
              while (nrf_esb_read_rx_payload(&rx_payload) == NRF_SUCCESS) ;
  
              // For each LED, set it as indicated in the rx_payload, but invert it for the button
              // which is pressed. This is because the ack payload from the PRX is reflecting the
              // state from before receiving the packet.
              nrf_gpio_pin_write(LED_1, !( ((rx_payload.data[0] & 0x01) == 0) ^ (button_state_1 == BTN_PRESSED)) );
              nrf_gpio_pin_write(LED_2, !( ((rx_payload.data[0] & 0x02) == 0) ^ (button_state_2 == BTN_PRESSED)) );
              nrf_gpio_pin_write(LED_3, !( ((rx_payload.data[0] & 0x04) == 0) ^ (button_state_3 == BTN_PRESSED)) );
              nrf_gpio_pin_write(LED_4, !( ((rx_payload.data[0] & 0x08) == 0) ^ (button_state_4 == BTN_PRESSED)) );
              break;
      }
  
      esb_completed = true;
  }
  
  
  void clocks_start( void )
  {
      // Start HFCLK and wait for it to start.
      NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
      NRF_CLOCK->TASKS_HFCLKSTART = 1;
      while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
  }
  
  
  uint32_t esb_init( void )
  {
      uint32_t err_code;
      uint8_t base_addr_0[4] = {0xE7, 0xE7, 0xE7, 0xE7};
      uint8_t base_addr_1[4] = {0xC2, 0xC2, 0xC2, 0xC2};
      uint8_t addr_prefix[8] = {0xE7, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8 };
  
  #ifndef NRF_ESB_LEGACY
      nrf_esb_config_t nrf_esb_config         = NRF_ESB_DEFAULT_CONFIG;
  #else // NRF_ESB_LEGACY
      nrf_esb_config_t nrf_esb_config         = NRF_ESB_LEGACY_CONFIG;
  #endif // NRF_ESB_LEGACY
      nrf_esb_config.retransmit_count         = 6;
      nrf_esb_config.selective_auto_ack       = false;
      nrf_esb_config.protocol                 = NRF_ESB_PROTOCOL_ESB_DPL;
      nrf_esb_config.bitrate                  = NRF_ESB_BITRATE_2MBPS;
      nrf_esb_config.event_handler            = nrf_esb_event_handler;
      nrf_esb_config.mode                     = NRF_ESB_MODE_PTX;
  
      err_code = nrf_esb_init(&nrf_esb_config);
      VERIFY_SUCCESS(err_code);
  
      err_code = nrf_esb_set_base_address_0(base_addr_0);
      VERIFY_SUCCESS(err_code);
  
      err_code = nrf_esb_set_base_address_1(base_addr_1);
      VERIFY_SUCCESS(err_code);
  
      err_code = nrf_esb_set_prefixes(addr_prefix, 8);
      VERIFY_SUCCESS(err_code);
  
      tx_payload.length  = 3;
      tx_payload.pipe    = 0;
      tx_payload.data[0] = 0x00;
  
      return NRF_SUCCESS;
  }
  
  
  uint32_t gpio_check_and_esb_tx()
  {
      uint32_t err_code;
      button_state_1 = nrf_gpio_pin_read(BUTTON_1);
      button_state_2 = nrf_gpio_pin_read(BUTTON_2);
      button_state_3 = nrf_gpio_pin_read(BUTTON_3);
      button_state_4 = nrf_gpio_pin_read(BUTTON_4);
      if (button_state_1 == BTN_PRESSED)
      {
          tx_payload.data[0] |= 1 << 0;
      }
      if (button_state_2 == BTN_PRESSED)
      {
          tx_payload.data[0] |= 1 << 1;
      }
      if (button_state_3 == BTN_PRESSED)
      {
          tx_payload.data[0] |= 1 << 2;
      }
      if (button_state_4 == BTN_PRESSED)
      {
          tx_payload.data[0] |= 1 << 3;
      }
      if (button_state_1 + button_state_2 + button_state_3 + button_state_4 == BTN_PRESSED + 3 * BTN_RELEASED)
      {
          tx_payload.noack = false;
          err_code = nrf_esb_write_payload(&tx_payload);
          VERIFY_SUCCESS(err_code);
      }
      else
      {
          esb_completed = true;
      }
  
      return NRF_SUCCESS;
  }
  
  
  void gpio_init( void )
  {
      nrf_gpio_cfg_sense_input(BUTTON_1, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW);
      nrf_gpio_cfg_sense_input(BUTTON_2, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW);
      nrf_gpio_cfg_sense_input(BUTTON_3, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW);
      nrf_gpio_cfg_sense_input(BUTTON_4, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW);
  
      // Workaround for PAN_028 rev1.1 anomaly 22 - System: Issues with disable System OFF mechanism
      nrf_delay_ms(1);
  
      bsp_board_init(BSP_INIT_LEDS);
  }
  
  
  void recover_state()
  {
      uint32_t            loop_count = 0;
      if ((NRF_POWER->GPREGRET >> 4) == RESET_MEMORY_TEST_BYTE)
      {
          // Take the loop_count value.
          loop_count          = (uint8_t)(NRF_POWER->GPREGRET & 0xFUL);
          NRF_POWER->GPREGRET = 0;
      }
  
      loop_count++;
      NRF_POWER->GPREGRET = ( (RESET_MEMORY_TEST_BYTE << 4) | loop_count);
  
      tx_payload.data[1] = loop_count << 4;
  }
  
  
  int main(void)
  {
      uint32_t err_code;
      // Initialize
      clocks_start();
      err_code = esb_init();
      APP_ERROR_CHECK(err_code);
  
      gpio_init();
  
      // Recover state if the device was woken from System OFF.
      recover_state();
  
      // Check state of all buttons and send an esb packet with the button press if there is exactly one.
      err_code = gpio_check_and_esb_tx();
      APP_ERROR_CHECK(err_code);
  
      while (true)
      {
          // Wait for esb completed and all buttons released before going to system off.
          if (esb_completed)
          {
              if (nrf_gpio_pin_read(BUTTON_1) == BTN_RELEASED &&
                  nrf_gpio_pin_read(BUTTON_2) == BTN_RELEASED &&
                  nrf_gpio_pin_read(BUTTON_3) == BTN_RELEASED &&
                  nrf_gpio_pin_read(BUTTON_4) == BTN_RELEASED
                 )
              {
                  system_off();
              }
          }
      }
  }
  /*lint -restore */
  