Optimize the composting process by monitoring the gas evolution in the compost bin. Proper composting reduces the amount of waste going to landfills and creates valuable fertilizer.
How does it work?
The MQ-2 sensor (protected from moisture) is installed in the lid of the compost bin or near the ventilation opening. It reacts to the gases released during the decomposition of organic matter (e.g. methane).
The controller collects data. If the gas concentration becomes too high (which may indicate a lack of oxygen and an improper decomposition process), the system sends a message.
The message can be simple, such as "Time to stir the compost!" or "The compost is too dry, water it!".
Green benefits:
Waste reduction: Encourages and facilitates the recycling of organic waste at home.
Methane Emission Control: Helps maintain an aerobic composting process that releases less methane (a potent greenhouse gas) compared to anaerobic decomposition in landfills.
Circular Economy: Helps produce natural fertilizers, reducing the need to buy chemical fertilizers.
Story (Step-by-Step Guide)Step 0: Gateway: Setup and ConfigurationTo avoid damage to the gateway, make sure to connect the antenna before turning it on!
This step is thoroughly explained in the guide - IoT Education Kit - Setup the Gateway RAK7268V2 - that can be found on https://www.hackster.io/520073/iot-education-kit-setup-the-gateway-rak7268v2-6b222f
Step 1: Mounting the WisBlock Components- Place the microprocessor (RAK11300) in the dedicated slot on the motherboard, aligning the pins and holes correctly.
- Press lightly until you hear a click, then secure it using the screws and screwdriver in the kit.
- Carefully connect the LoRa 863-870MHz antenna to its designated location.
- Assemble the gas sensor module by mounting the MQ-2 sensor on the RAK12004 controller board.
- Connect the gas sensor to IO SLOT B.
The hardware is assembled.
- Install Arduino IDE/PlatformIO and support for the RAK11300 board. Install the necessary libraries for the sensors.
Insert this code:
#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));
}
- Write code to initialize and read data from the MQ2 gas sensor.
function decodeUplink(input) {
var bytes = input.bytes;
// PPM (float32, 4 bytes, little-endian)
var ppmArray = bytes.slice(0, 4);
var ppm = new DataView(new Uint8Array(ppmArray).buffer).getFloat32(0, true);
// PPM percentage (float32, 4 bytes, little-endian)
var percentArray = bytes.slice(4, 8);
var percent = new DataView(new Uint8Array(percentArray).buffer).getFloat32(0, true);
// Optional: round values
ppm = Math.round(ppm * 100) / 100;
percent = Math.round(percent * 1000) / 1000;
return {
data: {
ppm: ppm,
percent: percent
}
};
}
Step 2: Monitor Data in TTN
Comments