Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

installation in platform.io #6

Open
cibernox opened this issue Mar 19, 2024 · 7 comments
Open

installation in platform.io #6

cibernox opened this issue Mar 19, 2024 · 7 comments

Comments

@cibernox
Copy link

Hi! This may be me because I'm fairly new to IOT development, but I wanted to use this library on Platform.io instead of the arduino IDE, but it's not clear to me how to add it unofficial boards (if at all possible).

Can this library be installed on Platform.io?

@ropg
Copy link
Owner

ropg commented Mar 19, 2024

Hey there! I'm sure it can be, but to be honest I've really only used other people's board setups in PlatformIO and never been customizing much there. It's on my list to figure that out and make it just as nice and "works out of the box" there as it is in the Arduino IDE. Tomorrow I'm getting the 2 x Wireless Stick Lite I ordered, but that should be minimal work, mostly just verifying that it's like I expect it is and adding a #define and a board definition, and then after that I might get to it.

@aintgotnoname
Copy link

My (humble) answer is Yes, but....

I've only been playing with PlatformIO/C++/VScode for a few months. Even though I sport old gray hairs, I don't consider myself an expert at this new fangled (and wonderful) stuff.

The library installs and is easily usable in single file projects. However, I had duplicated code blocks causing linker problems (even with the no instance macros) with includes in multiple cpp files. For me, it was easiest to duplicate the heltec.h file as a pure include (heltec_api.h) that just references/externs much of the original work. While not pure, this worked for me. I mention it only as an additional include file (and minor refactoring) may keep with the original intent (it just works), while providing api definitions for old foggies that need to include the library definitions throughout multiple files within a project.

Again, my thanks for the library. It saved me a lot of work doing exactly the same on my own.


[env:heltec_wifi_lora_32_V3]
platform = espressif32
board = heltec_wifi_lora_32_V3
framework = arduino
monitor_speed = 115200
upload_speed = 921600

board_build.mcu = esp32s3
board_build.f_cpu = 240000000L

lib_deps =
  ${libraries.pubsubclient}
  ${libraries.arduinojson}
  ;${libraries.arduinolog}
  ;${libraries.radiolib}
  ${libraries.heltec_wifi_kit_32_V3_unofficial}
  GPIO
  SPI
  EEPROM
  SERIAL
  WIFI

build_flags = 
 -DMQTT_MAX_PACKET_SIZE=1000
 -DCORE_DEBUG_LEVEL=0
 -DIOTWEBCONF_DEBUG_DISABLED=1
 -DARDUINOJSON_USE_LONG_LONG=1
 -DLoRaWAN_DEBUG_LEVEL=1
 -DWireless_Stick_V3=1
 -DREGION_US915=1
 -DHELTEC_WIRELESS_STICK=1

And my heltec_api.h : (essentially just function prototypes and defines). Worthy as a discussion, but not worthy as a solution.

/**
 * @file heltec_api.h
 * @brief Header file for the Heltec library.
 *
 * This file contains the definitions and declarations for the Heltec library.
 * The library provides functions for controlling the Heltec ESP32 LoRa V3
 * board, including LED brightness control, voltage measurement, deep sleep
 * mode, and more.
 */

#ifndef heltec_api_h
#define heltec_api_h

// 'PRG' Button
#define BUTTON    GPIO_NUM_0
// LED pin & PWM parameters
#define LED_PIN   GPIO_NUM_35
#define LED_FREQ  5000
#define LED_CHAN  0
#define LED_RES   8
// External power control
#define VEXT      GPIO_NUM_36
// Battery voltage measurement
#define VBAT_CTRL GPIO_NUM_37
#define VBAT_ADC  GPIO_NUM_1
// SPI pins
//#define SS        GPIO_NUM_8
//#define MOSI      GPIO_NUM_10
//#define MISO      GPIO_NUM_11
//#define SCK       GPIO_NUM_9
// Radio pins
#define DIO1      GPIO_NUM_14
//#define RST_LoRa  GPIO_NUM_12
//#define BUSY_LoRa GPIO_NUM_13
// Display pins
//#define SDA_OLED  GPIO_NUM_17
//#define SCL_OLED  GPIO_NUM_18
//#define RST_OLED  GPIO_NUM_21

