# How to compile the rnode firmware yourself

## Dependencies

Make sure 'git', 'make', 'python' and 'pip' are instaled. Most like;y these are already availble on your system. Otherwise do:

    $ sudo apt install git
    $ sudo apt install make
    $ sudo apt install pip
    $ sudo apt install python

Download arduino-cli

    $ cd
    $ curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh

Add arduino-cli to path by editing ~/.bashrc

    $ nano ~/.bashrc

    add 'export PATH=~/bin:$PATH' to end of file

## Clone git repo

    $ git clone https://github.com/liberatedsystems/RNode_Firmware_CE.git

## Prepare

Install the required BSP and libraries for the ESP32 system.

    $ cd RNode_Firmware_CE/
    $ make prep-esp32

This command stalled. I stopped the command by hiting CTRL-C and restarted it.

Add rns software to path:

    $ nano ~/.bashrc

    add 'export PATH=~/.local/bin:$PATH' to the end of the file.

## Test

To test if you can compile the firmware try it:

    $ make firmware-heltec32_v3

Fingers crossed!

## Define new board, the theory

In order to build custom firmware for a new board it has to be defined in several files.

### Makefile

BOARD_MODEL defines the target board. It is a unique 8 bit number.
BOARD_VARIANT defines the variant of the board. A board could come with different LoRa transceivers, for example. It is also a unique 8 bit number.

Every target board has a section 'firmware-<board_name>', 'upload-<board-name>' and 'release-<board-name>'

### boards.h

Both BOARD_MODEL and BOARD_VARIANT are #defines in this file. They are used to define the pinout of the SPI port and if it has certain perifirals such as a screen BLE etc.

Let's see an example:

    #elif BOARD_MODEL == BOARD_HELTEC32_V3
      #define IS_ESP32S3 true
      #define HAS_DISPLAY true
      #define DISPLAY OLED
      #define HAS_BLUETOOTH false
      #define HAS_BLE true
      #define HAS_PMU true
      #define HAS_CONSOLE true
      #define HAS_EEPROM true
      #define HAS_INPUT true
      #define HAS_SLEEP true
      #define PIN_WAKEUP GPIO_NUM_0
      #define WAKEUP_LEVEL 0
      #define INTERFACE_COUNT 1
      #define OCP_TUNED 0x38

      const int pin_btn_usr1 = 0;

      #if defined(EXTERNAL_LEDS)
        const int pin_led_rx = 13;
        const int pin_led_tx = 14;
      #else
        const int pin_led_rx = 35;
        const int pin_led_tx = 35;
      #endif

      const uint8_t interfaces[INTERFACE_COUNT] = {SX1262};
      const bool interface_cfg[INTERFACE_COUNT][3] = { 
                    // SX1262
          {
              true, // DEFAULT_SPI
              true, // HAS_TCXO
              true  // DIO2_AS_RF_SWITCH
          }, 
      };
      const int8_t interface_pins[INTERFACE_COUNT][10] = { 
                  // SX1262
          {
              8, // pin_ss
              9, // pin_sclk
              10, // pin_mosi
              11, // pin_miso
              13, // pin_busy
              14, // pin_dio
              12, // pin_reset
              -1, // pin_txen
              -1, // pin_rxen
              -1  // pin_tcxo_enable
          }
      };

If we want to add support for a new board we have to add a new section for this board. Here we define all the features it has or not has.

## Define new board for real

Let's say we want to add support for the Waveshare ESP32-S3 Pico with an SX1278 LoRa transceiver. First we have to make define both BOARD_MODEL and BOARD_VARIANT. Let's define BOARD_MODEL as 0x61 and BOARD_VARIANT as 0x31 as these numbers are not used yet. Here the 0x31 indicates the use of a SX1278 LoRa transceiver. In the future we can define more transceivers for this board. Then we make entries in the Makefile.

    # Added board from Mees Electronics
    firmware-waveshare-esp32-s3-pico: check_bt_buffers
	    arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x61\""

    # Added board from Mees Electronics
    upload-waveshare-esp32-s3-pico:
	    arduino-cli upload -p $(or $(port), /dev/ttyACM0) --fqbn esp32:esp32:esp32s3
	    @sleep 1
	    rnodeconf $(or $(port), /dev/ttyACM0) --firmware-hash $$(./partition_hashes ./build/esp32.esp32.esp32s3/RNode_Firmware_CE.ino.bin)
	    @sleep 3
	    python3 ./Release/esptool/esptool.py --port $(or $(port), /dev/ttyACM0) --chip esp32-s3 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x210000 ./Release/console_image.bin

    release-waveshare-esp32-s3-pico:
        arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x61\" \"-DBOARD_VARIANT=0x31\""
        cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_waveshare-esp32-s3-pico.boot_app0
        cp build/esp32.esp32.waveshare-esp32-s3-pico/RNode_Firmware_CE.ino.bin build/rnode_firmware_waveshare-esp32-s3-pico.bin
        cp build/esp32.esp32.waveshare-esp32-s3-pico/RNode_Firmware_CE.ino.bootloader.bin build/rnode_firmware_waveshare-esp32-s3-pico.bootloader
        cp build/esp32.esp32.waveshare-esp32-s3-pico/RNode_Firmware_CE.ino.partitions.bin build/rnode_firmware_waveshare-esp32-s3-pico.partitions
        zip --junk-paths ./Release/rnode_firmware_waveshare-esp32-s3-pico.zip ./Release/esptool/esptool.py ./Release/console_image.bin build/rnode_firmware_t3s3_sx126xrnode_firmware_waveshare-esp32-s3-pico.boot_app0 build/rnode_firmware_waveshare-esp32-s3-pico.bin build/rnode_firmware_waveshare-esp32-s3-pico.bootloader build/rnode_firmware_waveshare-esp32-s3-pico.partitions
        rm -r build

