#include "MQTTDataStreamer.hpp" #include namespace po = boost::program_options; extern "C" { #include "lmic.h" #include "debug.h" } using namespace std; const std::string DFLT_SERVER_ADDRESS{"tcp://localhost:1883"}; const std::string CLIENT_ID{"paho_cpp_async_publish"}; const std::string PERSIST_DIR{"./persist"}; const char *LWT_PAYLOAD = "Last will and testament."; uint8_t QOS = 1; const auto TIMEOUT = std::chrono::seconds(1); po::variables_map vm; ////////////////////////////////////////////////// // CONFIGURATION (FOR APPLICATION CALLBACKS BELOW) ////////////////////////////////////////////////// // application router ID (LSBF) static const u1_t APPEUI[8] = {0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x23, 0x45}; // unique device ID (LSBF) static const u1_t DEVEUI[8] = {0x8D, 0x53, 0x05, 0xD0, 0x7E, 0xD5, 0xB3, 0x70}; // device-specific AES key (derived from device EUI) static const u1_t DEVKEY[16] = {0x44, 0x99, 0xB4, 0xD3, 0xDB, 0x02, 0x07, 0xE3, 0xD6, 0x1A, 0x06, 0x6E, 0xCB, 0x26, 0xFC, 0xA2}; ////////////////////////////////////////////////// // LMIC APPLICATION CALLBACKS ////////////////////////////////////////////////// // provide application router ID (8 bytes, LSBF) void os_getArtEui(u1_t *buf) { memcpy(buf, APPEUI, 8); } // provide device ID (8 bytes, LSBF) void os_getDevEui(u1_t *buf) { memcpy(buf, DEVEUI, 8); } // provide device key (16 bytes) void os_getDevKey(u1_t *buf) { memcpy(buf, DEVKEY, 16); } ////////////////////////////////////////////////// // MQTT APPLICATION CALLBACKS ///////////////////////////////////////////////// void handleTopics(std::shared_ptr streamer_obj, const std::vector> &topics_to_handle, std::mutex *mut) { /* * This function runs in a separate thread. Here you can * write down the logic to be executed when a message is sent over a * subscribed topic. Subscription happens via Callback described in * HelperClasses.hh file in MQTTDataStreamer library. * Messages over subscribed topics are mainly supposed * to act as a trigger. If you want to perform * actions on the sent msg, it is currently only possible in the HelperClasses.hh * This boundary is rather limiting and it require a rewrite of MQTTDataStreamer * library(which is highly needed in my(Nirmal) opinion) */ while (true) { for (const auto &topic : topics_to_handle) { if (topic->name == "LoRa_test/transmitPacket/") { if (topic->message_received) { topic->message_received = false; } } else { std::cout << "\tTopic '" << topic->name << "' not handled\n"; exit(1); } } } } class DataTransmitTopic : public virtual TopicsToHandle { /* * This class is used provided as a means to do something * with the messages sent over subscribed messages. It requires * changing processMessage() to processMessage(const_message_ptr). * This can be theoretically done and boundary between MQTTDataStreamer * and main() can be broken by declaring static variables in this * Translation Unit. */ public: DataTransmitTopic(const std::string &name, uint8_t QoS = 1) : TopicsToHandle(name, QoS) {} void processMessage(mqtt::const_message_ptr msg_) override { message_received = true; std::size_t msg_len = msg_->get_payload().size(); char *msg = new char[msg_len + 1]; std::memcpy(msg, msg_->get_payload().data(), msg_len + 1); std::cout << "sending packet ..." << std::endl; for (int i = 0; i < msg_len; i++) { std::printf("%02hhX", msg[i]); } std::cout << std::endl; for (int i = 0; i < msg_len; i++) { LMIC.frame[i] = msg[i]; } LMIC_setTxData2(1, LMIC.frame, msg_len, 0); // (port 1, 2 bytes, unconfirmed) } }; ////////////////////////////////////////////////// // MAIN - INITIALIZATION AND STARTUP ////////////////////////////////////////////////// // initial job static void initfunc(osjob_t *j) { // reset MAC state LMIC_reset(); // start joining LMIC_startJoining(); // init done - onEvent() callback will be invoked... } // application entry point int main(int argc, char *argv[]) { osjob_t initjob; po::options_description desc("Allowed options"); desc.add_options()("help", "produce help message")("hostname,h", po::value()->default_value("localhost"), "Hostname")("port,p", po::value()->default_value(1883), "Port"); po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); if (vm.count("help")) { std::cout << desc << std::endl; return 0; } std::cout << "Initializing and connecting for server '" << vm["hostname"].as() << "'..." << std::endl; std::vector> topics_to_handle; topics_to_handle.push_back(std::make_shared( "LoRa_test/transmitPacket/")); auto mqtt_async_client = std::make_shared((string) "tcp://" + vm["hostname"].as() + (string) ":" + to_string(vm["port"].as()), CLIENT_ID); auto callback = std::make_shared(mqtt_async_client, topics_to_handle); auto streamer_obj = std::make_shared( std::make_tuple(mqtt_async_client, callback)); std::mutex mut; std::cout << " ...OK" << endl; // initialize runtime env os_init(); // initialize debug library debug_init(); // setup initial job os_setCallback(&initjob, initfunc); // execute scheduled jobs and events os_runloop(); // (not reached) return 0; } ////////////////////////////////////////////////// // UTILITY JOB ////////////////////////////////////////////////// static osjob_t reportjob; // report sensor value every minute static void reportfunc(osjob_t *j) { // read sensor u2_t val = 543; debug_val("val = ", val); // prepare and schedule data for transmission LMIC.frame[0] = val >> 8; LMIC.frame[1] = val; LMIC_setTxData2(1, LMIC.frame, 2, 0); // (port 1, 2 bytes, unconfirmed) // reschedule job in 60 seconds os_setTimedCallback(j, os_getTime() + sec2osticks(60), reportfunc); } ////////////////////////////////////////////////// // LMIC EVENT CALLBACK ////////////////////////////////////////////////// extern "C" { void onEvent(ev_t ev) { debug_event(ev); switch (ev) { // network joined, session established case EV_JOINED: break; // data frame received case EV_RXCOMPLETE: // log frame data debug_buf(LMIC.frame + LMIC.dataBeg, LMIC.dataLen); /* if(LMIC.dataLen == 1) { // set LED state if exactly one byte is received debug_led(LMIC.frame[LMIC.dataBeg] & 0x01); } */ break; // scheduled data sent (optionally data received) case EV_TXCOMPLETE: if (LMIC.dataLen) { // data received in rx slot after tx debug_buf(LMIC.frame + LMIC.dataBeg, LMIC.dataLen); } break; } } }