#ifndef HELTEC_NO_RADIOLIB
  #include "RadioLib/RadioLib.h"
  // make sure the power off button works when using RADIOLIB_OR_HALT
  // (See RadioLib_convenience.h)
  #define RADIOLIB_DO_DURING_HALT heltec_delay(10)
  #include "RadioLib_convenience.h"
  String radiolib_result_string(const int16_t result);
#endif

#ifdef HELTEC_NO_DISPLAY
  #define HELTEC_NO_DISPLAY_INSTANCE
#else
  #include "display/SSD1306Wire.h"
#endif

//#include "HotButton.h"

#ifndef HELTEC_NO_RADIO_INSTANCE
  #ifndef HELTEC_NO_RADIOLIB
    extern SX1262 radio;
  #endif
#endif

// Don't you just hate it when battery percentages are wrong?
//
// I measured the actual voltage drop on a LiPo battery and these are the
// average voltages, expressed in 1/256'th steps between min_voltage and
// max_voltage for each 1/100 of the time it took to discharge the battery. The
// code for a telnet server that outputs battery voltage as CSV data is in
// examples, and a python script that outputs the constants below is in
// src/tools.
extern const float min_voltage;
extern const float max_voltage;
extern const uint8_t scaled_voltage[100];

/**
 * @class PrintSplitter
 * @brief A class that splits the output of the Print class to two different
 *        Print objects.
 *
 * The PrintSplitter class is used to split the output of the Print class to two
 * different Print objects. It overrides the write() function to write the data
 * to both Print objects.
 */
class PrintSplitter : public Print {
  public:
    PrintSplitter(Print &_a, Print &_b) : a(_a), b(_b) {}
    size_t write(uint8_t c) {
      a.write(c);
      return b.write(c);
    }
    size_t write(const char* str) {
      a.write(str);
      return b.write(str);
    }
  private:
    Print &a;
    Print &b;
};


  extern SSD1306Wire display;
  extern PrintSplitter both;


  // extern HotButton button;

/**
 * @brief Controls the LED brightness based on the given percentage.
 *
 * This function sets up the LED channel, frequency, and resolution, and then
 * adjusts the LED brightness based on the given percentage. If the percentage
 * is 0 or less, the LED pin is set as an input pin.
 *
 * @param percent The brightness percentage of the LED (0-100).
 */
void heltec_led(int percent);

/**
 * @brief Controls the VEXT pin to enable or disable external power.
 *
 * This function sets the VEXT pin as an output pin and sets its state based on
 * the given parameter. If the state is true, the VEXT pin is set to LOW to
 * enable external power. If the state is false, the VEXT pin is set to INPUT to
 * disable external power.
 *
 * @param state The state of the VEXT pin (true = enable, false = disable).
 */
void heltec_ve(bool state);

/**
 * @brief Measures the battery voltage.
 *
 * This function measures the battery voltage by controlling the VBAT_CTRL pin
 * and reading the analog value from the VBAT_ADC pin. The measured voltage is
 * then converted to a float value and returned.
 *
 * @return The battery voltage in volts.
 */
float heltec_vbat();

/**
 * @brief Puts the device into deep sleep mode.
 *
 * This function prepares the device for deep sleep mode by disconnecting from
 * WiFi, turning off the display, disabling external power, and turning off the
 * LED. It can also be configured to wake up after a certain number of seconds
 * using the optional parameter.
 *
 * @param seconds The number of seconds to sleep before waking up (default = 0).
 */
void heltec_deep_sleep(int seconds = 0);