In the file Boards.h make an entry for the new board inside the '#if MCU_VARIANT == MCU_ESP32':

    // Board definition added by Mees Electronics
    #elif BOARD_MODEL == BOARD_WAVESHARE_ESP32_S3_PICO
      #define IS_ESP32S3 true
      #define HAS_DISPLAY false
      #define HAS_BLUETOOTH false
      #define HAS_BLE true
      #define HAS_PMU true
      #define HAS_CONSOLE true
      #define HAS_EEPROM true
      #define INTERFACE_COUNT 1

      #define HAS_NP true
      const int pin_np = 21;
      #if HAS_NP == false
        #if defined(EXTERNAL_LEDS)
          const int pin_led_rx = 16;
          const int pin_led_tx = 17;
        #else
          const int pin_led_rx = 21;
          const int pin_led_tx = 21;
        #endif
      #endif

      const uint8_t interfaces[INTERFACE_COUNT] = {SX1278};
      const bool interface_cfg[INTERFACE_COUNT][3] = {
                    // SX1278
          {
              false, // DEFAULT_SPI
              false, // HAS_TCXO
              false  // DIO2_AS_RF_SWITCH
          },
      };
      const int8_t interface_pins[INTERFACE_COUNT][10] = {
                  // SX1278
          {
               10, // pin_ss
               12, // pin_sclk
               11, // pin_mosi
               13, // pin_miso
               -1, // pin_busy
               15, // pin_dio
               14, // pin_reset
               -1, // pin_txen
               -1, // pin_rxen
               -1  // pin_tcxo_enable
          }
      };

Also, in the file **Utilities.h** add entry. Again, inside the '#if MCU_VARIANT == MCU_ESP32'.

    	#elif BOARD_MODEL == BOARD_WAVESHARE_ESP32_S3_PICO
            void led_rx_on()  { digitalWrite(pin_led_rx, HIGH); }
            void led_rx_off() {	digitalWrite(pin_led_rx, LOW); }
            void led_tx_on()  { digitalWrite(pin_led_tx, HIGH); }
            void led_tx_off() { digitalWrite(pin_led_tx, LOW); }
            void led_id_on()  { }
            void led_id_off() { }

In **Utilities.h** the functions **led_indicate_info()**, **led_indicate_standby()** and **led_indicate_not_ready()** is also defined for the different boards. If the new board has a NeoPixel, nothing has to be added her.

In **Utilities.h** the function **bool eeprom_model_valid()** also has some board specific definitions. Make sure these are set correctly to prevent an EEPROM error during startup:

    // Added by Mees Electronics
	#elif BOARD_MODEL == BOARD_WAVESHARE_ESP32_S3_PICO
	if (model == MODEL_A4) {

In **Utilities.h** the function **void setTXPower()** defines the LoRa power setting per Lora module/board pair. Add the new board here:

	// Added by Mees Electronics
	if (model == MODEL_A4) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);

The file **display.h** also has a board definition section. If the new board has a display, make sure there is a proper entry for it.  Again, inside the '#if MCU_VARIANT == MCU_ESP32'. If the board does not have a display, don't define this section.

    #elif BOARD_MODEL == BOARD_WAVESHARE_ESP32_S3_PICO
        disp_mode = DISP_MODE_LANDSCAPE;
        display.setRotation(0);

The file **Power.h** must also have a board definition. And also proper definitions in **void measure_battery()** and **bool init_pmu()**. These are more elaborate and not yet fully understood, but probably used when the board has a battery installed. If the new board does not have a battery, do not define anything here!

    #elif BOARD_MODEL == BOARD_WAVESHARE_ESP32_S3_PICO
        #define BAT_V_MIN       3.15
        #define BAT_V_MAX       4.3
        #define BAT_V_CHG       4.48
        #define BAT_V_FLOAT     4.33
        #define BAT_SAMPLES     5
        const uint8_t pin_vbat = 35;
        float bat_p_samples[BAT_SAMPLES];
        float bat_v_samples[BAT_SAMPLES];
        uint8_t bat_samples_count = 0;
        int bat_discharging_samples = 0;
        int bat_charging_samples = 0;
        int bat_charged_samples = 0;
        bool bat_voltage_dropping = false;
        float bat_delay_v = 0;
        float bat_state_change_v = 0;

### Flashing the board

Make shure you are in the dialout group. If not:

    $ sudo usermod -aG dialout <username>

If you are flashing a custom board, you will need to generate a signing key in rnodeconf prior to flashing if you do not already have one by running:

    $ rnodeconf -k

Than flash the firmware.

    $ make upload-waveshare-esp32-s3-pico

This will end with error 'This device has not been provisioned yet, cannot set firmware hash'. But fear not. After flashing a custom board, you will also need to provision the EEPROM before use:

    $ rnodeconf /dev/ttyACM0 -r --platform ESP32 --model a4 --product f0 --hwrev 3

Sometimes the board give error whule flashing. Try flashing the board via the other serial interface (ttyACM1). Don't know why, but it works.

### Migrating to a other system

When the rnode is flashed, it is signed with a key. If you connect the rnode to another system, this key cannot be validated. It is possible to write a new key to the rnode by erasing the eeprom and writing the local key to it.

    $ rnodeconf /dev/ttyACM0 --eeprom-wipe
    $ rnodeconf /dev/ttyACM0 -r --platform ESP32 --model a4 --product f0 --hwrev 3