Code: Alles auswählen
}CODE entfernt, da Fehlerhaft! 21.03.26 Code: Alles auswählen
/* Sendertest für Heltec LoRa V4 - Vorsicht, nicht ohne Antenne betreiben. Zerstoerungsgefahr!!!!
*
* Ruesseltechnik 2026
* */
#include "LoRaWan_APP.h"
#include "Arduino.h"
#include "HT_st7735.h"
#include "HT_SSD1306Wire.h"
#if defined(WIRELESS_TRACKER_V2)
HT_st7735 st7735;
#endif
#if defined(WIFI_LORA_32_V4) || defined(WIFI_LORA_32_V2)
SSD1306Wire factory_display(0x3c, 500000, SDA_OLED, SCL_OLED, GEOMETRY_128_64, RST_OLED); // addr , freq , i2c group , resolution , rst
#endif
#define RF_FREQUENCY 868000000 // Hz - Frequenzeinstellung
#define TX_OUTPUT_POWER 30 // dBm - HF Leistung, 30 dBm entsprechen 1 Watt Ausgangsleistung
#define LORA_BANDWIDTH 1 // [0: 125 kHz,
// 1: 250 kHz,
// 2: 500 kHz,
// 3: Reserved]
#define LORA_SPREADING_FACTOR 11 // [SF7..SF12]
#define LORA_CODINGRATE 1 // [1: 4/5,
// 2: 4/6,
// 3: 4/7,
// 4: 4/8]
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx
#define LORA_SYMBOL_TIMEOUT 0 // Symbols
#define LORA_FIX_LENGTH_PAYLOAD_ON false
#define LORA_IQ_INVERSION_ON false
#define RX_TIMEOUT_VALUE 1000
#define BUFFER_SIZE 30 // Define the payload size here
char txpacket[BUFFER_SIZE];
char rxpacket[BUFFER_SIZE];
double txNumber;
bool lora_idle=true;
static RadioEvents_t RadioEvents;
void OnTxDone( void );
void OnTxTimeout( void );
void setup() {
Serial.begin(115200);
Mcu.begin(HELTEC_BOARD,SLOW_CLK_TPYE);
txNumber=0;
RadioEvents.TxDone = OnTxDone;
RadioEvents.TxTimeout = OnTxTimeout;
Radio.Init( &RadioEvents );
Radio.SetChannel( RF_FREQUENCY );
Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH,
LORA_SPREADING_FACTOR, LORA_CODINGRATE,
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON,
true, 0, 0, LORA_IQ_INVERSION_ON, 3000 );
#if defined(WIRELESS_TRACKER_V2)
st7735.st7735_init();
st7735.st7735_fill_screen(ST7735_BLACK);
st7735.st7735_write_str(0, 0, (String)"WIRELESS_TRACKER_V2");
st7735.st7735_write_str(30, 0, (String)RF_FREQUENCY);
#endif
#if defined(WIFI_LORA_32_V4) || defined(WIFI_LORA_32_V2)
pinMode(Vext,OUTPUT);
digitalWrite(Vext, LOW);
delay(100);
factory_display.init();
factory_display.clear();
factory_display.drawString(0, 0, (String)"WIFI_LORA_32_V4");
factory_display.display();
#endif
}
uint32_t num = 0;
void loop()
{
if(num>=500)
{
Serial.println("Test Ende!");
#if defined(WIFI_LORA_32_V4) || defined(WIFI_LORA_32_V2)
factory_display.clear();
factory_display.drawString(0, 0, (String)"Test Ende!");
factory_display.display();
#endif
while (1)
{
delay(1000);
}
}
if(lora_idle == true)
{
delay(100);
txNumber += 0.01;
sprintf(txpacket,"30dBm %d MHz Nr. %0.2f",RF_FREQUENCY/1000000,txNumber); //start a package
#if defined(WIRELESS_TRACKER_V2)
st7735.st7735_write_str(0, 0, (String)txpacket);
#endif
#if defined(WIFI_LORA_32_V4) || defined(WIFI_LORA_32_V2)
factory_display.clear();
factory_display.drawString(0, 0, (String)txpacket);
factory_display.display();
#endif
Serial.printf("\r\nsending packet \"%s\" , length %d\r\n",txpacket, strlen(txpacket));
Radio.Send( (uint8_t *)txpacket, strlen(txpacket) ); //send the package out
lora_idle = false;
}
Radio.IrqProcess( );
}
void OnTxDone( void )
{
Serial.println("TX done......");
lora_idle = true;
num++;
}
void OnTxTimeout( void )
{
Radio.Sleep( );
Serial.println("TX Timeout......");
lora_idle = true;
}
Code: Alles auswählen
/*
* Heltec LoRa V4.3 PingPong Test
* c2026 Ruesseltechnik - Achtung!! Nicht ohne Antenne ausfuehren, Zerstoerungsgefahr!!!!
*/
#include "Arduino.h"
#include "LoRaWan_APP.h"
#include <Wire.h>
#include "HT_SSD1306Wire.h"
/********************************* lora *********************************************/
#define RF_FREQUENCY 868000000 // Hz - auf angepasster Antenne achten!! Zerstörungsgefahr!!!
#define TX_OUTPUT_POWER 27 // dBm max.30 - Antenne unbedingt anschliessen!!!
#define LORA_BANDWIDTH 0 // [0: 125 kHz, - größte Reichweite, langsamste Übertragung.
// 1: 250 kHz,
// 2: 500 kHz,
// 3: Reserved]
#define LORA_SPREADING_FACTOR 7 // [SF7..SF12]Pegel, die durch das SNR (Signal Noise Ratio)noch identifiziert werden. SF7: -123 dBm, SF12: -137 dBm
#define LORA_CODINGRATE 1 // [1: 4/5, Je kleiner die Codierungsrate ist (die kleinste ist 4/8), desto höher ist die On-Air-Zeit bei einer Übertragung, damit deutlicher.
// 2: 4/6,
// 3: 4/7,
// 4: 4/8]
#define LORA_PREAMBLE_LENGTH 8 // Es bedeutet, dass alle 4 Nutzbits je nach Wert durch 5, 6, 7 oder 8 Übertragungsbits codiert werden.
#define LORA_SYMBOL_TIMEOUT 0 // Bei Empangsabbruch die Zeit den Empfänger schlafen zu legen
#define LORA_FIX_LENGTH_PAYLOAD_ON false
#define LORA_IQ_INVERSION_ON false
#define RX_TIMEOUT_VALUE 1000
#define BUFFER_SIZE 30 // Definiere die zu übertragende Datengröße
char txpacket[BUFFER_SIZE];
char rxpacket[BUFFER_SIZE];
static RadioEvents_t RadioEvents;
void OnTxDone( void );
void OnTxTimeout( void );
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr );
typedef enum
{
LOWPOWER,
STATE_RX,
STATE_TX
}States_t;
int16_t txNumber;
int16_t rxNumber;
States_t state;
bool sleepMode = false;
int16_t Rssi,rxSize;
String rssi = "RSSI --";
String packSize = "--";
String packet;
String send_num;
String show_lora = "Empfange Daten:";
unsigned int counter = 0;
bool receiveflag = false; // Software Flag für LoRa Empfänger, empfangene Daten machen es "wahr"
long lastSendTime = 0; // Letzte Sendezeit
int interval = 1000; // Interval zwischen zwei Sendungen
uint64_t chipid;
int16_t RssiDetection = 0;
void OnTxDone( void )
{
Serial.print("TX done......");
state=STATE_RX;
}
void OnTxTimeout( void )
{
Radio.Sleep( );
Serial.print("TX Timeout......");
state=STATE_TX;
}
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )
{
rxNumber++;
Rssi=rssi;
rxSize=size;
memcpy(rxpacket, payload, size );
rxpacket[size]='\0';
Radio.Sleep( );
Serial.printf("\r\nreceived packet \"%s\" with Rssi %d , length %d\r\n",rxpacket,Rssi,rxSize);
Serial.println("Warte um naestes Paket zu senden");
receiveflag = true;
state=STATE_TX;
}
void lora_init(void)
{
Mcu.begin(HELTEC_BOARD,SLOW_CLK_TPYE);
txNumber=0;
Rssi=0;
rxNumber = 0;
RadioEvents.TxDone = OnTxDone;
RadioEvents.TxTimeout = OnTxTimeout;
RadioEvents.RxDone = OnRxDone;
Radio.Init( &RadioEvents );
Radio.SetChannel( RF_FREQUENCY );
Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH,
LORA_SPREADING_FACTOR, LORA_CODINGRATE,
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON,
true, 0, 0, LORA_IQ_INVERSION_ON, 3000 );
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR,
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
0, true, 0, 0, LORA_IQ_INVERSION_ON, true );
state=STATE_TX;
}
/********************************* lora *********************************************/
SSD1306Wire factory_display(0x3c, 500000, SDA_OLED, SCL_OLED, GEOMETRY_128_64, RST_OLED); // addr , freq , i2c group , resolution , rst
bool resendflag=false;
bool deepsleepflag=false;
bool interrupt_flag = false;
void interrupt_GPIO0()
{
interrupt_flag = true;
}
void interrupt_handle(void)
{
if(interrupt_flag)
{
interrupt_flag = false;
if(digitalRead(0)==0)
{
if(rxNumber <=2)
{
resendflag=true;
}
else
{
deepsleepflag=true;
}
}
}
}
void VextON(void)
{
pinMode(Vext,OUTPUT);
digitalWrite(Vext, LOW);
}
void VextOFF(void) //Vext default OFF
{
pinMode(Vext,OUTPUT);
digitalWrite(Vext, HIGH);
}
void setup()
{
Serial.begin(115200);
VextON();
delay(100);
factory_display.init();
factory_display.clear();
chipid=ESP.getEfuseMac();//Die Chip-ID ist im Wesentlichen seine MAC-Adresse (Länge: 6 Bytes).
Serial.printf("ESP32ChipID=%04X",(uint16_t)(chipid>>32));//print High 2 bytes
Serial.printf("%08X\n",(uint32_t)chipid);//print Low 4bytes.
attachInterrupt(0,interrupt_GPIO0,FALLING);
lora_init();
packet ="Warte auf Datenempfang!";
factory_display.drawString(0, 10, packet);
factory_display.display();
delay(100);
factory_display.clear();
pinMode(LED ,OUTPUT);
digitalWrite(LED, LOW);
}
void loop()
{
interrupt_handle();
if(deepsleepflag)
{
VextOFF();
Radio.Sleep();
SPI.end();
pinMode(RADIO_DIO_1,ANALOG);
pinMode(RADIO_NSS,ANALOG);
pinMode(RADIO_RESET,ANALOG);
pinMode(RADIO_BUSY,ANALOG);
pinMode(LORA_CLK,ANALOG);
pinMode(LORA_MISO,ANALOG);
pinMode(LORA_MOSI,ANALOG);
esp_sleep_enable_timer_wakeup(600*1000*(uint64_t)1000);
esp_deep_sleep_start();
}
if(resendflag)
{
state = STATE_TX;
resendflag = false;
}
if(receiveflag && (state==LOWPOWER) )
{
receiveflag = false;
packet ="Inhalt:";
int i = 0;
while(i < rxSize)
{
packet += rxpacket[i];
i++;
}
packSize = "R_Size: ";
packSize += String(rxSize,DEC);
packSize += " R_rssi: ";
packSize += String(Rssi,DEC);
send_num = "Sende Nummer: ";
send_num += String(txNumber,DEC);
factory_display.drawString(0, 0, show_lora);
factory_display.drawString(0, 10, packet);
factory_display.drawString(0, 20, packSize);
factory_display.drawString(0, 50, send_num);
factory_display.display();
delay(10);
factory_display.clear();
}
switch(state)
{
case STATE_TX:
delay(1000);
txNumber++;
sprintf(txpacket,"Ahoi %d,Rssi:%d",txNumber,Rssi); // Sendenachricht die verschickt wird, hier ein freundliches Ahoi!
Serial.printf("\r\nsending packet \"%s\" , length %d\r\n",txpacket, strlen(txpacket));
Radio.Send( (uint8_t *)txpacket, strlen(txpacket) );
state=LOWPOWER;
break;
case STATE_RX:
Serial.println("into RX mode");
Radio.Rx( 0 );
digitalWrite(LED, HIGH);
delay(100);
digitalWrite(LED, LOW);
state=LOWPOWER;
break;
case LOWPOWER:
Radio.IrqProcess( );
break;
default:
break;
}
}
-----------------------------------------------------------------------------------------------Ja, dafür gibt es inzwischen eine ziemlich klare Erklärung. Der springende Punkt ist: bei Heltec WiFi LoRa 32 V4.3.1 wurde das HF-Frontend geändert. Heltec dokumentiert, dass der FEM von GC1109 auf KCT8103L umgestellt wurde, damit sich der RX-Pfad per Software zwischen LNA aktiv und Bypass umschalten lässt. In demselben Update wurde GPIO5 als FEM-Steuerpin eingeführt, während GPIO46 wieder frei geworden ist. Genau deshalb kann Code, der auf V4.2 noch lief, auf V4.3/4.3.1 plötzlich keinen brauchbaren Empfang mehr haben, wenn diese Umschaltung nicht explizit gemacht wird.
Meshtastic hat das bereits sauber abgebildet. In variant.h für heltec_v4 ist hinterlegt: GPIO7 schaltet die FEM-Versorgung, GPIO2 aktiviert den KCT8103L, GPIO5 wählt den RX-Modus, und SX1262 DIO2 wird als RF-Switch benutzt. Für den KCT8103L gilt dort ausdrücklich: GPIO5 LOW = RX mit LNA, GPIO5 HIGH = RX-Bypass. Außerdem ist SX126X_DIO2_AS_RF_SWITCH gesetzt, damit der SX1262 den TX/RX-Pfad passend steuert.
Noch deutlicher wird es in Meshtastics LoRaFEMInterface.cpp: Beim Initialisieren des KCT8103L setzt Meshtastic GPIO7 auf HIGH, GPIO2 auf HIGH und GPIO5 auf LOW, mit dem Kommentar „LNA enabled by default“. In einem späteren Fix für Heltec v4.3 steht sogar ausdrücklich, dass Nutzer von v4.3, die das nicht explizit setzen, schlechten RX bekommen. Menschliche Hardwarepflege eben: ein neuer Pin, ein stilles Problem, und dann rätseln alle.
Code: Alles auswählen
/* Sendertest für Heltec LoRa ab V4 - Vorsicht, nicht ohne Antenne betreiben. Zerstoerungsgefahr!!!!
*
* Ruesseltechnik 2026.3.25
* */
#include "LoRaWan_APP.h"
#include "Arduino.h"
#include "HT_st7735.h"
#include "HT_SSD1306Wire.h"
#if defined(WIRELESS_TRACKER_V2)
HT_st7735 st7735;
#endif
#if defined(WIFI_LORA_32_V4) || defined(WIFI_LORA_32_V2)
SSD1306Wire factory_display(0x3c, 500000, SDA_OLED, SCL_OLED, GEOMETRY_128_64, RST_OLED);
#endif
#define RF_FREQUENCY 868000000
#define TX_OUTPUT_POWER 30
#define LORA_BANDWIDTH 1
#define LORA_SPREADING_FACTOR 11
#define LORA_CODINGRATE 1
#define LORA_PREAMBLE_LENGTH 8
#define LORA_SYMBOL_TIMEOUT 0
#define LORA_FIX_LENGTH_PAYLOAD_ON false
#define LORA_IQ_INVERSION_ON false
#define RX_TIMEOUT_VALUE 1000
#define BUFFER_SIZE 30
char txpacket[BUFFER_SIZE];
char rxpacket[BUFFER_SIZE];
double txNumber;
bool lora_idle = true;
static RadioEvents_t RadioEvents;
void OnTxDone( void );
void OnTxTimeout( void );
void ensureHighPowerPins() {
// Set the control pins required for high power on Heltec V4.3
pinMode(7, OUTPUT); digitalWrite(7, HIGH);
pinMode(2, OUTPUT); digitalWrite(2, HIGH);
pinMode(5, OUTPUT); digitalWrite(5, HIGH);
delay(10);
}
void setup() {
Serial.begin(115200);
Mcu.begin(HELTEC_BOARD,SLOW_CLK_TPYE);
txNumber = 0;
RadioEvents.TxDone = OnTxDone;
RadioEvents.TxTimeout = OnTxTimeout;
// --- Wichtig: GPIOs für hohe Sendeleistung VOR Radio.Init setzen ---
ensureHighPowerPins();
// -------------------------------------------------------------------
Radio.Init( &RadioEvents );
Radio.SetChannel( RF_FREQUENCY );
// Hinweis: das 'true' in SetTxConfig ist hier so belassen wie im Original.
// Falls eure Radio-API eine explizite PA_BOOST-Option hat, stellt sicher, dass PA_BOOST verwendet wird.
Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH,
LORA_SPREADING_FACTOR, LORA_CODINGRATE,
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON,
true, 0, 0, LORA_IQ_INVERSION_ON, 3000 );
#if defined(WIRELESS_TRACKER_V2)
st7735.st7735_init();
st7735.st7735_fill_screen(ST7735_BLACK);
st7735.st7735_write_str(0, 0, (String)"WIRELESS_TRACKER_V2");
st7735.st7735_write_str(30, 0, (String)RF_FREQUENCY);
#endif
#if defined(WIFI_LORA_32_V4) || defined(WIFI_LORA_32_V2)
pinMode(Vext,OUTPUT);
digitalWrite(Vext, LOW);
delay(100);
factory_display.init();
factory_display.clear();
factory_display.drawString(0, 0, (String)"WIFI_LORA_32_V4");
factory_display.display();
#endif
}
uint32_t num = 0;
void loop()
{
// Stelle sicher, dass die High-Power-Pins dauerhaft HIGH bleiben
ensureHighPowerPins();
if(num >= 500)
{
Serial.println("Test Ende!");
#if defined(WIFI_LORA_32_V4) || defined(WIFI_LORA_32_V2)
factory_display.clear();
factory_display.drawString(0, 0, (String)"Test Ende!");
factory_display.display();
#endif
while (1) { delay(1000); }
}
if(lora_idle == true)
{
delay(100);
txNumber += 0.01;
sprintf(txpacket,"30dBm %d MHz Nr. %0.2f",RF_FREQUENCY/1000000,txNumber);
#if defined(WIRELESS_TRACKER_V2)
st7735.st7735_write_str(0, 0, (String)txpacket);
#endif
#if defined(WIFI_LORA_32_V4) || defined(WIFI_LORA_32_V2)
factory_display.clear();
factory_display.drawString(0, 0, (String)txpacket);
factory_display.display();
#endif
Serial.printf("\r\nsending packet \"%s\" , length %d\r\n",txpacket, strlen(txpacket));
Radio.Send( (uint8_t *)txpacket, strlen(txpacket) );
lora_idle = false;
}
Radio.IrqProcess();
}
void OnTxDone( void )
{
// Sicherstellen, dass die Steuerpins nach TX weiterhin HIGH sind
ensureHighPowerPins();
Serial.println("TX done......");
lora_idle = true;
num++;
}
void OnTxTimeout( void )
{
// Wenn Radio in Sleep geht, die Pins trotzdem HIGH lassen (PA aktiviert)
ensureHighPowerPins();
Radio.Sleep();
Serial.println("TX Timeout......");
lora_idle = true;
}
Code: Alles auswählen
/*
* Heltec LoRa V4.31 PingPong Test mit eingeschalteten Booster für 30 dBm Sendeleistung
* c2026.03.26 Ruesseltechnik / Teilweise KI generiert - Achtung!! Nicht ohne Antenne ausfuehren, Zerstoerungsgefahr!!!!
*/
#include "Arduino.h"
#include "LoRaWan_APP.h"
#include <Wire.h>
#include "HT_SSD1306Wire.h"
/********************************* lora *********************************************/
#define RF_FREQUENCY 869000000 // Hz - auf angepasster Antenne achten!! Zerstörungsgefahr!!!
#define TX_OUTPUT_POWER 27 // dBm max.30 - Antenne unbedingt anschliessen!!!
#define LORA_BANDWIDTH 0 // [0: 125 kHz, 1: 250 kHz, 2: 500 kHz]
#define LORA_SPREADING_FACTOR 12 // [SF7..SF12]
#define LORA_CODINGRATE 1 // [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
#define LORA_PREAMBLE_LENGTH 8
#define LORA_SYMBOL_TIMEOUT 0
#define LORA_FIX_LENGTH_PAYLOAD_ON false
#define LORA_IQ_INVERSION_ON false
#define RX_TIMEOUT_VALUE 1000
#define BUFFER_SIZE 30 // Definiere die zu übertragende Datengröße
char txpacket[BUFFER_SIZE];
char rxpacket[BUFFER_SIZE];
/* Heltec V4.3 FEM control pins (KCT8103L) */
#define FEM_VCC_PIN 7 // schaltet FEM-Versorgung (Vfem)
#define FEM_ENABLE_PIN 2 // KCT8103L enable
#define FEM_RXMODE_PIN 5 // RX LNA select: LOW = LNA enabled, HIGH = RX bypass
static RadioEvents_t RadioEvents;
void OnTxDone( void );
void OnTxTimeout( void );
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr );
typedef enum
{
LOWPOWER,
STATE_RX,
STATE_TX
}States_t;
int16_t txNumber;
int16_t rxNumber;
States_t state;
bool sleepMode = false;
int16_t Rssi,rxSize;
String rssi = "RSSI --";
String packSize = "--";
String packet;
String send_num;
String show_lora = "Empfange Daten:";
unsigned int counter = 0;
bool receiveflag = false; // Software Flag für LoRa Empfänger, empfangene Daten machen es "wahr"
long lastSendTime = 0; // Letzte Sendezeit
int interval = 1000; // Interval zwischen zwei Sendungen
uint64_t chipid;
int16_t RssiDetection = 0;
void OnTxDone( void )
{
Serial.print("TX done......");
// Nach TX wieder LNA aktivieren für bestmöglichen Empfang
digitalWrite(FEM_RXMODE_PIN, LOW);
state=STATE_RX;
}
void OnTxTimeout( void )
{
Radio.Sleep( );
Serial.print("TX Timeout......");
state=STATE_TX;
}
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )
{
rxNumber++;
Rssi=rssi;
rxSize=size;
memcpy(rxpacket, payload, size );
rxpacket[size]='\0';
Radio.Sleep( );
Serial.printf("\r\nreceived packet \"%s\" with Rssi %d , length %d\r\n",rxpacket,Rssi,rxSize);
Serial.println("Warte um naestes Paket zu senden");
receiveflag = true;
// Sicherstellen, dass LNA aktiv ist
digitalWrite(FEM_RXMODE_PIN, LOW);
state=STATE_TX;
}
/* FEM initialisieren: FEM-Versorgung, FEM enable, LNA enabled by default */
void fem_init() {
pinMode(FEM_VCC_PIN, OUTPUT);
pinMode(FEM_ENABLE_PIN, OUTPUT);
pinMode(FEM_RXMODE_PIN, OUTPUT);
digitalWrite(FEM_VCC_PIN, HIGH); // Vfem einschalten
digitalWrite(FEM_ENABLE_PIN, HIGH); // KCT8103L aktivieren
digitalWrite(FEM_RXMODE_PIN, LOW); // LNA enabled by default
delay(5);
}
void lora_init(void)
{
Mcu.begin(HELTEC_BOARD,SLOW_CLK_TPYE);
fem_init(); // <<< neu: FEM initialisieren bevor Radio initialisiert wird
txNumber=0;
Rssi=0;
rxNumber = 0;
RadioEvents.TxDone = OnTxDone;
RadioEvents.TxTimeout = OnTxTimeout;
RadioEvents.RxDone = OnRxDone;
Radio.Init( &RadioEvents );
Radio.SetChannel( RF_FREQUENCY );
Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH,
LORA_SPREADING_FACTOR, LORA_CODINGRATE,
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON,
true, 0, 0, LORA_IQ_INVERSION_ON, 3000 );
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR,
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
0, true, 0, 0, LORA_IQ_INVERSION_ON, true );
state=STATE_TX;
}
/********************************* lora *********************************************/
SSD1306Wire factory_display(0x3c, 500000, SDA_OLED, SCL_OLED, GEOMETRY_128_64, RST_OLED); // addr , freq , i2c group , resolution , rst
bool resendflag=false;
bool deepsleepflag=false;
bool interrupt_flag = false;
void interrupt_GPIO0()
{
interrupt_flag = true;
}
void interrupt_handle(void)
{
if(interrupt_flag)
{
interrupt_flag = false;
if(digitalRead(0)==0)
{
if(rxNumber <=2)
{
resendflag=true;
}
else
{
deepsleepflag=true;
}
}
}
}
void VextON(void)
{
pinMode(Vext,OUTPUT);
digitalWrite(Vext, LOW);
}
void VextOFF(void) //Vext default OFF
{
pinMode(Vext,OUTPUT);
digitalWrite(Vext, HIGH);
}
void setup()
{
Serial.begin(115200);
VextON();
delay(100);
factory_display.init();
factory_display.clear();
chipid=ESP.getEfuseMac();//Die Chip-ID ist im Wesentlichen seine MAC-Adresse (Länge: 6 Bytes).
Serial.printf("ESP32ChipID=%04X",(uint16_t)(chipid>>32));//print High 2 bytes
Serial.printf("%08X\n",(uint32_t)chipid);//print Low 4bytes.
attachInterrupt(0,interrupt_GPIO0,FALLING);
lora_init();
packet ="Warte auf Datenempfang!";
factory_display.drawString(0, 10, packet);
factory_display.display();
delay(100);
factory_display.clear();
pinMode(LED ,OUTPUT);
digitalWrite(LED, LOW);
}
void loop()
{
interrupt_handle();
if(deepsleepflag)
{
VextOFF();
Radio.Sleep();
SPI.end();
pinMode(RADIO_DIO_1,ANALOG);
pinMode(RADIO_NSS,ANALOG);
pinMode(RADIO_RESET,ANALOG);
pinMode(RADIO_BUSY,ANALOG);
pinMode(LORA_CLK,ANALOG);
pinMode(LORA_MISO,ANALOG);
pinMode(LORA_MOSI,ANALOG);
esp_sleep_enable_timer_wakeup(600*1000*(uint64_t)1000);
esp_deep_sleep_start();
}
if(resendflag)
{
state = STATE_TX;
resendflag = false;
}
if(receiveflag && (state==LOWPOWER) )
{
receiveflag = false;
packet ="Inhalt:";
int i = 0;
while(i < rxSize)
{
packet += rxpacket[i];
i++;
}
packSize = "R_Size: ";
packSize += String(rxSize,DEC);
packSize += " R_rssi: ";
packSize += String(Rssi,DEC);
send_num = "Sende Nummer: ";
send_num += String(txNumber,DEC);
factory_display.drawString(0, 0, show_lora);
factory_display.drawString(0, 10, packet);
factory_display.drawString(0, 20, packSize);
factory_display.drawString(0, 50, send_num);
factory_display.display();
delay(10);
factory_display.clear();
}
switch(state)
{
case STATE_TX:
delay(1000);
txNumber++;
// Bypass LNA während TX
digitalWrite(FEM_RXMODE_PIN, HIGH);
sprintf(txpacket,"Ahoi %d,Rssi:%d",txNumber,Rssi); // Sendenachricht die verschickt wird
Serial.printf("\r\nsending packet \"%s\" , length %d\r\n",txpacket, strlen(txpacket));
Radio.Send( (uint8_t *)txpacket, strlen(txpacket) );
state=LOWPOWER;
break;
case STATE_RX:
Serial.println("into RX mode");
// Sicherstellen, dass LNA aktiv ist bevor RX gestartet wird
digitalWrite(FEM_RXMODE_PIN, LOW);
Radio.Rx( 0 );
digitalWrite(LED, HIGH);
delay(100);
digitalWrite(LED, LOW);
state=LOWPOWER;
break;
case LOWPOWER:
Radio.IrqProcess( );
break;
default:
break;
}
}
Code: Alles auswählen
/*
* Heltec LoRa V4.31 PingPong Test mit eingeschalteten Booster für 30 dBm Sendeleistung, Version laeuft bei Unterbrechung nach neuen Kontakt weiter.
* c2026.03.30 Ruesseltechnik / Teilweise KI generiert - Achtung!! Nicht ohne Antenne ausfuehren, Zerstoerungsgefahr!!!!
*/
#include "Arduino.h"
#include "LoRaWan_APP.h"
#include <Wire.h>
#include "HT_SSD1306Wire.h"
/********************************* lora *********************************************/
#define RF_FREQUENCY 868000000
#define TX_OUTPUT_POWER 30
#define LORA_BANDWIDTH 0
#define LORA_SPREADING_FACTOR 12
#define LORA_CODINGRATE 1
#define LORA_PREAMBLE_LENGTH 8
#define LORA_SYMBOL_TIMEOUT 0
#define LORA_FIX_LENGTH_PAYLOAD_ON false
#define LORA_IQ_INVERSION_ON false
#define RX_TIMEOUT_VALUE 1000
#define BUFFER_SIZE 30
char txpacket[BUFFER_SIZE];
char rxpacket[BUFFER_SIZE];
/* Heltec V4.3 FEM control pins (KCT8103L) */
#define FEM_VCC_PIN 7
#define FEM_ENABLE_PIN 2
#define FEM_RXMODE_PIN 5
static RadioEvents_t RadioEvents;
void OnTxDone( void );
void OnTxTimeout( void );
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr );
typedef enum
{
LOWPOWER,
STATE_RX,
STATE_TX
}States_t;
int16_t txNumber;
int16_t rxNumber;
States_t state;
bool sleepMode = false;
int16_t Rssi,rxSize;
String rssi = "RSSI --";
String packSize = "--";
String packet;
String send_num;
String show_lora = "Empfange Daten:";
unsigned int counter = 0;
bool receiveflag = false;
long lastSendTime = 0;
int interval = 1000;
uint64_t chipid;
int16_t RssiDetection = 0;
/* --- Neuer Mechanismus: Activity / Reconnect --- */
unsigned long lastActivityMillis = 0;
const unsigned long RECONNECT_TIMEOUT_MS = 30000; // 30s Inaktivität -> Reinit
void updateActivity() {
lastActivityMillis = millis();
}
void OnTxDone( void )
{
Serial.println("TX done......");
digitalWrite(FEM_RXMODE_PIN, LOW); // LNA wieder aktivieren
state=STATE_RX;
updateActivity();
}
void OnTxTimeout( void )
{
Radio.Sleep( );
Serial.println("TX Timeout......");
state=STATE_TX;
updateActivity();
}
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )
{
rxNumber++;
Rssi=rssi;
rxSize=size;
memcpy(rxpacket, payload, size );
rxpacket[size]='\0';
Radio.Sleep( );
Serial.printf("\r\nreceived packet \"%s\" with Rssi %d , length %d\r\n",rxpacket,Rssi,rxSize);
Serial.println("Warte um naestes Paket zu senden");
receiveflag = true;
digitalWrite(FEM_RXMODE_PIN, LOW);
state=STATE_TX;
updateActivity();
}
/* FEM initialisieren */
void fem_init() {
pinMode(FEM_VCC_PIN, OUTPUT);
pinMode(FEM_ENABLE_PIN, OUTPUT);
pinMode(FEM_RXMODE_PIN, OUTPUT);
digitalWrite(FEM_VCC_PIN, HIGH);
digitalWrite(FEM_ENABLE_PIN, HIGH);
digitalWrite(FEM_RXMODE_PIN, LOW);
delay(5);
}
/* Radio (Re)Initialisierung in eigene Funktion ausgelagert */
void radio_reinit() {
Serial.println("Radio Reinit: Starte Neuinitialisierung...");
// FEM sicherstellen
fem_init();
// Radio Events erneut zuweisen (falls verloren)
RadioEvents.TxDone = OnTxDone;
RadioEvents.TxTimeout = OnTxTimeout;
RadioEvents.RxDone = OnRxDone;
// Radio neu initialisieren und konfigurieren
Radio.Init( &RadioEvents );
Radio.SetChannel( RF_FREQUENCY );
Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH,
LORA_SPREADING_FACTOR, LORA_CODINGRATE,
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON,
true, 0, 0, LORA_IQ_INVERSION_ON, 3000 );
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR,
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
0, true, 0, 0, LORA_IQ_INVERSION_ON, true );
// Nach Reinit in TX starten, damit Ablauf weiterläuft
state = STATE_TX;
updateActivity();
Serial.println("Radio Reinit: Fertig.");
}
void lora_init(void)
{
Mcu.begin(HELTEC_BOARD,SLOW_CLK_TPYE);
fem_init();
txNumber=0;
Rssi=0;
rxNumber = 0;
RadioEvents.TxDone = OnTxDone;
RadioEvents.TxTimeout = OnTxTimeout;
RadioEvents.RxDone = OnRxDone;
Radio.Init( &RadioEvents );
Radio.SetChannel( RF_FREQUENCY );
Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH,
LORA_SPREADING_FACTOR, LORA_CODINGRATE,
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON,
true, 0, 0, LORA_IQ_INVERSION_ON, 3000 );
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR,
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
0, true, 0, 0, LORA_IQ_INVERSION_ON, true );
state=STATE_TX;
updateActivity();
}
/********************************* lora *********************************************/
SSD1306Wire factory_display(0x3c, 500000, SDA_OLED, SCL_OLED, GEOMETRY_128_64, RST_OLED);
bool resendflag=false;
bool deepsleepflag=false;
bool interrupt_flag = false;
void interrupt_GPIO0()
{
interrupt_flag = true;
}
void interrupt_handle(void)
{
if(interrupt_flag)
{
interrupt_flag = false;
if(digitalRead(0)==0)
{
if(rxNumber <=2)
{
resendflag=true;
}
else
{
deepsleepflag=true;
}
}
}
}
void VextON(void)
{
pinMode(Vext,OUTPUT);
digitalWrite(Vext, LOW);
}
void VextOFF(void) //Vext default OFF
{
pinMode(Vext,OUTPUT);
digitalWrite(Vext, HIGH);
}
void setup()
{
Serial.begin(115200);
VextON();
delay(100);
factory_display.init();
factory_display.clear();
chipid=ESP.getEfuseMac();
Serial.printf("ESP32ChipID=%04X",(uint16_t)(chipid>>32));
Serial.printf("%08X\n",(uint32_t)chipid);
attachInterrupt(0,interrupt_GPIO0,FALLING);
lora_init();
packet ="Warte auf Datenempfang!";
factory_display.drawString(0, 10, packet);
factory_display.display();
delay(100);
factory_display.clear();
pinMode(LED ,OUTPUT);
digitalWrite(LED, LOW);
}
void loop()
{
interrupt_handle();
if(deepsleepflag)
{
VextOFF();
Radio.Sleep();
SPI.end();
pinMode(RADIO_DIO_1,ANALOG);
pinMode(RADIO_NSS,ANALOG);
pinMode(RADIO_RESET,ANALOG);
pinMode(RADIO_BUSY,ANALOG);
pinMode(LORA_CLK,ANALOG);
pinMode(LORA_MISO,ANALOG);
pinMode(LORA_MOSI,ANALOG);
esp_sleep_enable_timer_wakeup(600*1000*(uint64_t)1000);
esp_deep_sleep_start();
}
if(resendflag)
{
state = STATE_TX;
resendflag = false;
}
// Wenn ein Paket empfangen wurde und wir im LOWPOWER sind, Anzeige aktualisieren
if(receiveflag && (state==LOWPOWER) )
{
receiveflag = false;
packet ="Inhalt:";
int i = 0;
while(i < rxSize)
{
packet += rxpacket[i];
i++;
}
packSize = "R_Size: ";
packSize += String(rxSize,DEC);
packSize += " R_rssi: ";
packSize += String(Rssi,DEC);
send_num = "Sende Nummer: ";
send_num += String(txNumber,DEC);
factory_display.drawString(0, 0, show_lora);
factory_display.drawString(0, 10, packet);
factory_display.drawString(0, 20, packSize);
factory_display.drawString(0, 50, send_num);
factory_display.display();
delay(10);
factory_display.clear();
}
// --- Reconnect Check: wenn zu lange keine Aktivität, Radio neu initialisieren ---
if (millis() - lastActivityMillis > RECONNECT_TIMEOUT_MS) {
Serial.println("Keine Aktivitaet erkannt: versuche Radio neu zu initialisieren...");
radio_reinit();
}
switch(state)
{
case STATE_TX:
delay(1000);
txNumber++;
digitalWrite(FEM_RXMODE_PIN, HIGH); // Bypass LNA während TX
sprintf(txpacket,"Ahoi %d,Rssi:%d",txNumber,Rssi);
Serial.printf("\r\nsending packet \"%s\" , length %d\r\n",txpacket, strlen(txpacket));
Radio.Send( (uint8_t *)txpacket, strlen(txpacket) );
state=LOWPOWER;
updateActivity();
break;
case STATE_RX:
Serial.println("into RX mode");
digitalWrite(FEM_RXMODE_PIN, LOW);
Radio.Rx( 0 );
digitalWrite(LED, HIGH);
delay(100);
digitalWrite(LED, LOW);
state=LOWPOWER;
updateActivity();
break;
case LOWPOWER:
Radio.IrqProcess( );
break;
default:
break;
}
}