/**
 * @brief Calculates the battery percentage based on the measured battery
 * voltage.
 *
 * This function calculates the battery percentage based on the measured battery
 * voltage. If the battery voltage is not provided as a parameter, it will be
 * measured using the heltec_vbat() function. The battery percentage is then
 * returned as an integer value.
 *
 * @param vbat The battery voltage in volts (default = -1).
 * @return The battery percentage (0-100).
 */
int heltec_battery_percent(float vbat = -1);

/**
 * @brief Checks if the device woke up from deep sleep due to button press.
 * 
 * @return True if the wake-up cause is a button press, false otherwise.
 */
bool heltec_wakeup_was_button();

/**
 * @brief Checks if the device woke up from deep sleep due to a timer.
 * 
 * This function checks if the device woke up from deep sleep due to a timer.
 * 
 * @return True if the wake-up cause is a timer interrupt, false otherwise.
 */
bool heltec_wakeup_was_timer();

/**
 * @brief Measures esp32 chip temperature
 * 
 * @return float with temperature in degrees celsius.
*/
float heltec_temperature();

/**
 * @brief Initializes the Heltec library.
 *
 * This function should be the first thing in setup() of your sketch. It
 * initializes the Heltec library by setting up serial port and display.
 */
void heltec_setup();

/**
 * @brief The main loop function for the Heltec library.
 *
 * This function should be called in loop() of the Arduino sketch. It updates
 * the state of the power button and implements long-press power off if used.
 */
void heltec_loop();

/**
 * @brief Delays the execution of the program for the specified number of
 *        milliseconds.
 *
 * This function delays the execution of the program for the specified number of
 * milliseconds. During the delay, it also calls the heltec_loop() function to
 * allow for the power off button to be checked.
 *
 * @param ms The number of milliseconds to delay.
 */
void heltec_delay(int ms);

#endif  // heltec_api_h

@PandemiK911
Copy link

