#include <Wire.h>
#include "ADC121C021.h"
#include <Arduino.h>
#include "LoRaWan-Arduino.h"
// ============================
// LoRaWAN Settings
// ============================
bool doOTAA = true;
#define LORAWAN_APP_PORT 2
#define LORAWAN_APP_INTERVAL 10000 // ms
DeviceClass_t g_CurrentClass = CLASS_A;
LoRaMacRegion_t g_CurrentRegion = LORAMAC_REGION_EU868;
lmh_confirm g_CurrentConfirm = LMH_UNCONFIRMED_MSG;
// OTAA keys (pakeisk savo)
uint8_t nodeDeviceEUI[8] = {0xAC,0x1F,0x09,0xFF,0xFE,0x06,0xB5,0xFB};
uint8_t nodeAppEUI[8] = {0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88};
uint8_t nodeAppKey[16] = {0xA6,0x7D,0x91,0xEA,0xD1,0xB8,0x6B,0x66,0x61,0x91,0xB9,0xDC,0xDD,0x4A,0x53,0xDD};
// ============================
// LoRaWAN Buffers
// ============================
#define LORAWAN_APP_DATA_BUFF_SIZE 16
static uint8_t m_lora_app_data_buffer[LORAWAN_APP_DATA_BUFF_SIZE];
static lmh_app_data_t m_lora_app_data = {m_lora_app_data_buffer, 0, 0, 0, 0};
mbed::Ticker appTimer;
// ============================
// MQ2 Settings
// ============================
#define EN_PIN WB_IO6 // Power pin
ADC121C021 MQ2;
#define MQ2_ADDRESS 0x51
#define MQ2_RL 10.0
#define RatioMQ2CleanAir 1.0
float calcR0;
// ============================
// Forward Declarations
// ============================
static void lorawan_has_joined_handler(void);
static void lorawan_join_failed_handler(void);
static void lorawan_rx_handler(lmh_app_data_t *app_data);
static void lorawan_confirm_class_handler(DeviceClass_t Class);
void tx_lora_periodic_handler(void);
void send_lora_frame(float ppm, float percent);
// LoRa callbacks
static lmh_callback_t g_lora_callbacks = {
BoardGetBatteryLevel,
BoardGetUniqueId,
BoardGetRandomSeed,
lorawan_rx_handler,
lorawan_has_joined_handler,
lorawan_confirm_class_handler,
lorawan_join_failed_handler,
nullptr,
nullptr
};
// ============================
// Helpers
// ============================
void floatToBytes(float value, uint8_t* bytes) {
union {
float f;
uint8_t b[4];
} u;
u.f = value;
for(int i=0;i<4;i++) bytes[i] = u.b[i];
}
// ============================
// Setup
// ============================
void setup() {
Serial.begin(115200);
while(!Serial){}
// Power on MQ2
pinMode(EN_PIN, OUTPUT);
digitalWrite(EN_PIN, HIGH);
delay(300);
// Init MQ2
while(!(MQ2.begin(MQ2_ADDRESS, Wire))){
Serial.println("MQ2 not found! Check wiring.");
delay(200);
}
MQ2.setRL(MQ2_RL);
MQ2.setA(-0.890);
MQ2.setB(1.125);
MQ2.setRegressionMethod(0);
calcR0 = 0;
for(int i = 0; i < 100; i++){
calcR0 += MQ2.calibrateR0(RatioMQ2CleanAir);
}
MQ2.setR0(calcR0/100);
Serial.printf("R0 Value: %3.2f\r\n", MQ2.getR0());
// Init LoRa
lora_rak11300_init();
if(doOTAA){
lmh_setDevEui(nodeDeviceEUI);
lmh_setAppEui(nodeAppEUI);
lmh_setAppKey(nodeAppKey);
}
lmh_param_t g_lora_param_init = {LORAWAN_ADR_ON, DR_0, LORAWAN_PUBLIC_NETWORK, 3, TX_POWER_5, LORAWAN_DUTYCYCLE_OFF};
if(lmh_init(&g_lora_callbacks, g_lora_param_init, doOTAA, g_CurrentClass, g_CurrentRegion) != 0){
Serial.println("lmh_init failed");
return;
}
lmh_join();
}
// ============================
// Loop
// ============================
void loop() {
float sensorPPM = MQ2.readSensor();
float PPMpercentage = sensorPPM / 10000.0;
Serial.printf("MQ2 Gas Sensor PPM: %3.2f\n", sensorPPM);
Serial.printf("PPM Percentage: %3.3f%%\n", PPMpercentage);
// Send via LoRa
send_lora_frame(sensorPPM, PPMpercentage);
delay(LORAWAN_APP_INTERVAL);
}
// ============================
// LoRa Send
// ============================
void send_lora_frame(float ppm, float percent){
if(lmh_join_status_get() != LMH_SET) return;
uint8_t i = 0;
floatToBytes(ppm, &m_lora_app_data.buffer[i]); i += 4;
floatToBytes(percent, &m_lora_app_data.buffer[i]); i += 4;
m_lora_app_data.port = LORAWAN_APP_PORT;
m_lora_app_data.buffsize = i;
if(lmh_send(&m_lora_app_data, g_CurrentConfirm) == LMH_SUCCESS){
Serial.println("MQ2 data sent via LoRaWAN");
} else {
Serial.println("Send failed!");
}
}
// ============================
// LoRa Handlers
// ============================
void lorawan_has_joined_handler(void){
Serial.println("OTAA Network Joined!");
lmh_class_request(g_CurrentClass);
appTimer.attach(tx_lora_periodic_handler, std::chrono::milliseconds(LORAWAN_APP_INTERVAL));
}
void lorawan_join_failed_handler(void){ Serial.println("OTAA join failed!"); }
void lorawan_rx_handler(lmh_app_data_t *app_data){ Serial.printf("RX on port %d, size %d\n", app_data->port, app_data->buffsize); }
void lorawan_confirm_class_handler(DeviceClass_t Class){ Serial.printf("Switched to class %c\n", "ABC"[Class]); }
void tx_lora_periodic_handler(void){
appTimer.attach(tx_lora_periodic_handler, std::chrono::milliseconds(LORAWAN_APP_INTERVAL));
}
Comments