Code: Alles auswählen
/*
* Version mit grosser Empfangsstärken Anzeige.
* Heltec LoRa V4.31 PingPong Test mit eingeschalteten Booster für 30 dBm Sendeleistung, Version laeuft bei Unterbruchung nach neuen Kontakt weiter.
* c2026.03.30 Ruesseltechnik / Teilweise KI generiert - Achtung!! Nicht ohne Antenne ausfuehren, Zerstoerungsgefahr!!!!
*/
#include "Arduino.h"
#include "LoRaWan_APP.h"
#include <Wire.h>
#include "HT_SSD1306Wire.h"
/********************************* lora *********************************************/
#define RF_FREQUENCY 868000000
#define TX_OUTPUT_POWER 30
#define LORA_BANDWIDTH 0
#define LORA_SPREADING_FACTOR 12
#define LORA_CODINGRATE 1
#define LORA_PREAMBLE_LENGTH 8
#define LORA_SYMBOL_TIMEOUT 0
#define LORA_FIX_LENGTH_PAYLOAD_ON false
#define LORA_IQ_INVERSION_ON false
#define RX_TIMEOUT_VALUE 1000
#define BUFFER_SIZE 30
char txpacket[BUFFER_SIZE];
char rxpacket[BUFFER_SIZE];
/* Heltec V4.3 FEM control pins (KCT8103L) */
#define FEM_VCC_PIN 7
#define FEM_ENABLE_PIN 2
#define FEM_RXMODE_PIN 5
static RadioEvents_t RadioEvents;
void OnTxDone( void );
void OnTxTimeout( void );
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr );
typedef enum
{
LOWPOWER,
STATE_RX,
STATE_TX
} States_t;
int16_t txNumber;
int16_t rxNumber;
States_t state;
bool sleepMode = false;
int16_t Rssi, rxSize;
String rssi = "RSSI --";
String packSize = "--";
String packet;
String send_num;
String show_lora = "Empfange Daten:";
unsigned int counter = 0;
bool receiveflag = false;
long lastSendTime = 0;
int interval = 1000;
uint64_t chipid;
int16_t RssiDetection = 0;
/* --- Neuer Mechanismus: Activity / Reconnect --- */
unsigned long lastActivityMillis = 0;
const unsigned long RECONNECT_TIMEOUT_MS = 10000; // 10s Inaktivität -> Reinit
void updateActivity() {
lastActivityMillis = millis();
}
void OnTxDone( void )
{
Serial.println("TX done......");
digitalWrite(FEM_RXMODE_PIN, LOW); // LNA wieder aktivieren
state = STATE_RX;
updateActivity();
}
void OnTxTimeout( void )
{
Radio.Sleep();
Serial.println("TX Timeout......");
state = STATE_TX;
updateActivity();
}
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )
{
rxNumber++;
Rssi = rssi;
rxSize = size;
memcpy(rxpacket, payload, size);
rxpacket[size] = '\0';
Radio.Sleep();
Serial.printf("\r\nreceived packet \"%s\" with Rssi %d , length %d\r\n", rxpacket, Rssi, rxSize);
Serial.println("Warte um naestes Paket zu senden");
receiveflag = true;
digitalWrite(FEM_RXMODE_PIN, LOW);
state = STATE_TX;
updateActivity();
}
/* FEM initialisieren */
void fem_init() {
pinMode(FEM_VCC_PIN, OUTPUT);
pinMode(FEM_ENABLE_PIN, OUTPUT);
pinMode(FEM_RXMODE_PIN, OUTPUT);
digitalWrite(FEM_VCC_PIN, HIGH);
digitalWrite(FEM_ENABLE_PIN, HIGH);
digitalWrite(FEM_RXMODE_PIN, LOW);
delay(5);
}
/* Radio (Re)Initialisierung in eigene Funktion ausgelagert */
void radio_reinit() {
Serial.println("Radio Reinit: Starte Neuinitialisierung...");
fem_init();
RadioEvents.TxDone = OnTxDone;
RadioEvents.TxTimeout = OnTxTimeout;
RadioEvents.RxDone = OnRxDone;
Radio.Init(&RadioEvents);
Radio.SetChannel(RF_FREQUENCY);
Radio.SetTxConfig(MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH,
LORA_SPREADING_FACTOR, LORA_CODINGRATE,
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON,
true, 0, 0, LORA_IQ_INVERSION_ON, 3000);
Radio.SetRxConfig(MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR,
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
0, true, 0, 0, LORA_IQ_INVERSION_ON, true);
state = STATE_TX;
updateActivity();
Serial.println("Radio Reinit: Fertig.");
}
void lora_init(void)
{
Mcu.begin(HELTEC_BOARD, SLOW_CLK_TPYE);
fem_init();
txNumber = 0;
Rssi = 0;
rxNumber = 0;
RadioEvents.TxDone = OnTxDone;
RadioEvents.TxTimeout = OnTxTimeout;
RadioEvents.RxDone = OnRxDone;
Radio.Init(&RadioEvents);
Radio.SetChannel(RF_FREQUENCY);
Radio.SetTxConfig(MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH,
LORA_SPREADING_FACTOR, LORA_CODINGRATE,
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON,
true, 0, 0, LORA_IQ_INVERSION_ON, 3000);
Radio.SetRxConfig(MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR,
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
0, true, 0, 0, LORA_IQ_INVERSION_ON, true);
state = STATE_TX;
updateActivity();
}
/********************************* lora *********************************************/
SSD1306Wire factory_display(0x3c, 500000, SDA_OLED, SCL_OLED, GEOMETRY_128_64, RST_OLED);
bool resendflag = false;
bool deepsleepflag = false;
bool interrupt_flag = false;
void interrupt_GPIO0()
{
interrupt_flag = true;
}
void interrupt_handle(void)
{
if (interrupt_flag)
{
interrupt_flag = false;
if (digitalRead(0) == 0)
{
if (rxNumber <= 2)
{
resendflag = true;
}
else
{
deepsleepflag = true;
}
}
}
}
void VextON(void)
{
pinMode(Vext, OUTPUT);
digitalWrite(Vext, LOW);
}
void VextOFF(void) //Vext default OFF
{
pinMode(Vext, OUTPUT);
digitalWrite(Vext, HIGH);
}
void setup()
{
Serial.begin(115200);
VextON();
delay(100);
factory_display.init();
factory_display.clear();
chipid = ESP.getEfuseMac();
Serial.printf("ESP32ChipID=%04X", (uint16_t)(chipid >> 32));
Serial.printf("%08X\n", (uint32_t)chipid);
attachInterrupt(0, interrupt_GPIO0, FALLING);
lora_init();
packet = "Warte auf Datenempfang!";
factory_display.setFont(ArialMT_Plain_10);
factory_display.drawString(0, 10, packet);
factory_display.display();
delay(100);
factory_display.clear();
pinMode(LED, OUTPUT);
digitalWrite(LED, LOW);
}
void loop()
{
interrupt_handle();
if (deepsleepflag)
{
VextOFF();
Radio.Sleep();
SPI.end();
pinMode(RADIO_DIO_1, ANALOG);
pinMode(RADIO_NSS, ANALOG);
pinMode(RADIO_RESET, ANALOG);
pinMode(RADIO_BUSY, ANALOG);
pinMode(LORA_CLK, ANALOG);
pinMode(LORA_MISO, ANALOG);
pinMode(LORA_MOSI, ANALOG);
esp_sleep_enable_timer_wakeup(600 * 1000 * (uint64_t)1000);
esp_deep_sleep_start();
}
if (resendflag)
{
state = STATE_TX;
resendflag = false;
}
// Wenn ein Paket empfangen wurde und wir im LOWPOWER sind, Anzeige aktualisieren
if (receiveflag && (state == LOWPOWER))
{
receiveflag = false;
packet = "Inhalt:";
int i = 0;
while (i < rxSize)
{
packet += rxpacket[i];
i++;
}
// packSize wird weiterhin gebildet, aber R_rssi wird nicht in dieser Zeile angezeigt
packSize = "R_Size: ";
packSize += String(rxSize, DEC);
send_num = "Sende Nummer: ";
send_num += String(txNumber, DEC);
// RSSI als eigene Anzeige vorbereiten
String rssiValue = String(Rssi, DEC); // z.B. "-72"
String rssiUnit = "dBm";
String rxLabel = "RX";
// Zeichne normale Texte mit kleiner Schrift (oben)
factory_display.setFont(ArialMT_Plain_10);
// Erste Zeile wieder wie gewünscht: "Empfange Daten:"
factory_display.drawString(0, 0, show_lora);
// Pakettext in Zeile 2: Y etwas tiefer setzen, damit die große Zahl nicht überlappt
factory_display.drawString(0, 12, packet);
// Große RSSI-Anzeige: ArialMT_Plain_24 (größer nicht verfügbar)
// Wir fügen jetzt links vor der großen Zahl das Label "RX" in kleiner Schrift ein.
factory_display.setFont(ArialMT_Plain_24);
int numWidth = factory_display.getStringWidth(rssiValue);
// Einheit in kleiner Schrift messen
factory_display.setFont(ArialMT_Plain_10);
int unitWidth = factory_display.getStringWidth(rssiUnit);
// "RX" in kleiner Schrift messen
int rxLabelWidth = factory_display.getStringWidth(rxLabel);
int spacingLabel = 6; // Abstand zwischen "RX" und großer Zahl
int spacingUnit = 6; // Abstand zwischen großer Zahl und Einheit
int totalWidth = rxLabelWidth + spacingLabel + numWidth + spacingUnit + unitWidth;
int xStart = (128 - totalWidth) / 2;
if (xStart < 0) xStart = 0;
// Y-Position jetzt einige Pixel weiter nach unten verschoben, damit oben weniger Leerraum ist
// und die große Zahl nicht in Zeile 2 hineinragt. Wert leicht erhöht gegenüber vorher (z.B. 26).
int yNumber = 26;
// Zeichne "RX" (kleine Schrift) links von der großen Zahl
factory_display.setFont(ArialMT_Plain_10);
factory_display.drawString(xStart, yNumber + 8, rxLabel);
// Zahl zeichnen (große Schrift) rechts von "RX"
factory_display.setFont(ArialMT_Plain_24);
int xNumber = xStart + rxLabelWidth + spacingLabel;
factory_display.drawString(xNumber, yNumber, rssiValue);
// Einheit rechts neben der Zahl in kleiner Schrift, vertikal leicht versetzt
factory_display.setFont(ArialMT_Plain_10);
factory_display.drawString(xNumber + numWidth + spacingUnit, yNumber + 8, rssiUnit);
// Unter der großen Zahl nur noch Sende-Nummer (keine R_Size / R_rssi)
factory_display.setFont(ArialMT_Plain_10);
factory_display.drawString(0, 54, send_num);
factory_display.display();
delay(10);
factory_display.clear();
}
// --- Reconnect Check: wenn zu lange keine Aktivität, Radio neu initialisieren ---
if (millis() - lastActivityMillis > RECONNECT_TIMEOUT_MS) {
Serial.println("Keine Aktivitaet erkannt: versuche Radio neu zu initialisieren...");
radio_reinit();
}
switch (state)
{
case STATE_TX:
delay(1000);
txNumber++;
digitalWrite(FEM_RXMODE_PIN, HIGH); // Bypass LNA während TX
sprintf(txpacket, "Ahoi %d,Rssi:%d", txNumber, Rssi);
Serial.printf("\r\nsending packet \"%s\" , length %d\r\n", txpacket, strlen(txpacket));
Radio.Send((uint8_t *)txpacket, strlen(txpacket));
state = LOWPOWER;
updateActivity();
break;
case STATE_RX:
Serial.println("into RX mode");
digitalWrite(FEM_RXMODE_PIN, LOW);
Radio.Rx(0);
digitalWrite(LED, HIGH);
delay(100);
digitalWrite(LED, LOW);
state = LOWPOWER;
updateActivity();
break;
case LOWPOWER:
Radio.IrqProcess();
break;
default:
break;
}
}
Ich habe da eine andere Idee zur Umsetzung, 8 Wochen zur Realisierung wäre mir eigentlich zu lang.........Kurzantwort: Ja — technisch ist eine vollständige Bluetooth‑Fernsteuerung der Blackmagic Pocket Cinema Camera 6K mit dem Heltec V4 (ESP32‑S3, BLE-fähig) realisierbar, erfordert aber Reverse‑Engineering der BLE‑GATT‑Services oder Nutzung bestehender Open‑Source‑Implementierungen als Vorlage; mit ESP‑IDF/NimBLE oder Arduino‑BLE, einem kleinen UI/Encoder und Tests mit nRF Connect lässt sich ein stabiles Remote bauen.
Machbarkeit und wichtigste Fakten
• Heltec V4 (ESP32‑S3) hat native Bluetooth Low Energy und ist für solche Projekte geeignet.
• Blackmagic Kameras bieten ein BLE‑Protokoll, das bereits von Desktop‑Apps und kommerziellen Controllern genutzt wird — Open‑Source‑Clients (z. B. CutePocketRemote) zeigen, dass Steuerbefehle per BLE möglich sind.
• Es gibt fertige Apps/Produkte (Bluetooth+ App, HaloRC), die als Referenz für unterstützte Funktionen und Reichweite dienen.
Konkreter Umsetzungsplan (Kurzversion)
Hardware
• Heltec WiFi LoRa 32 V4 (ESP32‑S3) mit USB‑C, Akkuversorgung; optional kleines Touch/Encoder + Gehäuse.
Software / Firmware
• ESP‑IDF (NimBLE) oder Arduino‑BLE als BLE‑Client; implementiere GATT‑Discovery, Pairing und Subscribe auf relevanten Characteristics.
• Nutze CutePocketRemote als Referenz für GATT‑IDs und Befehlsabläufe; analysiere mit nRF Connect oder Wireshark (BLE‑HCI) die Kommunikation beim Smartphone/Desktop‑Client.
Funktionen, die realistisch sind
• Start/Stop Aufnahme, Foto auslösen, ISO/Shutter/Aperture ändern, Weißabgleich, Fokussteuerung (relativ/absolut je nach Objektiv) — bereits in Open‑Source‑Clients implementiert.
Schritt‑für‑Schritt Umsetzung (technisch)
1. Referenz‑Client testen (Desktop/Phone) und mit nRF Connect GATT‑Profile beobachten.
2. Heltec als BLE‑Client programmieren: Scan → Pair → Discover → Subscribe. Verwende NimBLE für stabilen Low‑power‑Betrieb.
3. Mapping der Commands: Buttons/Encoder → GATT‑Writes/Notifications. Implementiere Retry/ACK‑Handling.
4. UI & Power: kleines OLED/Encoder oder Touch; Akku‑Management beachten (deep sleep, Wake on BLE optional).
Risiken, Einschränkungen & Tipps
• Protokoll‑Änderungen durch Blackmagic können Kompatibilität brechen; halte Firmware‑Update‑Pfad offen.
• Pairing/Authentifizierung: Kamera kann Pairing‑Verhalten ändern; teste mit mehreren Firmware‑Ständen.
• Reichweite & Latenz: BLE ist kurzreichweitig (~10–30 m praktisch) und nicht für extrem niedrige Latenz kritischer Fokus‑Motoren gedacht.
Empfehlung & Aufwand
• Schnellstart: Klone CutePocketRemote zur Analyse, baue ein Minimal‑Proof‑of‑Concept auf Heltec (Record/Stop + Status).
• Geschätzter Aufwand: 1–2 Wochen für POC (erfahrener ESP32‑Entwickler), 4–8 Wochen für robustes Produkt mit UI, Gehäuse und Akku‑Management.
Wenn du willst, liefere ich ein konkretes Hardware‑Teile‑Listing, eine minimal funktionale Firmware‑Skizze (NimBLE) und ein Test‑Protokoll zum Nachmessen der GATT‑Services — ich kann das direkt ausarbeiten und Beispielcode bereitstellen.
Code: Alles auswählen
// Ampel-Demo für Heltec V4 (GPIO 19,48,33)
// Rot = 19, Gelb = 48, Grün = 33
const int PIN_RED = 19;
const int PIN_YELLOW = 48;
const int PIN_GREEN = 33;
void setup() {
pinMode(PIN_RED, OUTPUT);
pinMode(PIN_YELLOW, OUTPUT);
pinMode(PIN_GREEN, OUTPUT);
// Alle LEDs aus
digitalWrite(PIN_RED, LOW);
digitalWrite(PIN_YELLOW, LOW);
digitalWrite(PIN_GREEN, LOW);
}
void loop() {
// Rot an, Grün aus
digitalWrite(PIN_RED, HIGH);
digitalWrite(PIN_YELLOW, LOW);
digitalWrite(PIN_GREEN, LOW);
delay(5000); // 5 s
// Rot+Gelb (Übergang)
digitalWrite(PIN_RED, HIGH);
digitalWrite(PIN_YELLOW, HIGH);
digitalWrite(PIN_GREEN, LOW);
delay(2000); // 2 s
// Grün an
digitalWrite(PIN_RED, LOW);
digitalWrite(PIN_YELLOW, LOW);
digitalWrite(PIN_GREEN, HIGH);
delay(5000); // 5 s
// Gelb (Warnung)
digitalWrite(PIN_RED, LOW);
digitalWrite(PIN_YELLOW, HIGH);
digitalWrite(PIN_GREEN, LOW);
delay(2000); // 2 s
}


