diff --git a/EasyLoRaWANGateway/_udpSemtech.ino b/EasyLoRaWANGateway/_udpSemtech.ino deleted file mode 100644 index 7024df5..0000000 --- a/EasyLoRaWANGateway/_udpSemtech.ino +++ /dev/null @@ -1,684 +0,0 @@ -// 1-channel LoRa Gateway for ESP8266 and ESP32 -// Copyright (c) 2016-2020 Maarten Westenberg version for ESP8266 -// -// based on work done by Thomas Telkamp for Raspberry PI 1ch gateway -// and many others. -// -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the MIT License -// which accompanies this distribution, and is available at -// https://opensource.org/licenses/mit-license.php -// -// NO WARRANTY OF ANY KIND IS PROVIDED -// -// Author: Maarten Westenberg (mw12554@hotmail.com) -// -// _udpSemtech.ino: This file contains the UDP specific code enabling to receive -// and transmit packages/messages to the server usig Semtech protocol. -// ======================================================================================== - -// Also referred to as Semtech code - -#if defined(_UDPROUTER) - -// If _UDPROUTER is defined, _TTNROUTER should NOT be defined. So... -#if defined(_TTNROUTER) -#error "Please make sure that either _UDPROUTER or _TTNROUTER are defined but not both" -#endif - -// The following functions ae defined in this module: -// int readUdp(int Packetsize) -// int sendUdp(IPAddress server, int port, uint8_t *msg, uint16_t length) -// bool connectUdp(); -// void pullData(); -// void sendstat(); - -// ---------------------------------------------------------------------------- -// connectUdp() -// connect to UDP (which is a local thing, after all UDP -// connections do not exist. -// Parameters: -// -// Returns -// Boollean indicating success or not -// ---------------------------------------------------------------------------- -bool connectUdp() -{ - - bool ret = false; - unsigned int localPort = _LOCUDPPORT; // To listen to return messages from WiFi -# if _MONITOR>=1 - if (debug>=1) { - mPrint("Local UDP port=" + String(localPort)); - } -# endif //_MONITOR - - if (Udp.begin(localPort) == 1) { -# if _MONITOR>=1 - if (debug>=1) { - mPrint("UDP Connection successful"); - } -# endif //_MONITOR - ret = true; - } - else{ -# if _MONITOR>=1 - if (debug>=0) { - mPrint("Connection failed"); - } -# endif //_MONITOR - } - return(ret); -}// connectUdp - - -// ----------------------------------- DOWN ----------------------------------- -// -// readUdp() -// Read DOWN a package from UDP socket, can come from any server -// Messages are received when server responds to gateway requests from LoRa nodes -// (e.g. JOIN requests etc.) or when server has downstream data. -// We respond only to the server that sents us a message! -// -// Note: So normally we can forget here about codes that do upstream -// -// Parameters: -// Packetsize: size of the buffer to read, as read by loop() calling function -// -// Byte 0: Contains Protocol Version -// Byte 1+2: Contain Token -// Byte 3: Contains PULL_RESP or other identifier -// Byte 4 > Contains payload (or Gateway EUI 8 bytes first) -// -// Returns: -// -1 or false if not read -// Or number of characters read if success -// -// ---------------------------------------------------------------------------- -int readUdp(int packetSize) -{ - uint8_t buff[32]; // General buffer to use for UDP, set to 32 - uint8_t buff_down[RX_BUFF_SIZE]; // Buffer for downstream - - // Make sure we are connected over WiFI - if (WlanConnect(10) < 0) { // MMM 200316 Every call contains yield() -# if _MONITOR>=1 - mPrint("Dwn readUdp:: ERROR connecting to WLAN"); -# endif //_MONITOR - Udp.flush(); - return(-1); - } - - //yield(); // MMM 200320 Clear buffer in kernel (?) - - if (packetSize > RX_BUFF_SIZE) { -# if _MONITOR>=1 - mPrint("Dwn readUdp:: ERROR package of size: " + String(packetSize)); -# endif //_MONITOR - Udp.flush(); - return(-1); - } - - //yield(); // MMM 200406 - - // We assume here that we know the originator of the message. - // In practice however this can be any sender! - if (Udp.read(buff_down, packetSize) < packetSize) { -# if _MONITOR>=1 - mPrint("Dwn readUdp:: Reading less chars"); -# endif //_MONITOR - return(-1); - } - - yield(); // MMM 200406 - - // Remote Address should be known - IPAddress remoteIpNo = Udp.remoteIP(); - - // Remote port is either of the remote TTN server or from NTP server (=123) - - unsigned int remotePortNo = Udp.remotePort(); - if (remotePortNo == 123) { // NTP message arriving, not expected - // This is an NTP message arriving -# if _MONITOR>=1 - if (debug>=0) { - mPrint("Dwn readUdp:: NTP msg rcvd"); - } -# endif //_MONITOR - gwayConfig.ntpErr++; - gwayConfig.ntpErrTime = now(); - Udp.flush(); // MMM 200326 Clear buffer when time response arrives (error) - return(0); - } - - // If it is not NTP it must be a LoRa message for gateway or node - - else { - uint8_t *data = (uint8_t *) ((uint8_t *)buff_down + 4); - //uint8_t protocol= buff_down[0]; - //uint16_t token= buff_down[2]*256 + buff_down[1]; - uint8_t ident= buff_down[3]; - -# if _MONITOR>=1 - if ((debug>=2) && (pdebug & P_TX)) { - mPrint("Dwn readUdp:: message ident="+String(ident)); - } -# endif //_MONITOR - - // now parse the message type from the server (if any) - switch (ident) { - - - // This message is used by the gateway to send sensor data UP to server. - // As this function is used for downstream only, this option - // will never be selected but is included as a reference only - // Para 5.2.1, Semtech Gateway to Server Interface document - case PKT_PUSH_DATA: // 0x00 UP -# if _MONITOR>=1 - if (debug>=1) { - mPrint("Dwn PKT_PUSH_DATA:: size "+String(packetSize)+" From "+String(remoteIpNo.toString())); - } -# endif //_MONITOR - //Udp.flush(); - break; - - - // This message is sent DOWN by the server to acknowledge receipt of a - // (sensor) PKT_PUSH_DATA message sent with the code above. - // Para 5.2.2, Semtech Gateway to Server Interface document - case PKT_PUSH_ACK: // 0x01 DOWN -# if _MONITOR>=1 - if ((debug>=2) && (pdebug & P_TX)) { - mPrint("Dwn PKT_PUSH_ACK:: size="+String(packetSize)+", From "+String(remoteIpNo.toString())); - } -# endif //_MONITOR - //Udp.flush(); - break; - - - // PULL DATA message - // This is a request/UP message and will not be executed by this function. - // We have it here as a description only. - // Para 5.2.3, Semtech Gateway to Server Interface document - case PKT_PULL_DATA: // 0x02 UP -# if _MONITOR>=1 - if ((debug>=1) && (pdebug & P_RX)) { - mPrint("Dwn readUdp:: PKT_PULL_DATA"); - } -# endif //_MONITOR - Udp.flush(); // MMM 200419 Added - break; - - - // PULL_ACK message - // This is the response to PKT_PULL_DATA message - // Para 5.2.4, Semtech Gateway to Server Interface document - // With this ACK, the server confirms the gateway that the route is open - // for further PULL_RESP messages from the server (to the device) - // The server sends a PULL_ACK to confirm PULL_DATA receipt, no response is needed - // - // Byte 0 contains Protocol Version == 2 - // Byte 1-2 Random Token - // Byte 3 PULL_ACK ident == 0x04 - // - case PKT_PULL_ACK: // 0x04 DOWN -# if _MONITOR>=1 - if ((debug>=2) && (pdebug & P_TX)) { - String response="Dwn readUdp PKT_PULL_ACK: micr="; - printInt(micros(), response); - response += ", size="+String(packetSize)+" From "; - printIP(remoteIpNo,'.',response); - response += ", port " +String(remotePortNo); - mPrint(response); - - if (debug>=2) { - Serial.print(F(", data: ")); - for (int i=0; i=1) && (pdebug & P_TX)) { - mPrint("Dwn PKT_PULL_RESP:: sendPacket: micros="+String(micros() )); - } -# endif //_PROFILER - - // Send to the LoRa Node first (timing) and then do reporting to _Monitor - _state=S_TX; - sendTime = micros(); // record when we started sending the message - - // Send the package DOWN to the sensor - // We just read the packet from the Network Server and it is formatted - // as described in the specs. - if (sendPacket(data, packetSize-4) < 0) { -# if _MONITOR>=1 - if (debug>=0) { - mPrint("Dwn readUdp:: ERROR: PKT_PULL_RESP sendPacket failed"); - } -# endif //_MONITOR - Udp.flush(); - return(-1); - } - - - // We need a timeout for this case. In case there does not come an interrupt, - // then there will not be a TXDONE but probably another CDDONE/CDDETD before - // we have a timeout in the main program (Keep Alive) - - loraWait(&LoraDown); - - // Set state to transmit - // Clear interrupt flags and masks - writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); // MMM 200407 Reset - writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset interrupt flags - - // Initiate the transmission of the buffer - // (We normally react on ALL interrupts if we are in TX state) - txLoraModem(&LoraDown); - - // wait extra delay out. The delayMicroseconds timer is accurate until 16383 uSec. -# ifdef _PROFILER - if ((debug>=1) && (pdebug & P_TX)) - { - String response = "Dwn PKT_PULL_RESP:: txLoraModem done: "; - printDwn(&LoraDown, response); - mPrint(response); - } -# endif //_PROFILER - -# if _MONITOR>=1 - if (( debug>=2 ) && ( pdebug & P_TX )) { - uint8_t flags = readRegister(REG_IRQ_FLAGS); - uint8_t mask = readRegister(REG_IRQ_FLAGS_MASK); - uint8_t intr = flags & ( ~ mask ); - - String response="Dwn txLoraModem fini:: "; - mStat(intr, response); - mPrint(response); - - response = "Dwn readUdp:: PKT_PULL_RESP from IP="+String(remoteIpNo.toString()) - +", micros=" + String(micros()) - +", wait="; - if (sendTime < micros()) { - response += String(micros() - sendTime) ; - } - else { - response += "-" + String(sendTime - micros()) ; - }; - mPrint(response); - } -# endif //_MONITOR - - // After filling the buffer we only react on TXDONE interrupt - // So, more or less start at the "case TXDONE:" - _state=S_TXDONE; - _event=1; // Or remove the break below - - // No break!! - - - // This is the response to the PKT_PULL_RESP message by the sensor device - // it is sent by the gateway UP to the server to confirm the PULL_RESP message. - // - case PKT_TX_ACK: // Message id: 0x05 UP - - if (buff_down[0]== 1) { -# if _MONITOR>=1 - if ((debug>=1) && (pdebug & P_TX)) { - mPrint("UP readUdp:: PKT_TX_ACK: protocol version 1"); - data = buff_down + 4; - data[packetSize] = 0; - } -# endif - break; // return - } - -# ifdef _PROFILER - if ((debug>=1) && (pdebug & P_TX)) { - mPrint("UP readUDP:: TX_ACK protocol version 2+"); - } -# endif //_PROFILER - - - // Now respond with an PKT_TX_ACK; UP - // Byte 3 is 0x05; see para 5.2.6 of spec - buff[0]= buff_down[0]; // As read from the Network Server - buff[1]= buff_down[1]; // Token 1 - buff[2]= buff_down[2]; // Token 2 - buff[3]= PKT_TX_ACK; // ident == 0x05; - buff[4]= MAC_array[0]; - buff[5]= MAC_array[1]; - buff[6]= MAC_array[2]; - buff[7]= 0xFF; - buff[8]= 0xFF; - buff[9]= MAC_array[3]; - buff[10]=MAC_array[4]; - buff[11]=MAC_array[5]; - buff[12]=0; // Not error means "\0" - // If it is an error, please look at para 6.1.2 - - yield(); - - // Only send the PKT_PULL_ACK to the UDP socket that just sent the data!!! - Udp.beginPacket(remoteIpNo, remotePortNo); - - if (Udp.write((unsigned char *)buff, 12) != 12) { -# if _MONITOR>=1 - if (debug>=0) { - mPrint("UP readUdp:: ERROR: PKT_PULL_ACK write"); - } -# endif //_MONITOR - } - else { -# if _MONITOR>=1 - if ((debug>=0) && (pdebug & P_TX)) { - mPrint("UP readUdp:: PKT_TX_ACK: micros="+String(micros())); - } -# endif //_MONITOR - } - - if (!Udp.endPacket()) { -# if _MONITOR>=1 - if ((debug>=0) && (pdebug & P_TX)) { - mPrint("UP readUdp:: PKT_PULL_DATALL: ERROR Udp.endPacket"); - } -# endif //_MONITOR - } - - yield(); - - // ONLY NOW WE START TO MONITOR THE PKT_PULL_RESP MESSAGE. -# if _MONITOR>=1 - if ((debug>=1) && (pdebug & P_TX)) { - data = buff_down + 4; - data[packetSize] = 0; - mPrint("Dwn readUdp:: PKT_PULL_RESP: size="+String(packetSize)+", data="+String((char *)data)); - } -# endif //_MONITOR - - break; - - default: -# if _GATEWAYMGT==1 - // For simplicity, we send the first 4 bytes too - gateway_mgt(packetSize, buff_down); - else - -# endif -# if _MONITOR>=1 - mPrint(", ERROR ident not recognized="+String(ident)); -# endif //_MONITOR - break; - } - -# if _MONITOR>=1 - if (debug>=2) { - String response= "Dwn readUdp:: ret="+String(packetSize)+", data="; - for (int i=0; i=1 - if ( pdebug & P_MAIN ) { - mPrint("sendUdp: ERROR not connected to WiFi"); - } -# endif //_MONITOR // MMM 200426 We removed yield() - Udp.flush(); - return(0); - } - - //yield(); // MMM 200327 yield not necessary - - //send the update -# if _MONITOR>=1 - if (( debug>=2 ) && ( pdebug & P_MAIN )) { - mPrint("sendUdp: WlanConnect connected to="+WiFi.SSID()+". Server IP="+ String(WiFi.localIP().toString()) ); - } -# endif //_MONITOR - - if (!Udp.beginPacket(server, (int) port)) { -# if _MONITOR>=1 - if ( debug>=0 ) { - mPrint("M sendUdp:: ERROR Udp.beginPacket"); - } -# endif //_MONITOR - return(0); - } - - yield(); // MMM 200316 May not be necessary - - if (Udp.write((unsigned char *)msg, length) != length) { -# if _MONITOR>=1 - if ( debug>=0 ) { - mPrint("sendUdp:: ERROR Udp write"); - } -# endif //_MONITOR - Udp.endPacket(); // Close UDP - return(0); // Return error - } - - yield(); - - if (!Udp.endPacket()) { -# if _MONITOR>=1 - if (debug>=0) { - mPrint("sendUdp:: ERROR Udp.endPacket"); - } -# endif //_MONITOR - return(0); - } - return(1); -}//sendUDP - - - - -// ---------------------------------------------------------------------------- -// pullData() -// Send UDP periodic Pull_DATA message UP to server to keepalive the connection -// and to invite the server to send downstream messages when these are available -// *2, par. 5.2 -// - Protocol Version (1 byte) -// - Random Token (2 bytes) -// - PULL_DATA identifier (1 byte) = 0x02 -// - Gateway unique identifier (8 bytes) = MAC address -// ---------------------------------------------------------------------------- -void pullData() -{ - uint8_t pullDataReq[12]; // status report as a JSON object - int pullIndex=0; - - uint8_t token_h = (uint8_t)rand(); // random token - uint8_t token_l = (uint8_t)rand(); // random token - - // pre-fill the data buffer with fixed fields - pullDataReq[0] = PROTOCOL_VERSION; // 0x01 - pullDataReq[1] = token_h; - pullDataReq[2] = token_l; - pullDataReq[3] = PKT_PULL_DATA; // 0x02 - // READ MAC ADDRESS OF ESP8266, and return unique Gateway ID consisting of MAC address and 2bytes 0xFF - pullDataReq[4] = MAC_array[0]; - pullDataReq[5] = MAC_array[1]; - pullDataReq[6] = MAC_array[2]; - pullDataReq[7] = 0xFF; - pullDataReq[8] = 0xFF; - pullDataReq[9] = MAC_array[3]; - pullDataReq[10] = MAC_array[4]; - pullDataReq[11] = MAC_array[5]; - //pullDataReq[12] = 0/00; // add string terminator, for safety - - pullIndex = 12; // 12-byte header - - //send the update - - uint8_t *pullPtr; - pullPtr = pullDataReq, -#ifdef _TTNSERVER - sendUdp(ttnServer, _TTNPORT, pullDataReq, pullIndex); - yield(); -#endif - -# if _MONITOR>=1 - if (pullPtr != pullDataReq) { - mPrint("pullPtr != pullDatReq"); - } -# endif //_MONITOR - -#ifdef _THINGSERVER - sendUdp(thingServer, _THINGPORT, pullDataReq, pullIndex); -#endif - -#if _MONITOR>=1 - if ((debug>=2) && (pdebug & P_MAIN)) { - yield(); - mPrint("M PKT_PULL_DATA request, len=" + String(pullIndex) ); - for (int i=0; i -// ---------------------------------------------------------------------------- -void sendstat() -{ - - uint8_t status_report[STATUS_SIZE]; // status report as a JSON object - char stat_timestamp[32]; // - char clat[10]={0}; - char clon[10]={0}; - - int stat_index=0; - uint8_t token_h = (uint8_t)rand(); // random token - uint8_t token_l = (uint8_t)rand(); // random token - - // pre-fill the data buffer with fixed fields - status_report[0] = PROTOCOL_VERSION; // 0x01 - status_report[1] = token_h; - status_report[2] = token_l; - status_report[3] = PKT_PUSH_DATA; // 0x00 - - // READ MAC ADDRESS OF ESP8266, and return unique Gateway ID consisting of MAC address and 2bytes 0xFF - status_report[4] = MAC_array[0]; - status_report[5] = MAC_array[1]; - status_report[6] = MAC_array[2]; - status_report[7] = 0xFF; - status_report[8] = 0xFF; - status_report[9] = MAC_array[3]; - status_report[10] = MAC_array[4]; - status_report[11] = MAC_array[5]; - - stat_index = 12; // 12-byte header - - // XXX Using CET as the current timezone. Change to your timezone - sprintf(stat_timestamp, "%04d-%02d-%02d %02d:%02d:%02d CET", year(),month(),day(),hour(),minute(),second()); - //sprintf(stat_timestamp, "%04d-%02d-%02dT%02d:%02d:%02d.00000000Z", year(),month(),day(),hour(),minute(),second()); - - ftoa(lat,clat,5); // Convert lat to char array with 5 decimals - ftoa(lon,clon,5); // As IDE CANNOT prints floats - - // Build the Status message in JSON format, XXX Split this one up... - yield(); - - int j = snprintf((char *)(status_report + stat_index), STATUS_SIZE-stat_index, - "{\"stat\":{\"time\":\"%s\",\"lati\":%s,\"long\":%s,\"alti\":%i,\"rxnb\":%u,\"rxok\":%u,\"rxfw\":%u,\"ackr\":%u.0,\"dwnb\":%u,\"txnb\":%u,\"pfrm\":\"%s\",\"mail\":\"%s\",\"desc\":\"%s\"}}", - stat_timestamp, clat, clon, (int)alt, statc.msg_ttl, statc.msg_ok, statc.msg_down, 0, 0, 0, platform, email, description); - - yield(); // Give way to the internal housekeeping of the ESP8266 - - stat_index += j; - status_report[stat_index] = 0; // add string terminator, for safety - -# if _MONITOR>=1 - if (( debug>=2 ) && ( pdebug & P_MAIN )) { - mPrint("M stat update: <"+String(stat_index)+"> "+String((char *)(status_report+12)) ); - } -# endif //_MONITOR - - if (stat_index > STATUS_SIZE) { -# if _MONITOR>=1 - mPrint("sendstat:: ERROR buffer too big"); -# endif //_MONITOR - return; - } - - //send the update -# ifdef _TTNSERVER - sendUdp(ttnServer, _TTNPORT, status_report, stat_index); -# endif - -# ifdef _THINGSERVER - yield(); - sendUdp(thingServer, _THINGPORT, status_report, stat_index); -# endif - return; - -} // sendstat() - - -#endif //_UDPROUTER