diff --git a/src/Detector.cpp b/src/Detector.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eb6b41e3ca2bd9f2cfe67acbddfbf94fc6d49bb0 --- /dev/null +++ b/src/Detector.cpp @@ -0,0 +1,43 @@ +/**************************************************************************/ +/*! + @file ChangeDetector.cpp + @author anian buehler @ letsgoING.org +*/ +/**************************************************************************/ + +#include "Detector.h" + +EdgeDetector::EdgeDetector() {}; +EdgeDetector::~EdgeDetector() {}; + +int EdgeDetector::edgeDetected(bool currentState) +{ + static bool lastState = false; + int edEdge = 0; + + if (currentState && !lastState) + { + edEdge = RISING; + } + else if (!currentState && lastState) + { + edEdge = FALLING; + } + lastState = currentState; + return edEdge; +} + +ChangeDetector::ChangeDetector() {} +ChangeDetector::~ChangeDetector() {} + +bool ChangeDetector::valueChanged(int value, int threshold) +{ + static int lastValue = 0; + + if (abs(value - lastValue) > threshold) + { + lastValue = value; + return true; + } + return false; +} \ No newline at end of file diff --git a/src/Detector.h b/src/Detector.h new file mode 100644 index 0000000000000000000000000000000000000000..e86e7e3abbce7fc1c7edf7c7365bb9e3ea03f9db --- /dev/null +++ b/src/Detector.h @@ -0,0 +1,29 @@ +/************************************************************************** + @file ChangeDetector.h + @author anian buehler @ letsgoING +**************************************************************************/ + +#ifndef _LGI_DETECTOR_ +#define _LGI_DETECTOR_ + +#include "Arduino.h" + +class EdgeDetector +{ +private: +public: + EdgeDetector(); + ~EdgeDetector(); + int edgeDetected(bool); +}; + +class ChangeDetector +{ +private: +public: + ChangeDetector(); + ~ChangeDetector(); + bool valueChanged(int, int); +}; + +#endif \ No newline at end of file diff --git a/src/DidacticNet.cpp b/src/DidacticNet.cpp index a673d401596a78d7fc380e5a39d36ae94a2deae7..a3cf43c518cf18bb5afb15aaa3a220db7b4df1cb 100644 --- a/src/DidacticNet.cpp +++ b/src/DidacticNet.cpp @@ -3,753 +3,4 @@ @file DidacticNet.cpp @author anian buehler @ letsgoING.org */ -/**************************************************************************/ - -#include "Arduino.h" -#include "DidacticNet.h" - -//************************************************************************** -// ROOT -//************************************************************************** -DidacticPSNet::DidacticPSNet() {} - -DidacticPSNet::~DidacticPSNet() {} - -void DidacticPSNet::begin(Stream &_port) -{ - setStream(_port); -} - -void DidacticPSNet::begin(Stream &_port, PSNET_CALLBACK_SIGNATURE) -{ - setStream(_port); - setCallback(callback); -} - -DidacticPSNet &DidacticPSNet::setCallback(PSNET_CALLBACK_SIGNATURE) -{ - this->callback = callback; - return *this; -} - -void DidacticPSNet::setStream(Stream &stream) -{ - _port = &stream; -} - -bool DidacticPSNet::handleNetwork() -{ - if (checkData()) - { - if (recieveData()) - { - if (getMessageFilter(_readBufferMessage[1])) - { - handleData(); - } - } - _waitingTimeCSMA = millis() + random(CSMA_MIN_DELAY_MS, CSMA_MAX_DELAY_MS); - } - if (_dataToSend && _waitingTimeSend <= millis() && _waitingTimeCSMA <= millis()) - { - // send data to network - unsigned long delayStartTime = micros(); - while (micros() < delayStartTime + CSMA_CHECK_DELAY_US) - ; - if (!checkData()) - { - if (sendData()) - { - _dataToSend = false; - _waitingTimeSend = millis() + _intervalTime; - } - } - } - return !_dataToSend; // return true if sending is done -} - -bool DidacticPSNet::isDataToSend() -{ - return _dataToSend; -} - -bool DidacticPSNet::sendData() -{ - int counter = 0; - bool messageSent = false; - while (!messageSent) - { - if (counter >= MAX_LEN_TOPICS + MAX_LEN_PAYLOAD + LEN_OVERHEAD - 1) - { - _sendBufferMessage[counter] = MSG_DELIMITER; // cut message and stop sending - messageSent = true; - } - else if (_sendBufferMessage[counter] == MSG_DELIMITER) - { - messageSent = true; - } - - _port->write(_sendBufferMessage[counter]); - counter++; - } - return true; -} - -int DidacticPSNet::extractData(int startCounter, int maxLength, char *buffer, char limiter) -{ - int counter = startCounter; - while (_readBufferMessage[counter] != limiter) - { - buffer[counter - startCounter] = _readBufferMessage[counter]; - counter++; - if ((counter - startCounter) > maxLength) - { - counter--; - break; // if > maxLenght -> leave while and return - } - } - buffer[counter - startCounter] = '\0'; - return counter - startCounter; // length -} - -int DidacticPSNet::checkData() -{ - return (int)_port->available(); -} - -bool DidacticPSNet::recieveData() -{ - static int msgCounter = 0; - static int topicCounter = 0; - static int payloadCounter = 0; - - while (checkData()) - { - char localBuffer = _port->read(); - if (localBuffer == MSG_PRELIMITER) - { - msgCounter = 0; - topicCounter = 0; - payloadCounter = 0; - _readBufferMessage[msgCounter] = localBuffer; - } - else if (localBuffer == MSG_DELIMITER && _readBufferMessage[0] == MSG_PRELIMITER) - { - msgCounter++; - _readBufferMessage[msgCounter] = localBuffer; - _readBufferMessage[msgCounter + 1] = '0'; - msgCounter = 0; - return true; - } - else if (localBuffer == MSG_SEPARATOR && _readBufferMessage[0] == MSG_PRELIMITER) - { - topicCounter = msgCounter - 2; - msgCounter++; - _readBufferMessage[msgCounter] = localBuffer; - } - else if (_readBufferMessage[0] == MSG_PRELIMITER && localBuffer != MSG_DELIMITER) - { - if (msgCounter > LEN_OVERHEAD + MAX_LEN_TOPICS + MAX_LEN_PAYLOAD) - { - msgCounter == 0; - _readBufferMessage[0] = '\0'; - } - else - { - msgCounter++; - _readBufferMessage[msgCounter] = localBuffer; - } - } - } - return false; -} - -void DidacticPSNet::setInterval(long intervalTime) -{ - _intervalTime = intervalTime; -} - -//************************************************************************** -// CLIENT -//************************************************************************** -DidacticPSNetClient::~DidacticPSNetClient() {} - -// NEW easy interface for non advanced users -// ########################################## -void DidacticPSNetClient::begin(Stream &_port) // TODO: check if CLientMode is necessary or an unset callback ist not a problem -{ - DidacticPSNet::begin(_port); - _clientMode = CLIENT_MODE_BASIC; -} - -void DidacticPSNetClient::begin(Stream &_port, PSNET_CALLBACK_SIGNATURE) -{ - DidacticPSNet::begin(_port, callback); -} - -bool DidacticPSNetClient::available() -{ - return _newMessageAvailable; -} - -int DidacticPSNetClient::readLatestTopicNr() -{ - return _newMessageTopicNr; -} - -void DidacticPSNetClient::readTopic(int topicNr, char *topic) -{ - strcpy(topic, _topic[topicNr]); -} - -void DidacticPSNetClient::readTopic(char *topic) -{ - readTopic(_newMessageTopicNr, topic); -} - -void DidacticPSNetClient::readPayload(int topicNr, char *payload) -{ - strcpy(payload, _payload[topicNr]); - - if (topicNr == _newMessageTopicNr) - { - _newMessageAvailable = false; - } -} - -void DidacticPSNetClient::readPayload(char *payload) -{ - readPayload(_newMessageTopicNr, payload); -} - -void DidacticPSNetClient::readPayload(char *topic, char *payload) -{ - int topicNr = getTopicNr(topic); - if (topicNr == _newMessageTopicNr) - { - _newMessageAvailable = false; - } - readPayload(topicNr, payload); -} - -bool DidacticPSNetClient::readBooleanPayload() -{ - _newMessageAvailable = false; - return _payload[_newMessageTopicNr][0] == '1'; -} - -int DidacticPSNetClient::readIntegerPayload() -{ - _newMessageAvailable = false; - return atoi(_payload[_newMessageTopicNr]); -} -// ########################################## - -DidacticPSNetClient::DidacticPSNetClient() -{ - _intervalTime = INTERVAL_CLIENT; -} - -int DidacticPSNetClient::publish(char *topic, char *payload) -{ - return publish(topic, strlen(topic), payload, strlen(payload)); -} - -int DidacticPSNetClient::publish(char *topic, int topicLength, char *payload, int payloadLength) -{ - int error = DN_PUBLISH_SUCCESSULL; - - _sendBufferMessage[0] = MSG_PRELIMITER; - _sendBufferMessage[1] = MSG_PUBLISH; - _sendBufferMessage[2 + topicLength] = MSG_SEPARATOR; - _sendBufferMessage[2 + topicLength + 1 + payloadLength] = MSG_DELIMITER; - _sendBufferMessage[2 + topicLength + 1 + payloadLength + 1] = '\0'; - - // TODO: check - if (topicLength > MAX_LEN_TOPICS) - { - topicLength = MAX_LEN_TOPICS; - error += DN_ERROR_TOPIC_LEN; - } - if (payloadLength > MAX_LEN_PAYLOAD) - { - payloadLength = MAX_LEN_PAYLOAD; - error += DN_ERROR_PAYLOAD_LEN; - } - - for (int i = 0; i < topicLength; i++) - { - _sendBufferMessage[2 + i] = topic[i]; - } - for (int i = 0; i < payloadLength; i++) - { - _sendBufferMessage[2 + topicLength + 1 + i] = payload[i]; - } - - _dataToSend = true; - return error; -} - -int DidacticPSNetClient::publish(char *topic, int data) -{ - char sendPayload[MAX_LEN_PAYLOAD]; - itoa(data, sendPayload, 10); - - return publish(topic, sendPayload); -} - -int DidacticPSNetClient::publish(char *topic, bool data) -{ - char sendPayload[2]; - itoa(data, sendPayload, 10); - - return publish(topic, sendPayload); -} - -int DidacticPSNetClient::publishOnChange(char *topic, bool input) -{ - if (!_dataToSend) - { - if (eDetector.edgeDetected(input)) - { - return publish(topic, input); - } - } - return DN_ERROR_NO_ERROR; -} - -int DidacticPSNetClient::publishOnChange(char *topic, int input, int threshold) -{ - if (!_dataToSend) - { - if (cDetector.valueChanged(input, threshold)) - { - return publish(topic, input); - } - } - return DN_ERROR_NO_ERROR; -} - -int DidacticPSNetClient::subscribe(char *topic) -{ - return subscribe(topic, strlen(topic)); -} - -int DidacticPSNetClient::subscribe(char *topic, int topicLength) -{ - int error = DN_ERROR_NO_ERROR; - - if (topicLength > MAX_LEN_TOPICS) - { - topicLength = MAX_LEN_TOPICS; - error = DN_ERROR_TOPIC_LEN; - } - - _sendBufferMessage[0] = MSG_PRELIMITER; - _sendBufferMessage[1] = MSG_SUBSCRIBE; - _sendBufferMessage[2 + topicLength] = MSG_DELIMITER; - - int topicNumber = getTopicNr(topic); - - if (topicNumber < 0) - { - topicNumber = getFreeTopicNr(); - if (topicNumber < 0) - { - topicNumber = 0; - } - for (int i = 0; i < topicLength; i++) - { - _topic[topicNumber][i] = topic[i]; - _sendBufferMessage[2 + i] = topic[i]; - } - _topic[topicNumber][topicLength] = '\0'; - _sendBufferMessage[2 + topicLength + 1] = '\0'; - _dataToSend = true; - } - else - { - for (int i = 0; i < topicLength; i++) - { - _sendBufferMessage[2 + i] = topic[i]; - } - _sendBufferMessage[2 + topicLength + 1] = '\0'; - _dataToSend = true; - } - while (_dataToSend) - { - handleNetwork(); - } - return error; -} - -bool DidacticPSNetClient::unsubscribe(char *topic) -{ - return unsubscribe(topic, strlen(topic)); -} - -bool DidacticPSNetClient::unsubscribe(char *topic, int topicLength) -{ - int topicNumber = getTopicNr(topic); - if (topicNumber >= 0) - { - _topic[topicNumber][0] = '\0'; - return true; - } - return false; -} - -bool DidacticPSNetClient::getMessageFilter(char messageType) -{ - return messageType == MSG_UPDATE; -} - -bool DidacticPSNetClient::savePayload(char *buffer, int position) -{ - strcpy(_payload[position], buffer); - return true; -} - -bool DidacticPSNetClient::handleData() -{ - int currentTopicNr = 0; - int topicLength = 0; - int payloadLength = 0; - topicLength = extractData(2, MAX_LEN_TOPICS, _bufferTopic, MSG_SEPARATOR); - if (topicLength > 0) - { - currentTopicNr = getTopicNr(_bufferTopic); - payloadLength = extractData(topicLength + 3, MAX_LEN_PAYLOAD, _bufferPayload, MSG_DELIMITER); - if (currentTopicNr >= 0) - { - savePayload(_bufferPayload, currentTopicNr); - _newMessageAvailable = true; - _newMessageTopicNr = currentTopicNr; - - if (_clientMode == CLIENT_MODE_ADVANCED) - { -#ifdef CALLBACK_W_LENGTH - callback(_bufferTopic, topicLength, _bufferPayload, payloadLength); -#else - callback(_bufferTopic, _bufferPayload); -#endif - } - } - } - return true; -} - -int DidacticPSNetClient::getTopicNr(char *topic) -{ - for (int i = 0; i < MAX_NR_TOPICS_CLIENT; i++) - { - if (strcmp(_topic[i], topic) == 0 || _topic[i][0] == MSG_TOPIC_MULTI) - { - return i; - } - } - return -1; -} - -int DidacticPSNetClient::getFreeTopicNr() -{ - for (int i = 0; i < MAX_NR_TOPICS_CLIENT; i++) - { - if (strcmp(_topic[i], "") == 0) - { - return i; - } - } - return -1; -} - -int DidacticPSNetClient::getSubscribedTopic(char *topic, int number) -{ - if (number > 0 && number < getMaxNrTopics()) - { - strcpy(topic, _topic[number]); - return DN_ERROR_NO_ERROR; - } - return DN_ERROR_NO_TOPIC; -} - -int DidacticPSNetClient::getMaxNrTopics() -{ - return getFreeTopicNr(); -} - -//************************************************************************** -// Broker -//************************************************************************** -DidacticPSNetBroker::DidacticPSNetBroker() -{ - _intervalTime = INTERVAL_BROKER; -} - -DidacticPSNetBroker::~DidacticPSNetBroker() {} - -bool DidacticPSNetBroker::getMessageFilter(char messageType) -{ - return (messageType == MSG_PUBLISH || messageType == MSG_SUBSCRIBE); -} - -bool DidacticPSNetBroker::savePayload(char *buffer, int position) -{ - strcpy(_data[position], buffer); - return true; -} - -void DidacticPSNetBroker::writeDataToTopic(int topicNumber, char *usedTopic, char *newData) -{ - if (strcmp(_topic[topicNumber], "") == 0) - { - strcpy(_topic[topicNumber], usedTopic); - } - strcpy(_data[topicNumber], newData); -} - -bool DidacticPSNetBroker::handleData() -{ - int currentTopicNr = 0; - int topicLength = 0; - int dataLength = 0; - - printVerbose(_readBufferMessage); - - if (_readBufferMessage[1] == MSG_PUBLISH) - { - topicLength = extractData(2, MAX_LEN_TOPICS, _bufferTopic, MSG_SEPARATOR); - if (topicLength > 0) - { - currentTopicNr = getTopicNr(_bufferTopic); - dataLength = extractData(topicLength + 3, MAX_LEN_PAYLOAD, _bufferPayload, MSG_DELIMITER); - if (currentTopicNr >= 0) - { - writeDataToTopic(currentTopicNr, _bufferTopic, _bufferPayload); - update(_topic[currentTopicNr], topicLength, _data[currentTopicNr], dataLength); - } - } - } - else if (_readBufferMessage[1] == MSG_SUBSCRIBE) - { - topicLength = extractData(2, MAX_LEN_TOPICS, _bufferTopic, MSG_DELIMITER); - if (topicLength > 0) - { - currentTopicNr = getTopicNr(_bufferTopic); - if (currentTopicNr >= 0) - { - update(_topic[currentTopicNr], strlen(_topic[currentTopicNr]), _data[currentTopicNr], strlen(_data[currentTopicNr])); - } - } - } - return true; -} - -bool DidacticPSNetBroker::update(char *topic, int topicLength, char *data, int dataLength) -{ - _sendBufferMessage[0] = MSG_PRELIMITER; - _sendBufferMessage[1] = MSG_UPDATE; - _sendBufferMessage[2 + topicLength] = MSG_SEPARATOR; - _sendBufferMessage[2 + topicLength + 1 + dataLength] = MSG_DELIMITER; - _sendBufferMessage[2 + topicLength + 1 + dataLength + 1] = '\0'; - - if (topicLength <= MAX_LEN_TOPICS) - { - for (int i = 0; i < topicLength; i++) - { - _sendBufferMessage[2 + i] = topic[i]; - } - } - else - { - _dataToSend = false; - return false; - } - if (dataLength <= MAX_LEN_PAYLOAD) - { - for (int i = 0; i < dataLength; i++) - { - _sendBufferMessage[2 + topicLength + 1 + i] = data[i]; - } - } - else - { - _dataToSend = false; - return false; - } - _dataToSend = true; - return true; -} - -int DidacticPSNetBroker::getTopicNr(char *topic) -{ - for (int i = 0; i < MAX_NR_TOPICS_BROKER; i++) - { - if (strcmp(_topic[i], topic) == 0) - { - return i; - } - } - return getFreeTopicNr(); -} - -int DidacticPSNetBroker::getFreeTopicNr() -{ - for (int i = 0; i < MAX_NR_TOPICS_BROKER; i++) - { - if (strcmp(_topic[i], "") == 0) - { - return i; - } - } - return -1; -} - -void DidacticPSNetBroker::setVerbose(Stream &verbosePort) -{ - _isVerbose = true; - _verbosePort = &verbosePort; -} - -void DidacticPSNetBroker::setNoneVerbose() -{ - _isVerbose = false; -} - -bool DidacticPSNetBroker::printVerbose(char *recievedData) -{ - char signBuffer = 0; - int signCounter = 0; - - if (_isVerbose) - { - while (signBuffer != MSG_DELIMITER) - { - signBuffer = recievedData[signCounter]; - _verbosePort->write(signBuffer); - signCounter++; - } - _verbosePort->write('\n'); - } - return _isVerbose; -} - -//************************************************************************** -// LITTLE HELPERS FOR CLIENTS ;-) -//************************************************************************* - -EdgeDetector::EdgeDetector(){}; -EdgeDetector::~EdgeDetector(){}; - -int EdgeDetector::edgeDetected(bool currentState) -{ - static bool lastState = false; - int edEdge = 0; - - if (currentState && !lastState) - { - edEdge = RISING; - } - else if (!currentState && lastState) - { - edEdge = FALLING; - } - lastState = currentState; - return edEdge; -} - -ChangeDetector::ChangeDetector() {} -ChangeDetector::~ChangeDetector() {} - -bool ChangeDetector::valueChanged(int value, int threshold) -{ - static int lastValue = 0; - - if (abs(value - lastValue) > threshold) - { - lastValue = value; - return true; - } - return false; -} - -UnblockingTimer::UnblockingTimer() {} -UnblockingTimer::~UnblockingTimer() {} - -bool UnblockingTimer::timeElapsed(long delayTime) -{ - long currentTime = millis(); - - if (lastTime + (delayTime - 1) < currentTime) - { - lastTime = currentTime; - return true; - } - return false; -} - -SerialReader::SerialReader() {} -SerialReader::~SerialReader() {} - -void SerialReader::begin(Stream &rsStream) -{ - _port = &rsStream; -} - -int SerialReader::readSerialData(char *rsDataArray, char rsEndSign) -{ - - if (_port->available()) - { - char charBuffer = _port->read(); - rsDataArray[charCounter] = charBuffer; - - if (charBuffer == rsEndSign) - { - rsDataArray[charCounter] = '\0'; - int nrOfChars = charCounter; - charCounter = 0; - return nrOfChars; - } - else - { - charCounter++; - } - } - return 0; -} - -serialMonitorUI::serialMonitorUI() {} -serialMonitorUI::~serialMonitorUI() {} - -char serialMonitorUI::extractCommand(char *userInput) -{ - if ((userInput[0] >= 33 && userInput[0] <= 47) || ((userInput[0] >= 58 && userInput[0] <= 64))) // ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ - { - char command = userInput[0]; - strcpy(userInput, &userInput[1]); - return command; - } - return DN_ASCII_EOS; -} - -bool serialMonitorUI::available() -{ - if (SerialReader::readSerialData(_currentInput, DN_ASCII_CR) > 0) - { - _currentCommand = extractCommand(_currentInput); - return true; - } - - return false; -} - -char serialMonitorUI::readCommand() -{ - return _currentCommand; -} - -void serialMonitorUI::readUserInput(char *inputData) -{ - strcpy(inputData, _currentInput); -} \ No newline at end of file +/**************************************************************************/ \ No newline at end of file diff --git a/src/DidacticNet.h b/src/DidacticNet.h index 38643b451cf2a29f8ef6c8d633e54f54bd23c501..2b6458a53f6d91568b324ab0662dc13058fe6cd1 100644 --- a/src/DidacticNet.h +++ b/src/DidacticNet.h @@ -8,242 +8,13 @@ #include "Arduino.h" -#ifdef CALLBACK_W_LENGTH -// callback(topic, topicLength, payload, payloadLength) -#define PSNET_CALLBACK_SIGNATURE void (*callback)(char *, int, char *, int) -#else -// callback(topic, payload) -#define PSNET_CALLBACK_SIGNATURE void (*callback)(char *, char *) -#endif - -#define CLIENT_MODE_BASIC false -#define CLIENT_MODE_ADVANCED true - -#define MSG_PRELIMITER '<' -#define MSG_DELIMITER '>' -#define MSG_SEPARATOR '|' - -//@ publish → on publish check topic, then send topic-update -//? subscribe → subscribe starts update, topic filter @client -// # update → update to specific topic Broker to client -#define MSG_PUBLISH '@' -#define MSG_SUBSCRIBE '?' -#define MSG_UPDATE '#' -#define MSG_TOPIC_MULTI '*' +#define DN_MSG_PRELIMITER '<' +#define DN_MSG_DELIMITER '>' +#define DN_MSG_SEPARATOR '|' //<@topic|payload> -#define LEN_OVERHEAD 4 - -#define CSMA_CHECK_DELAY_US 400 -#define CSMA_MIN_DELAY_MS 10 -#define CSMA_MID_DELAY_MS 20 -#define CSMA_MAX_DELAY_MS 30 - -#define INTERVAL_CLIENT 500L -#define INTERVAL_BROKER 0L - -#define MAX_NR_TOPICS_CLIENT 10 -#define MAX_NR_TOPICS_BROKER 20 -#define MAX_LEN_TOPICS 10 -#define MAX_LEN_PAYLOAD 20 -#define MAX_LEN_USERINPUT 41 - -#define DN_PUBLISH_SUCCESSULL 1 -#define DN_ERROR_NO_ERROR 0 -#define DN_ERROR_TOPIC_LEN -1 -#define DN_ERROR_PAYLOAD_LEN -2 -#define DN_ERROR_NO_TOPIC -3 - -// little helpers -#define DN_ASCII_EOS 0 -#define DN_ASCII_CR 13 -#define DN_ASCII_NL 10 -#define DN_ASCII_DEL 127 - -class EdgeDetector -{ -private: -public: - EdgeDetector(); - ~EdgeDetector(); - int edgeDetected(bool); -}; - -class ChangeDetector -{ -private: -public: - ChangeDetector(); - ~ChangeDetector(); - bool valueChanged(int, int); -}; - -class UnblockingTimer -{ -private: - long lastTime = 0L; - -public: - UnblockingTimer(); - ~UnblockingTimer(); - bool timeElapsed(long); -}; -class SerialReader -{ -protected: - Stream *_port; - int charCounter = 0; - -public: - SerialReader(); - ~SerialReader(); - void begin(Stream &); - int readSerialData(char *, char); -}; - -class serialMonitorUI : public SerialReader -{ -protected: - char _currentCommand = '\0'; - char _currentInput[MAX_LEN_USERINPUT + 1] = {'\0'}; - - char extractCommand(char *); - -public: - serialMonitorUI(); - ~serialMonitorUI(); - bool available(); - char readCommand(); - void readUserInput(char *); -}; - -class DidacticPSNet -{ -protected: - Stream *_port; - - PSNET_CALLBACK_SIGNATURE; - - char _bufferTopic[MAX_LEN_TOPICS + 1] = {0}; - char _bufferPayload[MAX_LEN_PAYLOAD + 1] = {0}; - char _readBufferMessage[MAX_LEN_TOPICS + MAX_LEN_PAYLOAD + LEN_OVERHEAD + 1]; - char _sendBufferMessage[MAX_LEN_TOPICS + MAX_LEN_PAYLOAD + LEN_OVERHEAD + 1]; - - bool _dataToSend = false; // int Data to send for queue? - unsigned long _waitingTimeSend = 0L; - unsigned long _waitingTimeCSMA = 0L; - unsigned long _intervalTime = 0L; - int _currentTopicLength = 0; - int _currentPayloadLength = 0; - - DidacticPSNet &setCallback(PSNET_CALLBACK_SIGNATURE); - - void setStream(Stream &_port); - - int checkData(); - bool recieveData(); - bool sendData(); - int extractData(int, int, char *, char); - void writeDataToTopic(char *, char *); - virtual int getTopicNr(char *) = 0; - virtual int getFreeTopicNr() = 0; - virtual bool getMessageFilter(char) = 0; - virtual bool savePayload(char *, int) = 0; - virtual bool handleData() = 0; - -public: - DidacticPSNet(); - ~DidacticPSNet(); - - void begin(Stream &_port); - void begin(Stream &_port, PSNET_CALLBACK_SIGNATURE); - - bool handleNetwork(); - bool isDataToSend(); - - void setInterval(long); -}; - -class DidacticPSNetClient : public DidacticPSNet -{ -private: - EdgeDetector eDetector; - ChangeDetector cDetector; - - char _topic[MAX_NR_TOPICS_CLIENT][MAX_LEN_TOPICS + 1] = {{0}}; - char _payload[MAX_NR_TOPICS_CLIENT][MAX_LEN_PAYLOAD + 1] = {{0}}; - - bool _clientMode = CLIENT_MODE_ADVANCED; - bool _newMessageAvailable = false; - int _newMessageTopicNr = 0; - char _currentTopic[MAX_LEN_TOPICS + 1] = {0}; - char _currentPayload[MAX_LEN_PAYLOAD + 1] = {0}; - - bool savePayload(char *, int); - bool getMessageFilter(char); - bool handleData(); - int getTopicNr(char *); - int getFreeTopicNr(); - - int edgeDetected(bool); - bool valueChanged(int, int); - -public: - DidacticPSNetClient(); - ~DidacticPSNetClient(); - - void begin(Stream &_port); - void begin(Stream &_port, PSNET_CALLBACK_SIGNATURE); - - bool available(); - int readLatestTopicNr(); - void readTopic(char *); - void readTopic(int, char *); - void readPayload(char *); - void readPayload(int, char *); - void readPayload(char *, char *); - bool readBooleanPayload(); - int readIntegerPayload(); - - int getMaxNrTopics(); - int getSubscribedTopic(char *, int); - - int publish(char *, char *); - int publish(char *, int, char *, int); - int publish(char *, int); - int publish(char *, bool); - int publishOnChange(char *, bool); - int publishOnChange(char *, int, int); - int subscribe(char *); - int subscribe(char *, int); - bool unsubscribe(char *); - bool unsubscribe(char *, int); -}; - -class DidacticPSNetBroker : public DidacticPSNet -{ -private: - Stream *_verbosePort; - char _topic[MAX_NR_TOPICS_BROKER][MAX_LEN_TOPICS + 1] = {{0}}; - char _data[MAX_NR_TOPICS_BROKER][MAX_LEN_PAYLOAD + 1] = {{0}}; - - bool _isVerbose = false; - - bool savePayload(char *, int); - bool getMessageFilter(char); - void writeDataToTopic(int, char *, char *); - bool handleData(); - int getTopicNr(char *); - int getFreeTopicNr(); - bool update(char *, int, char *, int); - bool printVerbose(char *); - -public: - DidacticPSNetBroker(); - ~DidacticPSNetBroker(); - - void setVerbose(Stream &_port); - void setNoneVerbose(); -}; +#define DN_LEN_OVERHEAD 4 +#define DN_MAX_LEN_TELEGRAM 40 -#endif +#include "DidacticNetTransmit.h" +#include "DidacticNetPSN.h" diff --git a/src/DidacticNetPSN.cpp b/src/DidacticNetPSN.cpp new file mode 100644 index 0000000000000000000000000000000000000000..64c1f398ae4005f73079607e214458794ccf6b18 --- /dev/null +++ b/src/DidacticNetPSN.cpp @@ -0,0 +1,473 @@ +/**************************************************************************/ +/*! + @file DidacticNetPSN.cpp + @author anian buehler @ letsgoING.org +*/ +/**************************************************************************/ + +#include "Arduino.h" + +//************************************************************************** +// CLIENT +//************************************************************************** +DidacticNetPSNClient::~DidacticNetPSNClient() {} + +// NEW easy interface for non advanced users +// ########################################## +void DidacticNetPSNClient::begin(Stream &_port) // TODO: check if CLientMode is necessary or an unset callback ist not a problem +{ + DidacticNetTransmit::begin(_port); + _clientMode = CLIENT_MODE_BASIC; +} + +void DidacticNetPSNClient::begin(Stream &_port, PSNET_CALLBACK_SIGNATURE) +{ + DidacticNetTransmit::begin(_port, callback); +} + +bool DidacticNetPSNClient::available() +{ + return _newMessageAvailable; +} + +int DidacticNetPSNClient::readLatestTopicNr() +{ + return _newMessageTopicNr; +} + +void DidacticNetPSNClient::readTopic(int topicNr, char *topic) +{ + strcpy(topic, _topic[topicNr]); +} + +void DidacticNetPSNClient::readTopic(char *topic) +{ + readTopic(_newMessageTopicNr, topic); +} + +void DidacticNetPSNClient::readPayload(int topicNr, char *payload) +{ + strcpy(payload, _payload[topicNr]); + + if (topicNr == _newMessageTopicNr) + { + _newMessageAvailable = false; + } +} + +void DidacticNetPSNClient::readPayload(char *payload) +{ + readPayload(_newMessageTopicNr, payload); +} + +void DidacticNetPSNClient::readPayload(char *topic, char *payload) +{ + int topicNr = getTopicNr(topic); + if (topicNr == _newMessageTopicNr) + { + _newMessageAvailable = false; + } + readPayload(topicNr, payload); +} + +bool DidacticNetPSNClient::readBooleanPayload() +{ + _newMessageAvailable = false; + return _payload[_newMessageTopicNr][0] == '1'; +} + +int DidacticNetPSNClient::readIntegerPayload() +{ + _newMessageAvailable = false; + return atoi(_payload[_newMessageTopicNr]); +} +// ########################################## + +DidacticNetPSNClient::DidacticNetPSNClient() +{ + _intervalTime = PS_INTERVAL_CLIENT; +} + +int DidacticNetPSNClient::publish(char *topic, char *payload) +{ + return publish(topic, strlen(topic), payload, strlen(payload)); +} + +int DidacticNetPSNClient::publish(char *topic, int topicLength, char *payload, int payloadLength) +{ + int error = DN_PUBLISH_SUCCESSULL; + + _sendBufferMessage[0] = DN_MSG_PRELIMITER; + _sendBufferMessage[1] = PS_MSG_PUBLISH; + _sendBufferMessage[2 + topicLength] = DN_MSG_SEPARATOR; + _sendBufferMessage[2 + topicLength + 1 + payloadLength] = DN_MSG_DELIMITER; + _sendBufferMessage[2 + topicLength + 1 + payloadLength + 1] = '\0'; + + // TODO: check + if (topicLength > MAX_LEN_TOPICS) + { + topicLength = MAX_LEN_TOPICS; + error += PS_ERROR_TOPIC_LEN; + } + if (payloadLength > MAX_LEN_PAYLOAD) + { + payloadLength = MAX_LEN_PAYLOAD; + error += PS_ERROR_PAYLOAD_LEN; + } + + for (int i = 0; i < topicLength; i++) + { + _sendBufferMessage[2 + i] = topic[i]; + } + for (int i = 0; i < payloadLength; i++) + { + _sendBufferMessage[2 + topicLength + 1 + i] = payload[i]; + } + + _dataToSend = true; + return error; +} + +int DidacticNetPSNClient::publish(char *topic, int data) +{ + char sendPayload[MAX_LEN_PAYLOAD]; + itoa(data, sendPayload, 10); + + return publish(topic, sendPayload); +} + +int DidacticNetPSNClient::publish(char *topic, bool data) +{ + char sendPayload[2]; + itoa(data, sendPayload, 10); + + return publish(topic, sendPayload); +} + +int DidacticNetPSNClient::publishOnChange(char *topic, bool input) +{ + if (!_dataToSend) + { + if (eDetector.edgeDetected(input)) + { + return publish(topic, input); + } + } + return PS_ERROR_NO_ERROR; +} + +int DidacticNetPSNClient::publishOnChange(char *topic, int input, int threshold) +{ + if (!_dataToSend) + { + if (cDetector.valueChanged(input, threshold)) + { + return publish(topic, input); + } + } + return PS_ERROR_NO_ERROR; +} + +int DidacticNetPSNClient::subscribe(char *topic) +{ + return subscribe(topic, strlen(topic)); +} + +int DidacticNetPSNClient::subscribe(char *topic, int topicLength) +{ + int error = PS_ERROR_NO_ERROR; + + if (topicLength > MAX_LEN_TOPICS) + { + topicLength = MAX_LEN_TOPICS; + error = PS_ERROR_TOPIC_LEN; + } + + _sendBufferMessage[0] = DN_MSG_PRELIMITER; + _sendBufferMessage[1] = PS_MSG_SUBSCRIBE; + _sendBufferMessage[2 + topicLength] = DN_MSG_DELIMITER; + + int topicNumber = getTopicNr(topic); + + if (topicNumber < 0) + { + topicNumber = getFreeTopicNr(); + if (topicNumber < 0) + { + topicNumber = 0; + } + for (int i = 0; i < topicLength; i++) + { + _topic[topicNumber][i] = topic[i]; + _sendBufferMessage[2 + i] = topic[i]; + } + _topic[topicNumber][topicLength] = '\0'; + _sendBufferMessage[2 + topicLength + 1] = '\0'; + _dataToSend = true; + } + else + { + for (int i = 0; i < topicLength; i++) + { + _sendBufferMessage[2 + i] = topic[i]; + } + _sendBufferMessage[2 + topicLength + 1] = '\0'; + _dataToSend = true; + } + while (_dataToSend) + { + handleNetwork(); + } + return error; +} + +bool DidacticNetPSNClient::unsubscribe(char *topic) +{ + return unsubscribe(topic, strlen(topic)); +} + +bool DidacticNetPSNClient::unsubscribe(char *topic, int topicLength) +{ + int topicNumber = getTopicNr(topic); + if (topicNumber >= 0) + { + _topic[topicNumber][0] = '\0'; + return true; + } + return false; +} + +bool DidacticNetPSNClient::getMessageFilter(char messageType) +{ + return messageType == PS_MSG_UPDATE; +} + +bool DidacticNetPSNClient::savePayload(char *buffer, int position) +{ + strcpy(_payload[position], buffer); + return true; +} + +bool DidacticNetPSNClient::handleData() +{ + int currentTopicNr = 0; + int topicLength = 0; + int payloadLength = 0; + topicLength = extractData(2, MAX_LEN_TOPICS, _bufferTopic, DN_MSG_SEPARATOR); + if (topicLength > 0) + { + currentTopicNr = getTopicNr(_bufferTopic); + payloadLength = extractData(topicLength + 3, MAX_LEN_PAYLOAD, _bufferPayload, DN_MSG_DELIMITER); + if (currentTopicNr >= 0) + { + savePayload(_bufferPayload, currentTopicNr); + _newMessageAvailable = true; + _newMessageTopicNr = currentTopicNr; + + if (_clientMode == CLIENT_MODE_ADVANCED) + { +#ifdef CALLBACK_W_LENGTH + callback(_bufferTopic, topicLength, _bufferPayload, payloadLength); +#else + callback(_bufferTopic, _bufferPayload); +#endif + } + } + } + return true; +} + +int DidacticNetPSNClient::getTopicNr(char *topic) +{ + for (int i = 0; i < PS_MAX_NR_TOPICS_CLIENT; i++) + { + if (strcmp(_topic[i], topic) == 0 || _topic[i][0] == PS_MSG_TOPIC_MULTI) + { + return i; + } + } + return -1; +} + +int DidacticNetPSNClient::getFreeTopicNr() +{ + for (int i = 0; i < PS_MAX_NR_TOPICS_CLIENT; i++) + { + if (strcmp(_topic[i], "") == 0) + { + return i; + } + } + return -1; +} + +int DidacticNetPSNClient::getSubscribedTopic(char *topic, int number) +{ + if (number > 0 && number < getMaxNrTopics()) + { + strcpy(topic, _topic[number]); + return PS_ERROR_NO_ERROR; + } + return PS_ERROR_NO_TOPIC; +} + +int DidacticNetPSNClient::getMaxNrTopics() +{ + return getFreeTopicNr(); +} + +//************************************************************************** +// Broker +//************************************************************************** +DidacticNetPSNBroker::DidacticNetPSNBroker() +{ + _intervalTime = PS_INTERVAL_BROKER; +} + +DidacticNetPSNBroker::~DidacticNetPSNBroker() {} + +bool DidacticNetPSNBroker::getMessageFilter(char messageType) +{ + return (messageType == PS_MSG_PUBLISH || messageType == PS_MSG_SUBSCRIBE); +} + +bool DidacticNetPSNBroker::savePayload(char *buffer, int position) +{ + strcpy(_data[position], buffer); + return true; +} + +void DidacticNetPSNBroker::writeDataToTopic(int topicNumber, char *usedTopic, char *newData) +{ + if (strcmp(_topic[topicNumber], "") == 0) + { + strcpy(_topic[topicNumber], usedTopic); + } + strcpy(_data[topicNumber], newData); +} + +bool DidacticNetPSNBroker::handleData() +{ + int currentTopicNr = 0; + int topicLength = 0; + int dataLength = 0; + + printVerbose(_readBufferMessage); + + if (_readBufferMessage[1] == PS_MSG_PUBLISH) + { + topicLength = extractData(2, MAX_LEN_TOPICS, _bufferTopic, DN_MSG_SEPARATOR); + if (topicLength > 0) + { + currentTopicNr = getTopicNr(_bufferTopic); + dataLength = extractData(topicLength + 3, MAX_LEN_PAYLOAD, _bufferPayload, DN_MSG_DELIMITER); + if (currentTopicNr >= 0) + { + writeDataToTopic(currentTopicNr, _bufferTopic, _bufferPayload); + update(_topic[currentTopicNr], topicLength, _data[currentTopicNr], dataLength); + } + } + } + else if (_readBufferMessage[1] == PS_MSG_SUBSCRIBE) + { + topicLength = extractData(2, MAX_LEN_TOPICS, _bufferTopic, DN_MSG_DELIMITER); + if (topicLength > 0) + { + currentTopicNr = getTopicNr(_bufferTopic); + if (currentTopicNr >= 0) + { + update(_topic[currentTopicNr], strlen(_topic[currentTopicNr]), _data[currentTopicNr], strlen(_data[currentTopicNr])); + } + } + } + return true; +} + +bool DidacticNetPSNBroker::update(char *topic, int topicLength, char *data, int dataLength) +{ + _sendBufferMessage[0] = DN_MSG_PRELIMITER; + _sendBufferMessage[1] = PS_MSG_UPDATE; + _sendBufferMessage[2 + topicLength] = DN_MSG_SEPARATOR; + _sendBufferMessage[2 + topicLength + 1 + dataLength] = DN_MSG_DELIMITER; + _sendBufferMessage[2 + topicLength + 1 + dataLength + 1] = '\0'; + + if (topicLength <= MAX_LEN_TOPICS) + { + for (int i = 0; i < topicLength; i++) + { + _sendBufferMessage[2 + i] = topic[i]; + } + } + else + { + _dataToSend = false; + return false; + } + if (dataLength <= MAX_LEN_PAYLOAD) + { + for (int i = 0; i < dataLength; i++) + { + _sendBufferMessage[2 + topicLength + 1 + i] = data[i]; + } + } + else + { + _dataToSend = false; + return false; + } + _dataToSend = true; + return true; +} + +int DidacticNetPSNBroker::getTopicNr(char *topic) +{ + for (int i = 0; i < PS_MAX_NR_TOPICS_BROKER; i++) + { + if (strcmp(_topic[i], topic) == 0) + { + return i; + } + } + return getFreeTopicNr(); +} + +int DidacticNetPSNBroker::getFreeTopicNr() +{ + for (int i = 0; i < PS_MAX_NR_TOPICS_BROKER; i++) + { + if (strcmp(_topic[i], "") == 0) + { + return i; + } + } + return -1; +} + +void DidacticNetPSNBroker::setVerbose(Stream &verbosePort) +{ + _isVerbose = true; + _verbosePort = &verbosePort; +} + +void DidacticNetPSNBroker::setNoneVerbose() +{ + _isVerbose = false; +} + +bool DidacticNetPSNBroker::printVerbose(char *recievedData) +{ + char signBuffer = 0; + int signCounter = 0; + + if (_isVerbose) + { + while (signBuffer != DN_MSG_DELIMITER) + { + signBuffer = recievedData[signCounter]; + _verbosePort->write(signBuffer); + signCounter++; + } + _verbosePort->write('\n'); + } + return _isVerbose; +} diff --git a/src/DidacticNetPSN.h b/src/DidacticNetPSN.h new file mode 100644 index 0000000000000000000000000000000000000000..726bef75fff0e3b2acd7a1def5a4de181bb744d0 --- /dev/null +++ b/src/DidacticNetPSN.h @@ -0,0 +1,124 @@ +/************************************************************************** + @file DidacticNet.h + @author anian buehler @ letsgoING +**************************************************************************/ + +#include "Detector.h" + +// PubSub +#ifdef CALLBACK_W_LENGTH +// callback(topic, topicLength, payload, payloadLength) +#define PSNET_CALLBACK_SIGNATURE void (*callback)(char *, int, char *, int) +#else +// callback(topic, payload) +#define PSNET_CALLBACK_SIGNATURE void (*callback)(char *, char *) +#endif + +#define PS_CLIENT_MODE_BASIC false +#define PS_CLIENT_MODE_ADVANCED true + +//@ publish → on publish check topic, then send topic-update +//? subscribe → subscribe starts update, topic filter @client +// # update → update to specific topic Broker to client +#define PS_MSG_PUBLISH '@' +#define PS_MSG_SUBSCRIBE '?' +#define PS_MSG_UPDATE '#' +#define PS_MSG_TOPIC_MULTI '*' + +#define PS_MAX_NR_TOPICS_CLIENT 10 +#define PS_MAX_NR_TOPICS_BROKER 20 +#define PS_MAX_LEN_TOPICS 10 +#define PS_MAX_LEN_PAYLOAD 20 + +#define PS_PUBLISH_SUCCESSULL 1 +#define PS_ERROR_NO_ERROR 0 +#define PS_ERROR_TOPIC_LEN -1 +#define PS_ERROR_PAYLOAD_LEN -2 +#define PS_ERROR_NO_TOPIC -3 + +#define PS_INTERVAL_CLIENT 500L +#define PS_INTERVAL_BROKER 0L + +class DidacticPSNetClient : public DidacticNetTransmit +{ +private: + EdgeDetector eDetector; + ChangeDetector cDetector; + + char _topic[PS_MAX_NR_TOPICS_CLIENT][PS_MAX_LEN_TOPICS + 1] = {{0}}; + char _payload[PS_MAX_NR_TOPICS_CLIENT][PS_MAX_LEN_PAYLOAD + 1] = {{0}}; + + bool _clientMode = PS_CLIENT_MODE_ADVANCED; + bool _newMessageAvailable = false; + int _newMessageTopicNr = 0; + char _currentTopic[MAX_LEN_TOPICS + 1] = {0}; + char _currentPayload[MAX_LEN_PAYLOAD + 1] = {0}; + + bool savePayload(char *, int); + bool getMessageFilter(char); + bool handleData(); + int getTopicNr(char *); + int getFreeTopicNr(); + + int edgeDetected(bool); + bool valueChanged(int, int); + +public: + DidacticPSNetClient(); + ~DidacticPSNetClient(); + + void begin(Stream &_port); + void begin(Stream &_port, PSNET_CALLBACK_SIGNATURE); + + bool available(); + int readLatestTopicNr(); + void readTopic(char *); + void readTopic(int, char *); + void readPayload(char *); + void readPayload(int, char *); + void readPayload(char *, char *); + bool readBooleanPayload(); + int readIntegerPayload(); + + int getMaxNrTopics(); + int getSubscribedTopic(char *, int); + + int publish(char *, char *); + int publish(char *, int, char *, int); + int publish(char *, int); + int publish(char *, bool); + int publishOnChange(char *, bool); + int publishOnChange(char *, int, int); + int subscribe(char *); + int subscribe(char *, int); + bool unsubscribe(char *); + bool unsubscribe(char *, int); +}; + +class DidacticPSNetBroker : public DidacticNetTransmit +{ +private: + Stream *_verbosePort; + char _topic[MAX_NR_TOPICS_BROKER][MAX_LEN_TOPICS + 1] = {{0}}; + char _data[MAX_NR_TOPICS_BROKER][MAX_LEN_PAYLOAD + 1] = {{0}}; + + bool _isVerbose = false; + + bool savePayload(char *, int); + bool getMessageFilter(char); + void writeDataToTopic(int, char *, char *); + bool handleData(); + int getTopicNr(char *); + int getFreeTopicNr(); + bool update(char *, int, char *, int); + bool printVerbose(char *); + +public: + DidacticPSNetBroker(); + ~DidacticPSNetBroker(); + + void setVerbose(Stream &_port); + void setNoneVerbose(); +}; + +#endif diff --git a/src/DidacticNetTransmit.cpp b/src/DidacticNetTransmit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d5b7747d624b051a670ab3299f2b915f2a9c4c66 --- /dev/null +++ b/src/DidacticNetTransmit.cpp @@ -0,0 +1,167 @@ +/**************************************************************************/ +/*! + @file DidacticNetTransmit.cpp + @author anian buehler @ letsgoING.org +*/ +/**************************************************************************/ + +#include "DidacticNetTransmit.h" + +DidacticNetTransmit::DidacticNetTransmit() {} +DidacticNetTransmit::~DidacticNetTransmit() {} + +void DidacticNetTransmit::begin(Stream &_port) +{ + setStream(_port); +} + +void DidacticNetTransmit::begin(Stream &_port, PSNET_CALLBACK_SIGNATURE) +{ + setStream(_port); + setCallback(callback); +} + +DidacticNetTransmit &DidacticNetTransmit::setCallback(PSNET_CALLBACK_SIGNATURE) +{ + this->callback = callback; + return *this; +} + +void DidacticNetTransmit::setStream(Stream &stream) +{ + _port = &stream; +} + +bool DidacticNetTransmit::handleNetwork() +{ + if (dataAvailable()) + { + if (recieveData()) + { + if (getMessageFilter(_readBufferMessage[1])) + { + handleData(); + } + } + _waitingTimeCSMA = millis() + random(CSMA_MIN_DELAY_MS, CSMA_MAX_DELAY_MS); + } + if (_dataToSend && _waitingTimeSend <= millis() && _waitingTimeCSMA <= millis()) + { + // send data to network + unsigned long delayStartTime = micros(); + while (micros() < delayStartTime + CSMA_CHECK_DELAY_US) + ; + if (!dataAvailable()) + { + if (sendData()) + { + _dataToSend = false; + _waitingTimeSend = millis() + _intervalTime; + } + } + } + return !_dataToSend; // return true if sending is done +} + +bool DidacticNetTransmit::isDataToSend() +{ + return _dataToSend; +} + +bool DidacticNetTransmit::sendData() +{ + int counter = 0; + bool messageSent = false; + while (!messageSent) + { + if (counter >= DN_MAX_LEN_TELEGRAM - 1) + { + _sendBufferMessage[counter] = DN_MSG_DELIMITER; // cut message and stop sending + messageSent = true; + } + else if (_sendBufferMessage[counter] == DN_MSG_DELIMITER) + { + messageSent = true; + } + + _port->write(_sendBufferMessage[counter]); + counter++; + } + return true; +} + +int DidacticNetTransmit::extractData(int startCounter, int maxLength, char *buffer, char limiter) +{ + int counter = startCounter; + while (_readBufferMessage[counter] != limiter) + { + buffer[counter - startCounter] = _readBufferMessage[counter]; + counter++; + if ((counter - startCounter) > maxLength) + { + counter--; + break; // if > maxLenght -> leave while and return + } + } + buffer[counter - startCounter] = '\0'; + return counter - startCounter; // length +} + +int DidacticNetTransmit::dataAvailable() +{ + return (int)_port->available(); +} + +bool DidacticNetTransmit::recieveData() +{ + + static int msgCounter = 0; + static int topicCounter = 0; // check if needed? + static int payloadCounter = 0; // check if needed? + + while (dataAvailable()) + { + char localBuffer = _port->read(); + if (localBuffer == DN_MSG_PRELIMITER) + { + msgCounter = 0; + topicCounter = 0; + payloadCounter = 0; + _readBufferMessage[msgCounter] = localBuffer; + } + else if (localBuffer == DN_MSG_DELIMITER && _readBufferMessage[0] == DN_MSG_PRELIMITER) + { + msgCounter++; + _readBufferMessage[msgCounter] = localBuffer; + _readBufferMessage[msgCounter + 1] = '0'; + msgCounter = 0; + return true; + } + // check if needed? Don't think so... + else if (localBuffer == DN_MSG_SEPARATOR && _readBufferMessage[0] == DN_MSG_PRELIMITER) + { + topicCounter = msgCounter - 2; + msgCounter++; + _readBufferMessage[msgCounter] = localBuffer; + } + else if (_readBufferMessage[0] == DN_MSG_PRELIMITER && localBuffer != DN_MSG_DELIMITER) + { + if (msgCounter > DN_MAX_LEN_TELEGRAM) + { + msgCounter == 0; + _readBufferMessage[0] = '\0'; + } + else + { + msgCounter++; + _readBufferMessage[msgCounter] = localBuffer; + } + } + } + return false; +} + +void DidacticNetTransmit::setInterval(long intervalTime) +{ + _intervalTime = intervalTime; +} \ No newline at end of file diff --git a/src/DidacticNetTransmit.h b/src/DidacticNetTransmit.h new file mode 100644 index 0000000000000000000000000000000000000000..6abbce5bac7a411f48a454ec978e67bb9cea9153 --- /dev/null +++ b/src/DidacticNetTransmit.h @@ -0,0 +1,65 @@ +/************************************************************************** + @file DidacticNetTransmit.h + @author anian buehler @ letsgoING +**************************************************************************/ + +#ifndef _DIDACTICNETTRANSMIT_ +#define _DIDACTICNETTRANSMIT_ + +#include "Arduino.h" + +#define DN_LEN_OVERHEAD 4 + +#define DNT_CSMA_CHECK_DELAY_US 400 +#define DNT_CSMA_MIN_DELAY_MS 10 +#define DNT_CSMA_MID_DELAY_MS 20 +#define DNT_CSMA_MAX_DELAY_MS 30 + +class DidacticNetTransmit +{ +protected: + Stream *_port; + + PSNET_CALLBACK_SIGNATURE; + + char _bufferTopic[MAX_LEN_TOPICS + 1] = {0}; + char _bufferPayload[MAX_LEN_PAYLOAD + 1] = {0}; + char _readBufferMessage[DN_MAX_LEN_TELEGRAM + 1]; + char _sendBufferMessage[DN_MAX_LEN_TELEGRAM + 1]; + + bool _dataToSend = false; // int Data to send for queue? + unsigned long _waitingTimeSend = 0L; + unsigned long _waitingTimeCSMA = 0L; + unsigned long _intervalTime = 0L; + int _currentTopicLength = 0; + int _currentPayloadLength = 0; + + DidacticNetTransmit &setCallback(PSNET_CALLBACK_SIGNATURE); + + void setStream(Stream &_port); + + int checkData(); + bool recieveData(); + bool sendData(); + int extractData(int, int, char *, char); + void writeDataToTopic(char *, char *); + virtual int getTopicNr(char *) = 0; + virtual int getFreeTopicNr() = 0; + virtual bool getMessageFilter(char) = 0; + virtual bool savePayload(char *, int) = 0; + virtual bool handleData() = 0; + +public: + DidacticNetTransmit(); + ~DidacticNetTransmit(); + + void begin(Stream &_port); + void begin(Stream &_port, PSNET_CALLBACK_SIGNATURE); + + bool handleNetwork(); + bool isDataToSend(); + + void setInterval(long); +}; + +#endif \ No newline at end of file diff --git a/src/SerialUI.cpp b/src/SerialUI.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5365fe8d9e2fbbda2017d1c0e5221893ea0bb0eb --- /dev/null +++ b/src/SerialUI.cpp @@ -0,0 +1,74 @@ +/**************************************************************************/ +/*! + @file SerialReader.cpp + @author anian buehler @ letsgoING.org +*/ +/**************************************************************************/ + +#include SerialUI.h + +SerialReader::SerialReader() {} +SerialReader::~SerialReader() {} + +void SerialReader::begin(Stream &rsStream) +{ + _port = &rsStream; +} + +int SerialReader::readSerialData(char *rsDataArray, char rsEndSign) +{ + + if (_port->available()) + { + char charBuffer = _port->read(); + rsDataArray[charCounter] = charBuffer; + + if (charBuffer == rsEndSign) + { + rsDataArray[charCounter] = '\0'; + int nrOfChars = charCounter; + charCounter = 0; + return nrOfChars; + } + else + { + charCounter++; + } + } + return 0; +} + +serialMonitorUI::serialMonitorUI() {} +serialMonitorUI::~serialMonitorUI() {} + +char serialMonitorUI::extractCommand(char *userInput) +{ + if ((userInput[0] >= 33 && userInput[0] <= 47) || ((userInput[0] >= 58 && userInput[0] <= 64))) // ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ + { + char command = userInput[0]; + strcpy(userInput, &userInput[1]); + return command; + } + return DN_ASCII_EOS; +} + +bool serialMonitorUI::available() +{ + if (SerialReader::readSerialData(_currentInput, DN_ASCII_CR) > 0) + { + _currentCommand = extractCommand(_currentInput); + return true; + } + + return false; +} + +char serialMonitorUI::readCommand() +{ + return _currentCommand; +} + +void serialMonitorUI::readUserInput(char *inputData) +{ + strcpy(inputData, _currentInput); +} \ No newline at end of file diff --git a/src/SerialUI.h b/src/SerialUI.h new file mode 100644 index 0000000000000000000000000000000000000000..0b26e4905e7b1684cb483bb32638d4594435f458 --- /dev/null +++ b/src/SerialUI.h @@ -0,0 +1,48 @@ +/************************************************************************** + @file SerialReader.h + @author anian buehler @ letsgoING +**************************************************************************/ + +#ifndef _LGI_SERIALUI_ +#define _LGI_SERIALUI_ + +#include "Arduino.h" + +#define SUI_MAX_LEN_USERINPUT 41 + +// little helpers +#define SUI_ASCII_EOS 0 +#define SUI_ASCII_CR 13 +#define SUI_ASCII_NL 10 +#define SUI_ASCII_DEL 127 + +class SerialReader +{ +protected: + Stream *_port; + int charCounter = 0; + +public: + SerialReader(); + ~SerialReader(); + void begin(Stream &); + int readSerialData(char *, char); +}; + +class serialMonitorUI : public SerialReader +{ +protected: + char _currentCommand = SUI_ASCII_EOS; + char _currentInput[SUI_MAX_LEN_USERINPUT + 1] = {SUI_ASCII_EOS}; + + char extractCommand(char *); + +public: + serialMonitorUI(); + ~serialMonitorUI(); + bool available(); + char readCommand(); + void readUserInput(char *); +}; + +#endif \ No newline at end of file diff --git a/src/UnblockingTimer.cpp b/src/UnblockingTimer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2685229feff91dbe3063f1b552926600c85b469a --- /dev/null +++ b/src/UnblockingTimer.cpp @@ -0,0 +1,23 @@ +/**************************************************************************/ +/*! + @file UnblockingTimer.cpp + @author anian buehler @ letsgoING.org +*/ +/**************************************************************************/ + +#include UnblockingTimer.h + +UnblockingTimer::UnblockingTimer() {} +UnblockingTimer::~UnblockingTimer() {} + +bool UnblockingTimer::timeElapsed(long delayTime) +{ + long currentTime = millis(); + + if (lastTime + (delayTime - 1) < currentTime) + { + lastTime = currentTime; + return true; + } + return false; +} \ No newline at end of file diff --git a/src/UnblockingTimer.h b/src/UnblockingTimer.h new file mode 100644 index 0000000000000000000000000000000000000000..63530aa37730c1a2f37d22c8a3c3fd3b232164ad --- /dev/null +++ b/src/UnblockingTimer.h @@ -0,0 +1,22 @@ +/************************************************************************** + @file UnblockingTimer.h + @author anian buehler @ letsgoING +**************************************************************************/ + +#ifndef _LGI_UNBLOCKINGTIMER_ +#define _LGI_UNBLOCKINGTIMER_ + +#include "Arduino.h" + +class UnblockingTimer +{ +private: + long lastTime = 0L; + +public: + UnblockingTimer(); + ~UnblockingTimer(); + bool timeElapsed(long); +}; + +#endif \ No newline at end of file