Hello,
Thanks @aintgotnoname for this nice wrapper to include the lib from platformio.
Sorry to bother you with this but I should me missing something to include the header and gain access to functions :
.platformio/packages/[email protected]+2021r2-patch5/bin/../lib/gcc/xtensa-esp32s3-elf/8.4.0/../../../../xtensa-esp32s3-elf/bin/ld: .pio/build/heltec_wifi_lora_32_V3/src/main.cpp.o:(.literal._Z5setupv+0x30): undefined reference to `heltec_setup()'

My platformio.ini is pretty simple :

lib_deps = 
    PubSubClient
    plapointe6/HAMqttDevice@^1.4.0
    ropg/Heltec_ESP32_LoRa_v3@^0.9.1
    ropg/LoRaWAN_ESP32@^1.1.0
    ArduinoJson
    plapointe6/EspMQTTClient
    Preferences

In main.cpp :

#include "heltec_api.h"

SX1262 radio = new Module(LORA_NSS, LORA_DIO1, LORA_NRST, LORA_BUSY);
SSD1306Wire display = (0x3c, 500000, SDA_OLED, SCL_OLED, GEOMETRY_128_64, RST_OLED);

[...] 

void setup() {
  heltec_setup();
  float vbat = heltec_vbat();

heltec_api.h is a simple copy/paste of file in previous comment in the same dir of main.cpp.

So as my understanding, heltec_api.h is included with functions definitions but not the function code itself.
If I include heltec_unofficial.h, it ends (logically) with duplicate definition.

Could you point me out what I missed please ?

Thanks !

@aintgotnoname
Copy link

What I do is include heltec_unofficial.h (once, in main.c). From there, I call heltec_setup() (in setup()).
This provides the actual code definitions once, in main.

Everywhere else (any other .c/.cpp files) I include heltec_api.h; but NOT heltec_unofficial.h.
This provides declarations to the functions defined in the original library file (but without redefining and duplicating the actual code blocks).

Yes, heltec_api.h is really a cut and paste (hack) of the original heltec_unofficial.h to avoid duplicate code definitions.
I tend to recreate it with each new release of the heltec_unofficial.h library file.
In a single file (main.c) project, there is no need use heltec_api.h at all. It solely exists for a multiple .c project.

Sorry for any confusion. I wish my documentation skills were 1/4 as good as @ropg.

@PandemiK911
Copy link

Wow, that's exactly the point I was missing ! Thanks a lot !
Don't be sorry, you did a really nice job so we could use this lib with platformio.

@knowthelist
Copy link

PlatformIO is much better than the Arduino IDE.
1st argument for me is the compile + upload time.
If you change one line code would take Arduino IDE: 1:50 min; PlatformIO: 0:25

How To:
it is very easy from the PIO Home -> Import Arduino Project
Select the board: Heltec WiFi LoRa 32 (V3)
select one *.ino file from the examples

The platfomio.ini looks then like this:

[env:heltec_wifi_kit_32_V3]
platform = espressif32
board = heltec_wifi_kit_32_V3
framework = arduino
lib_deps = ropg/Heltec_ESP32_LoRa_v3@^0.9.1

I you create a new PlatformIO project from the wizard
Select the board: Heltec WiFi LoRa 32 (V3)
Select Framework: Arduino
Add the library ropg/Heltec_ESP32_LoRa_v3 to the project via PIO Home -> Libraries or just add it to the platformio.ini as

lib_deps = ropg/Heltec_ESP32_LoRa_v3@^0.9.1

Copy all from the example *.ino to the main.cpp (if you have a main.c rename it to main.cpp)

If you facing errors like this:

.pio/libdeps/heltec_wifi_lora_32_V3/Heltec_ESP32_LoRa_v3/src/heltec_unofficial.h:33:19: error: 'const uint8_t GPIO_NUM_8' redeclared as different kind of symbol
#define SS GPIO_NUM_8

The change all these lines in the .pio/libdeps/heltec_wifi_lora_32_V3/Heltec_ESP32_LoRa_v3/src/heltec_unofficial.h
from

// SPI pins
#define SS        GPIO_NUM_8
#define MOSI      GPIO_NUM_10
#define MISO      GPIO_NUM_11
#define SCK       GPIO_NUM_9
// Radio pins
#define DIO1      GPIO_NUM_14
#define RST_LoRa  GPIO_NUM_12
#define BUSY_LoRa GPIO_NUM_13
// Display pins
#define SDA_OLED  GPIO_NUM_17
#define SCL_OLED  GPIO_NUM_18
#define RST_OLED  GPIO_NUM_21

to:

// SPI pins
//#define SS        GPIO_NUM_8
//#define MOSI      GPIO_NUM_10
//#define MISO      GPIO_NUM_11
//#define SCK       GPIO_NUM_9
// Radio pins
#define DIO1      GPIO_NUM_14
//#define RST_LoRa  GPIO_NUM_12
//#define BUSY_LoRa GPIO_NUM_13
// Display pins
//#define SDA_OLED  GPIO_NUM_17
//#define SCL_OLED  GPIO_NUM_18
//#define RST_OLED  GPIO_NUM_21

This is necessary due to the fact that these are already defined in the
.platformio/packages/framework-arduinoespressif32/variants/heltec_wifi_lora_32_V3/pins_arduino.h

If you face issues like this:

src/main.cpp: In function 'void setup()':
src/main.cpp:53:23: error: 'rx' was not declared in this scope
   radio.setDio1Action(rx);
                       ^~
src/main.cpp: In function 'void loop()':
src/main.cpp:95:25: error: 'rx' was not declared in this scope
     radio.setDio1Action(rx);
                         ^~
src/main.cpp:111:29: error: 'rx' was not declared in this scope
         radio.setDio1Action(rx);

then just move the function

void rx() {
  rxFlag = true;
}

higher or even on top of the file before the first usage.

@Velocet
Copy link

Velocet commented Oct 3, 2024

There is a neat project on GitHub which abuses the bootloader:
The device never really resets and just waits for an image to execute.
The whole thing was written in assembler and he used it for rapid development. Sadly i cant find it anymore

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants