diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fb3ebab --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.vscode/* diff --git a/lora-mqtt.h b/lora-mqtt.h new file mode 100644 index 0000000..1fdbee4 --- /dev/null +++ b/lora-mqtt.h @@ -0,0 +1,61 @@ +#ifndef __LORA_MATT_H__ +#define __LORA_MATT_H__ + +#define MAX_STRINGVEC_SIZE 20 +#include "lora-socket.h" + + +class LoRaMQTT{ + +public: + + inline void ini(){ + socket.ini(); + socket.onReceived(_onReceived); + } + + inline void core(){ + socket.core(); + } + + + inline void publish(const String& subject, const String& content){ + String s = subject + "$" + content; + socket.udp(s); + } + + inline void subscribe(const String& subject){ + this->_subjects.PushBack(subject); + } + + inline static void onReceived(void (*f)(String, String)){ + _f = f; + } + + + +private: + static LoRaSocket socket; + static StringVec _subjects; + static void (*_f)(String, String); + + inline static void _onReceived(String msg, String from, String to, String type){ + + String subject = msg.substring(0, msg.indexOf('$')); + String content = msg.substring(msg.indexOf('$') + 1, msg.length()); + + if(_subjects.Find(subject) != -1){ + _f(subject, content); + } + }; + + +}; + + +LoRaSocket LoRaMQTT::socket; +StringVec LoRaMQTT::_subjects; +void (*LoRaMQTT::_f)(String, String); + + +#endif //__LORA_MATT_H__ \ No newline at end of file diff --git a/lora-mqtt.ino b/lora-mqtt.ino new file mode 100644 index 0000000..88efa7c --- /dev/null +++ b/lora-mqtt.ino @@ -0,0 +1,32 @@ + +#define LORA_SOCKET_IP "2.2.2.2" + +#include "lora-mqtt.h" + + +LoRaMQTT mqtt; + +void msg(String subject, String msg){ + Serial.println(subject); + Serial.println(msg); +} + + +void setup(){ + Serial.begin(115200); + if(!LoRa.begin(433E6) ){ + Serial.println("LoRa Failuer"); + while(1); + } + LoRa.receive(); + mqtt.ini(); + mqtt.subscribe("lalala"); + mqtt.onReceived(msg); + //mqtt.publish("lalala", "aaaaa"); +} + + +void loop(){ + + mqtt.core(); +} \ No newline at end of file diff --git a/lora-socket.h b/lora-socket.h new file mode 100644 index 0000000..4b7b644 --- /dev/null +++ b/lora-socket.h @@ -0,0 +1,246 @@ +#ifndef __LORA_SOCKET_H__ +#define __LORA_SOCKET_H__ + +#include +#include "vector.h" +#include "stringVec.h" + +#ifndef LORA_SOCKET_IP +#define LORA_SOCKET_IP "0.0.0.0" +#endif + +#ifndef MAX_RECEIVE_STACK_SIZE +#define MAX_RECEIVE_STACK_SIZE 5 +#endif + +#ifndef MAX_SEND_STACK_SIZE +#define MAX_SEND_STACK_SIZE 5 +#endif + +#ifndef MAX_TCP_TRY_TIMES +#define MAX_TCP_TRY_TIMES 8 +#endif + + +class LoRaSocket { + public: + LoRaSocket(){} + static void core(); + static void ini(); + static void udp(const String& msg, const String& to = "0.0.0.0"); + static void tcp(const String& msg, const String& to); + static void rtcp(const String& msg); + inline static void onReceived(void (*f)(String, String, String, String)){ + _f = f; + }; + + + private: + static StringVec tcp_sendingStack, tcp_receiveStack; + static Vector tcp_sendingTryTimes; + static void(*_f)(String, String, String, String); + inline static void _onReceived(const String& msg, const String& from, const String& to, const String& type){ + (*_f)(msg, from, to, type); + }; + /* LoRa Functions */ + static void LoRa_tx_mode(); + static void LoRa_rx_mode(); + static void send(const String& s); + static const String receiveMsg(); + /* Package Functions */ + inline static const String getIPHeader(const String& to = "0.0.0.0"){ + return to + "|" + LORA_SOCKET_IP + "|"; + }; + inline static bool isGoodPackage(const String& s){ + String body = s.substring(0, s.length() - 1); + if(s.substring(s.length() - 1, s.length()) != hash(body)){ + return false; + } + return true; + }; + inline static const String getToIP(const String& s){ + unsigned short left = s.indexOf('|'); + unsigned short right = s.indexOf('|', left + 1); + return s.substring(left + 1, right); + }; + inline static const String getFromIP(const String& s){ + unsigned short left = s.indexOf('|', s.indexOf('|') + 1); + unsigned short right = s.indexOf('|', left + 1); + return s.substring(left + 1, right); + }; + inline static const String getType(const String& s){ + return s.substring(0, s.indexOf('|')); + }; + inline static const String getContent(const String& s){ + unsigned short left = s.indexOf('|', s.indexOf('|', s.indexOf('|') + 1) + 1); + unsigned short right = s.indexOf('|', left + 1); + return decode(s.substring(left + 1, right)); + }; + inline static const String getTcpKey(const String& s){ + int left = s.indexOf('|', s.indexOf('|', s.indexOf('|', s.indexOf('|') + 1) + 1) + 1); + int right = s.indexOf('|', left + 1); + return s.substring(left + 1, right); + }; + /* receive Functions */ + static void getMsg(const String& msg); + /* tcp stack functions */ + static void checkSendStack(){ + for(unsigned int i = 0; i < tcp_sendingStack.Size(); i ++){ + send("tc"+tcp_sendingStack[i]); + tcp_sendingTryTimes[i] += 1; + + if(tcp_sendingTryTimes[i] >= MAX_TCP_TRY_TIMES){ + tcp_sendingStack.Erase(i); + tcp_sendingTryTimes.Erase(i); + } + } + }; + inline static void receiveStackClassify(){ + if(tcp_receiveStack.Size() > MAX_RECEIVE_STACK_SIZE) tcp_receiveStack.Erase(0); + }; + inline static void sendStackClassify(){ + if(tcp_sendingStack.Size() > MAX_SEND_STACK_SIZE) { + tcp_sendingStack.Erase(0); + tcp_sendingTryTimes.Erase(0); + } + }; + static void removeByKey(const String& key); + /* tools */ + static const String hash(const String& s); + static const String encode(const String& s){ + return s; + }; + static const String decode(const String& s){ + return s; + } + static const String generateRandomKey(){ + String o = ""; + for(unsigned short i = 0; i < 4; i ++){ + o += char(random(26) + 97); + } + return o; + } + + /* timer */ + static void setInterval(void (*function)(void), const int delay){ + static unsigned long startTime = millis(); + + if(millis() - startTime > delay){ + (*function)(); + startTime = millis(); + } + } + + +}; + +void LoRaSocket::getMsg(const String& msg){ + + if(!isGoodPackage(msg)) return; + if(getToIP(msg) != LORA_SOCKET_IP && getToIP(msg) != "0.0.0.0") return; + + if(getType(msg) == "udp") _onReceived(getContent(msg), getFromIP(msg), getToIP(msg), "udp"); + if(getType(msg) == "tcp"){ + rtcp(msg); + if(tcp_receiveStack.Find(msg) != -1) return; + _onReceived(getContent(msg), getFromIP(msg), getToIP(msg), "tcp"); + receiveStackClassify(); + } + if(getType(msg) == "rtcp"){ + removeByKey(getContent(msg)); + } + +} + +void LoRaSocket::udp(const String& msg, const String& to){ + String fin = "udp|"+ getIPHeader(to) + encode(msg) + "|"; + fin += hash(fin); + send(fin); +}; + + +void LoRaSocket::tcp(const String& msg, const String& to){ + String fin = "tcp|" + getIPHeader(to) + encode(msg) + "|" + generateRandomKey() + "|"; + fin += hash(fin); + tcp_sendingStack.PushBack(fin); + tcp_sendingTryTimes.PushBack(0); + sendStackClassify(); + send(fin); +}; + + +void LoRaSocket::rtcp(const String& msg){ + tcp_receiveStack.PushBack(msg); + String fin = "rtcp|" + getIPHeader(getFromIP(msg)) + getTcpKey(msg) + "|"; + fin += hash(fin); + send(fin); +} + +StringVec LoRaSocket::tcp_sendingStack, LoRaSocket::tcp_receiveStack; +Vector LoRaSocket::tcp_sendingTryTimes; +void (*LoRaSocket::_f)(String, String, String, String); + +void LoRaSocket::ini() { + + +} + +void LoRaSocket::core() { + + /* Listen Msg */ + if(LoRa.parsePacket()){ + getMsg(receiveMsg()); + } + + /* check tcp stack */ + setInterval(checkSendStack, 1000); + +} + +void LoRaSocket::LoRa_tx_mode(){ + LoRa.idle(); +} + +void LoRaSocket::LoRa_rx_mode(){ + LoRa.receive(); +} + +void LoRaSocket::send(const String& s){ + LoRa_tx_mode(); + delay(200); + LoRa.beginPacket(); + LoRa.print(s); + LoRa.endPacket(); + delay(200); + LoRa_rx_mode(); +} + + +const String LoRaSocket::receiveMsg(){ + String s = ""; + while (LoRa.available()) { + s += (char)LoRa.read(); + } + return s; +} + +const String LoRaSocket::hash(const String& s){ + unsigned char hashVal = 'k'; + for(unsigned short i = 0; i < s.length(); i ++){ + hashVal ^= s.charAt(i); + } + hashVal = hashVal % 26 + 97; + return String((char)hashVal); +} + + +void LoRaSocket::removeByKey(const String& key){ + for(unsigned int i = 0; i < tcp_sendingStack.Size(); i++){ + if(getTcpKey(tcp_sendingStack[i]) == key) { + tcp_sendingStack.Erase(i); + return; + } + } +} + +#endif //__LORA_SOCKET_H__ \ No newline at end of file diff --git a/stringVec.h b/stringVec.h new file mode 100644 index 0000000..08f4cf2 --- /dev/null +++ b/stringVec.h @@ -0,0 +1,77 @@ +#ifndef __STRINGVEC__ +#define __STRINGVEC__ + + + +#ifndef MAX_STRINGVEC_SIZE +#define MAX_STRINGVEC_SIZE 10 +#endif + +class StringVec{ +public: + StringVec():_size(0){ + this->clear(); + }; + + inline void PushBack(const String& ss){ + if(this->Size() == MAX_STRINGVEC_SIZE){ + this->shift(); + } + this->_s[this->Size()] = ss; + this->_size ++; + } + + const int Find(const String& ss){ + for(unsigned i = 0; i < this->Size(); i ++){ + if(this->_s[i] == ss){ + return i; + } + } + return -1; + } + + void Erase(const int& pos){ + if(pos < 0 || pos >= this->Size()) return; + for(unsigned int i = pos+1; i < this->Size(); i ++){ + this->_s[i-1] = this->_s[i]; + } + this->_size --; + } + + const String shift(){ + String s = this->_s[0]; + if(this->Size() != 0){ + for(unsigned int i = 1; i < this->Size(); i ++){ + this->_s[i-1] = this->_s[i]; + } + this->_size --; + } + return s; + } + + void clear(){ + for(unsigned int i = 0; i < this->Size(); i ++){ + this->_s[i] = ""; + } + } + + inline const int Size(){ + return this->_size; + } + + + String& operator[](int i){ + if(i < 0 || i >= this->Size()) return this->_s[0]; + return this->_s[i]; + } + +private: + String _s[MAX_STRINGVEC_SIZE]; + int _size; + +}; + + + + +#endif //__STRINGVEC__ \ No newline at end of file diff --git a/vector.h b/vector.h new file mode 100644 index 0000000..9f7ecad --- /dev/null +++ b/vector.h @@ -0,0 +1,269 @@ +/* + * Vector.h + * + * Created on: 05/04/2012 + * Author: tom + * Purpose: To play the part of a mutable array in the absence of the STL. + */ + +#ifndef VECTOR_H +#define VECTOR_H + +#include +#include +#include + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +#define SWAP(type, a, b) type tmp ## a = a; a = b; b = tmp ## a; + +template class Predicate +{ +public: + virtual void operator() (ParameterType ¶m) = 0; +}; + +template class Vector +{ + // The address of the first element of the vector + VectorType *begin; + // The address one after the last allocated entry in the underlying array + VectorType *storage; + // The index of the most recent element put in the underlying array - the head + int head; + +public: + // The value that is returned when the caller asks for an element that is out of the bounds of the vector + VectorType OB; + + // We can save a few re-sizings if we know how large the array is likely to grow to be + Vector(int initialSize = 0) + { + begin = new VectorType[initialSize]; //points to the beginning of the new array + head = initialSize - 1; + storage = begin + initialSize; //points to the element one outside of the array (such that end - begin = capacity) + } + + Vector(Vector &obj) + { + begin = new VectorType[0]; // Points to the beginning of the new array, it's zero but this line keeps malloc from seg faulting should we delete begin before resizing it + head = -1; + storage = begin; //points to the element one outside of the array (such that end - begin = capacity) + + *this = obj; + } + + // If there's anything in the vector then delete the array, if there's no array then doing will will cause seg faults + virtual ~Vector() { delete[] begin; } + + Vector &operator=(Vector &obj) + { + // Reallocate the underlying buffer to the same size as the + Resize(obj.Size()); + + for(int i = 0; i < obj.Size(); i++) + (*this)[i] = obj[i]; + + head = obj.head; + + return *this; + } + + void ForEach(Predicate &functor) + { + for(int i = 0; i < Size(); i++) + functor(begin[i]); + } + + // Swaps the underlying array and characteristics of this vector with another of the same type, very quickly + void Swap(Vector &obj) + { + SWAP(int, head, obj.head); + SWAP(VectorType*, begin, obj.begin); + SWAP(VectorType*, storage, obj.storage); + } + + // Checks the entire Vector to see whether a matching item exists. Bear in mind that the VectorType might need to implement + // equality operator (operator==) for this to work properly. + bool Contains(VectorType element) + { + for(int i = 0; i < Size(); i++) + if(operator [](i) == element) + return true; + + return false; + } + + int Find(VectorType element) + { + for(int i = 0; i < Size(); i++) + if(operator [](i) == element) + return i; + + return -1; + } + + void PushBack(VectorType element) { PushBack(&element, 1); } + + void PushBack(const VectorType *elements, int len) + { + // If the length plus this's size is greater than the capacity, reallocate to that size. + if(len + Size() > Capacity()) + ReAllocate(MAX(Size() + len, Size() * 2)); + + int append = MIN(storage - begin - head - 1, len), prepend = len - append; + + // memcpy the data starting at the head all the way up to the last element *(storage - 1) + memcpy((begin + head + 1), elements, sizeof(VectorType) * append); + + // If there's still data to copy memcpy whatever remains, starting at the first element *(begin) until the end of data. The first step will have ensured + // that we don't crash into the tail during this process. + memcpy(begin,(elements + append), sizeof(VectorType) * prepend); + + // Re-recalculate head and size. + head += len; + } + + void Erase(unsigned int position) { Erase(position, position + 1); } + + // Erase an arbitrary section of the vector from first up to last minus one. Like the stl counterpart, this is pretty labour intensive so go easy on it. + void Erase(int first, int last) + { + // For this we'll set the value of the array at first to the value of the array at last plus one. We'll do that all the way up to toIndex + for(int i = 0; i < (Size() - first); i++) + { + // If by trying to fill in the next element with the ones ahead of it we'll be running off the end of the vector, stop. + if((i + last) > (Size() - 1)) + break; + + begin[first + i] = begin[last + i]; + } + + // Adjust the head to reflect the new size + head -= last - first; + } + + // Remove the most recent element in the array + void PopBack() + { + if(Size() > 0) + head--; + } + + // Empty the vector, or to be precise - forget the fact that there was ever anything in there. + void Clear() { head = -1; } + + // Returns a bool indicating whether or not there are any elements in the array + bool Empty() { return head == -1; } + + // Returns the oldest element in the array (the one added before any other) + VectorType const &Back() { return *begin; } + + // Returns the newest element in the array (the one added after every other) + VectorType const &Front() { return begin[head]; } + + // Returns the nth element in the vector + VectorType &operator[](int n) + { + if(n < Size()) + return begin[n]; + else + return OB; + } + + // Returns a pointer such that the vector's data is laid out between ret to ret + size + VectorType *Data() { return begin; } + + // Recreates the vector to hold len elements, all being copies of val + void Assign(int len, const VectorType &val) + { + delete[] begin; + + // Allocate an array the same size as the one passed in + begin = new VectorType[len]; + storage = begin + len; + + // Refresh the head and tail, assuming the array is in order, which it really has to be + head = len - 1; + + for(int i = 0 ; i < Size(); i++) + begin[i] = val; + } + + // Recreates the vector using an external array + void Assign(VectorType *array, int len) + { + delete[] begin; + + // Allocate an array the same size as the one passed in + begin = new VectorType[len]; + storage = begin + len; + + // Refresh the head and tail, assuming the array is in order, which it really has to be + head = len - 1; + + // Copy over the memory + memcpy(begin, array, sizeof(VectorType) * len); + } + + // Returns the number of elements that the vector will support before needing resizing + int Capacity() { return (storage - begin); } + + // Returns the number of elements in vector + int Size() { return head + 1; } + + // Requests that the capacity of the allocated storage space for the elements + // of the vector be at least enough to hold size elements. + void Reserve(unsigned int size) + { + if(size > Capacity()) + ReAllocate(size); + } + + // Resizes the vector + void Resize(unsigned int size) + { + // If necessary, resize the underlying array to fit the new size + if(size > Capacity()) + ReAllocate(size); + + // Now revise the head and size (tail needn't change) to reflect the new size + head = size - 1; + } + +private: + + void ReAllocate(unsigned int size) + { + // Just in case we're re-allocating less room than we had before, make sure that we don't overrun the buffer by trying to write more elements than + // are now possible for this vector to hold. + if(Size() > (int)size) + head = size - 1; + + // Allocate an array twice the size of that of the old + VectorType *_begin = new VectorType[size]; + VectorType *_storage = _begin + size; + + int _head = Size() - 1; + + // Copy across all the old array's data and rearrange it! + for(int i = 0; i < Size(); i++) + _begin[i] = (*this)[i]; + + // Free the old memory + delete[] begin; + + // Redirect the old array to point to the new one + begin = _begin; + storage = _storage; + head = _head; + } +}; + +#endif // VECTOR_H \ No newline at end of file