diff --git a/src/main/java/com/ardublock/Main.java b/src/main/java/com/ardublock/Main.java index 2e9c717cfe44f4cf32a3e182d9c794a8288a5993..21b762d15a0a557d01a927a910b4c42571fd909a 100644 --- a/src/main/java/com/ardublock/Main.java +++ b/src/main/java/com/ardublock/Main.java @@ -2,7 +2,13 @@ package com.ardublock; import java.awt.Color; import java.awt.event.WindowEvent; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; import javax.swing.JFrame; import javax.swing.UIManager; @@ -28,6 +34,7 @@ public class Main { startOpenblocksFrame(); //startConsoleFrame(); + } private void startOpenblocksFrame() throws SAXException, IOException, ParserConfigurationException diff --git a/src/main/java/com/ardublock/core/Context.java b/src/main/java/com/ardublock/core/Context.java index 2b11994cfe8a33cc5c8bf3e2db8207e01fcb23b3..7ab7e7d6dc4b4bce300832ccb1dc3745a9835851 100644 --- a/src/main/java/com/ardublock/core/Context.java +++ b/src/main/java/com/ardublock/core/Context.java @@ -44,7 +44,6 @@ public class Context //TODO change AppName? by letsgoING final public static String APP_NAME = "ArduBlock"; - //TODO: ADD saveDefaultArdublockProgram() private Editor editor; @@ -197,7 +196,7 @@ public class Context e.printStackTrace(); } - path = path + "/../../../"; //from tools/ArduBlockTool/tool/ to Arduino-root + path = path + File.separatorChar+".."+ File.separatorChar+".."+ File.separatorChar+".."+ File.separatorChar; //from tools/ArduBlockTool/tool/ to Arduino-root //TODO: check on MAC and WIN /* diff --git a/src/main/java/com/ardublock/ui/OpenblocksFrame.java b/src/main/java/com/ardublock/ui/OpenblocksFrame.java index 806ad1d8c03f8d214b6b4744454269c3a7b5b951..8d06f091f633c22c5d4d5488090356d0ba04f57b 100644 --- a/src/main/java/com/ardublock/ui/OpenblocksFrame.java +++ b/src/main/java/com/ardublock/ui/OpenblocksFrame.java @@ -13,7 +13,10 @@ import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLDecoder; @@ -36,6 +39,7 @@ import javax.swing.ToolTipManager; import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileNameExtensionFilter; +import com.ardublock.ArduBlockTool; import com.ardublock.core.Context; import com.ardublock.ui.listener.ArdublockWorkspaceListener; import com.ardublock.ui.listener.ButtonMouseListener; @@ -125,6 +129,10 @@ public class OpenblocksFrame extends JFrame appPrefix = uiMessageBundle.getString("ardublock.ui.appprefix.standard"); setTitle(makeFrameTitle()); + //TODO: TEST addLibrary functionality + addLibrary("LGI_QTouch"); + addLibrary("didacticNet"); + initOpenBlocks(); } @@ -1278,5 +1286,88 @@ public class OpenblocksFrame extends JFrame public boolean getModeState(){ //letsgoING return workspaceModeState; } + + //TODO: TESTTESTTEST + private void addLibrary(String libraryName) + { + //TEST TODO: validate + //copy provided libraries to ArduinoLibraries + String libraryPath = null; + String libraryResource ="/com/ardublock/libraries/"+libraryName; + String arduinoLibraryPath = null; + File libraryHeaderFile = null; + File librarySourceFile = null; + + + System.out.println("copy libraries..."); + + //get current .jar path for temp files + //TODO: change path to sketchbook-folder + try { + arduinoLibraryPath = new File(URLDecoder.decode(getClass().getProtectionDomain().getCodeSource().getLocation().getFile(), "UTF-8")).getParentFile().getPath(); + arduinoLibraryPath = arduinoLibraryPath +File.separatorChar+".."+File.separatorChar+".."+File.separatorChar+".."+File.separatorChar+"libraries"; + } catch (UnsupportedEncodingException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + + System.out.println("Path: "+arduinoLibraryPath); + + File arduinoLibFolder = new File(arduinoLibraryPath); + + if(!arduinoLibFolder.exists()) { + System.out.println("No libraries-folder - Nothing to do for me..."); + }else { + libraryPath = arduinoLibraryPath + File.separatorChar + libraryName; + File libraryFolder = new File(libraryPath); + if(!libraryFolder.exists()) { + libraryFolder.mkdir(); + while(!libraryFolder.exists()); + } + try { + InputStream inputHeader = getClass().getResourceAsStream(libraryResource+".h"); + if(inputHeader != null) { + libraryHeaderFile = new File(libraryPath+File.separatorChar+libraryName+".h"); + OutputStream outHeader = new FileOutputStream(libraryHeaderFile); + int read; + byte[] bytes = new byte[1024]; + while ((read = inputHeader.read(bytes)) != -1) { + outHeader.write(bytes, 0, read); + } + outHeader.close(); + }else { + //TODO: handle if no lib is found + } + InputStream inputSource = getClass().getResourceAsStream(libraryResource+".cpp"); + if(inputSource != null) { + int read; + byte[] bytes = new byte[1024]; + librarySourceFile = new File(libraryPath+File.separatorChar+libraryName+".cpp"); + OutputStream outSource = new FileOutputStream(librarySourceFile); + while ((read = inputSource.read(bytes)) != -1) { + outSource.write(bytes, 0, read); + } + outSource.close(); + }else { + //TODO: handle if no lib is found + } + InputStream inputCSource = getClass().getResourceAsStream(libraryResource+".c"); + if(inputCSource != null) { + int read; + byte[] bytes = new byte[1024]; + librarySourceFile = new File(libraryPath+File.pathSeparator+libraryName+".c"); + OutputStream outSource = new FileOutputStream(librarySourceFile); + while ((read = inputSource.read(bytes)) != -1) { + outSource.write(bytes, 0, read); + } + outSource.close(); + }else { + //TODO: handle if no lib is found + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + } } diff --git a/src/main/resources/com/ardublock/block/ardublock_de.properties b/src/main/resources/com/ardublock/block/ardublock_de.properties index 9f8503682d6be4e4953e5eb6512cf65f03a987db..eb574f2f213e080cc4d2de4ad4ffbe1fc904d0da 100644 --- a/src/main/resources/com/ardublock/block/ardublock_de.properties +++ b/src/main/resources/com/ardublock/block/ardublock_de.properties @@ -706,19 +706,6 @@ bg.tele_read_data_frame=lese Daten aus Frame bg.mqtt_set_frame=erstelle MQTT Frame bg.mqtt_read_frame=lese aus MQTT Frame -bg.psn_broker=Broker psnName -bg.psn_client=Client psnName -bg.psn_callback=psnClientCallback -bg.psn_handle_network=psnName handleNetwork -bg.psn_data_to_send=psnName isDataToSend? -bg.psn_publish=psnName publish -bg.psn_publish_analog=psnName publish -bg.psn_publish_digital=psnName publish -bg.psn_publish_onchange=psnName publishOnChange -bg.psn_publish_onchange_bool=psnName publishOnChange -bg.psn_subscribe=psnName subscribe -bg.psn_unsubscribe=psnName unsubscribe - bg.serial_write.description=Sende Nachricht via Serial bg.serial_print.description=Sende Nachricht via Serial bg.serial_read.description=Lese Zeichen via Serial als kurze analoge Variable ein\n(siehe ASCII-Tabelle) @@ -771,6 +758,19 @@ bg.didacticnetworkDivider2=| PubSub publish | bg.didacticnetworkDivider3=| PubSub subscribe | bg.didacticnetworkDivider4=| PubSub callback | +bg.psn_broker=Broker psnName +bg.psn_client=Client psnName +bg.psn_callback=psnClientCallback +bg.psn_handle_network=psnName handleNetwork +bg.psn_data_to_send=psnName isDataToSend? +bg.psn_publish=psnName publish +bg.psn_publish_analog=psnName publish +bg.psn_publish_digital=psnName publish +bg.psn_publish_onchange=psnName publishOnChange +bg.psn_publish_onchange_bool=psnName publishOnChange +bg.psn_subscribe=psnName subscribe +bg.psn_unsubscribe=psnName unsubscribe + bg.psn_broker.description=Broker fuer PubSub Netzwerk erzeugen bg.psn_client.description=Client fuer PubSub Netzwerk erzeugen bg.psn_callback.description=Funktion wird automatisch aufgefufen wenn neue Daten empfangen werden diff --git a/src/main/resources/com/ardublock/block/ardublock_en_GB.properties b/src/main/resources/com/ardublock/block/ardublock_en_GB.properties index dea7645ffed8547d3a6011874f6d7782d697d99c..2a04923287e4ce65037914e1fbba47e272acfef4 100644 --- a/src/main/resources/com/ardublock/block/ardublock_en_GB.properties +++ b/src/main/resources/com/ardublock/block/ardublock_en_GB.properties @@ -758,6 +758,19 @@ bg.didacticnetworkDivider2=| PubSub publish | bg.didacticnetworkDivider3=| PubSub subscribe | bg.didacticnetworkDivider4=| PubSub callback | +bg.psn_broker=Broker psnName +bg.psn_client=Client psnName +bg.psn_callback=psnClientCallback +bg.psn_handle_network=psnName handleNetwork +bg.psn_data_to_send=psnName isDataToSend? +bg.psn_publish=psnName publish +bg.psn_publish_analog=psnName publish +bg.psn_publish_digital=psnName publish +bg.psn_publish_onchange=psnName publishOnChange +bg.psn_publish_onchange_bool=psnName publishOnChange +bg.psn_subscribe=psnName subscribe +bg.psn_unsubscribe=psnName unsubscribe + bg.psn_broker.description=Broker fuer PubSub Netzwerk erzeugen bg.psn_client.description=Client fuer PubSub Netzwerk erzeugen bg.psn_callback.description=Funktion wird automatisch aufgefufen wenn neue Daten empfangen werden diff --git a/src/main/resources/com/ardublock/libraries/LGI_QTouch.cpp b/src/main/resources/com/ardublock/libraries/LGI_QTouch.cpp new file mode 100644 index 0000000000000000000000000000000000000000..efb1f882593f59d78e0ce2412f6690bb06d7019f --- /dev/null +++ b/src/main/resources/com/ardublock/libraries/LGI_QTouch.cpp @@ -0,0 +1,304 @@ +/* +Library zur Verwendung der Analogeingänge für Touchsensoren +Diese Librarie basiert auf dem ATMEL QTouch-Prinzip und dem Basis Code von +J.Geisler -> https://github.com/jgeisler0303/QTouchADCArduino + +Hardware: +Kupferfläche oder sonstige leitfähige Fläche über 1-10kOhm Widerstand an AnalogIn +Attiny 25/45/85 AIN1-3 + +Anian Bühler 09.01.2015 +AB 27.05.2015 +*/ + +#include "LGI_QTouch.h" + + +//QTouch +//****************************************************************** +//****************************************************************** + +//Konstruktor +//****************************************************************** +QTouch :: QTouch(){} + +//setup +//****************************************************************** +void QTouch :: init(uint8_t TouchPin, uint8_t PartnerPin) { //Abfrage bei mehereren Objekten über ifndef _TOUCHINIT_ + + + + this -> _TouchPin = TouchPin; + this -> _PartnerPin = PartnerPin; + + #ifndef _TOUCHINIT_ + #define _TOUCHINIT_ + // prepare the ADC unit for one-shot measurements + // see the atmega328 datasheet for explanations of the registers and values + ADMUX = ADMUX_SETUP; // Vcc as voltage reference (bits76), right adjustment (bit5), use ADC0 as input (bits3210) + ADCSRA = 0b11000100; // enable ADC (bit7), initialize ADC (bit6), no autotrigger (bit5), don't clear int-flag (bit4), no interrupt (bit3), clock div by 16@16Mhz=1MHz (bit210) ADC should run at 50kHz to 200kHz, 1MHz gives decreased resolution + ADCSRB = 0b00000000; // autotrigger source free running (bits210) doesn't apply + while(ADCSRA & (1<<ADSC)){} // wait for first conversion to complete + #endif; +} + +//getter +//****************************************************************** +int QTouch :: getRawValue(uint8_t iterMeasure){ + int adc1 = 0; + int adc2 = 0; + + for(int i = 1; i <= iterMeasure; i++){ + adc1 += touch_probe(this-> _TouchPin, this-> _PartnerPin, true); + adc2 += touch_probe(this-> _TouchPin, this-> _PartnerPin, false); + } + + adc1 /= iterMeasure; + adc2 /= iterMeasure; + + return (adc2-adc1); +} + +uint16_t QTouch :: touch_probe(uint8_t pin, uint8_t partner, bool dir) { + uint8_t MUXPin; + uint8_t MUXPartner; + + // IF Attiny 8Pin is used --> ADC1 (PB2), ADC2(PB4), ADC3 (PB3) + #if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) + MUXPin = _tiny8AnalogToMux[pin]; + MUXPartner = _tiny8AnalogToMux[partner]; + pin = (pin < 6 ? pin : pin - 6);//analogPinToChannel(pin); + partner = (partner < 6 ? partner : partner - 6); + + #else // IF Standard Arduino Boards are used + //if const A0-A5 is used + pin = (pin < 14 ? pin : pin - 14);//analogPinToChannel(pin); + partner = (partner < 14 ? partner : partner - 14); + MUXPin = pin; + MUXPartner = partner; + #endif; + + uint8_t mask= _BV(pin) | _BV(partner); + + ADC_DIR|= mask; // config pins as push-pull output + if(dir) + ADC_PORT= (ADC_PORT & ~_BV(pin)) | _BV(partner); // set partner high to charge the s&h cap and pin low to discharge touch probe cap + else + ADC_PORT= (ADC_PORT & ~_BV(partner)) | _BV(pin); // set pin high to charge the touch probe and pin low to discharge s&h cap cap + // charge/discharge s&h cap by connecting it to partner + ADMUX = MUX_REF_VCC | MUXPartner; // select partner as input to the ADC unit + delayMicroseconds(CHARGE_DELAY); // wait for the touch probe and the s&h cap to be fully charged/dsicharged + ADC_DIR&= ~mask; // config pins as input + ADC_PORT&= ~mask; // disable the internal pullup to make the ports high Z + // connect touch probe cap to s&h cap to transfer the charge + ADMUX= MUX_REF_VCC | MUXPin; // select pin as ADC input + delayMicroseconds(TRANSFER_DELAY); // wait for charge to be transfered + // measure + ADCSRA|= (1<<ADSC); // start measurement + while(ADCSRA & (1<<ADSC)){ + } // wait for conversion to complete + + return ADC; // return conversion result +} + + +//QTouch BUTTON +//****************************************************************** +//****************************************************************** + + +//Konstruktor +//****************************************************************** +QTouchButton :: QTouchButton(uint8_t TouchPin, uint8_t PartnerPin):_QTouch(){ + + this->_QTouch.init(TouchPin, PartnerPin); + + this-> _Offset = 0; + this-> _hysteresis = 30; + +} + + +//setup +//****************************************************************** +void QTouchButton :: init(){ //Abfrage bei mehereren Objekten über ifndef _TOUCHINIT_ + this-> _Offset = this->_QTouch.getRawValue(10); +} + + +//setter +//****************************************************************** +void QTouchButton :: setHysteresis(uint8_t hysteresis){ + this-> _hysteresis = hysteresis; + } + +void QTouchButton :: setOffset(uint8_t Offset){ + this-> _Offset = Offset; + } + + +//getter +//****************************************************************** +uint8_t QTouchButton :: getHysteresis(){ + return this-> _hysteresis; + } + +int QTouchButton :: getOffset(){ + return this-> _Offset; + } + +bool QTouchButton :: isTouched(){ + return ((this->_QTouch.getRawValue(4) - this-> _Offset) > this-> _hysteresis); +} + +int QTouchButton :: getRawTouch(){ + return this->_QTouch.getRawValue(4); +} + + +//QTouch SLIDER +//****************************************************************** +//****************************************************************** + +//Konstruktor +//****************************************************************** +QTouchSlider :: QTouchSlider(uint8_t TouchPin1, uint8_t TouchPin2, uint8_t TouchPin3):_QTouch1(), _QTouch2(), _QTouch3(){ + + this->_QTouch1.init(TouchPin1, TouchPin3); + this->_QTouch2.init(TouchPin2, TouchPin1); + this->_QTouch3.init(TouchPin3, TouchPin2); + + this-> _Offset1 = 0; + this-> _Offset2 = 0; + this-> _Offset3 = 0; + this-> _hysteresis = 30; + this-> _threshold = 10; + this-> _maxVal1 = 0; + this-> _maxVal2 = 0; + this-> _maxVal3 = 0; + this-> _lastSliderPos = 0; +} + + +//setup +//****************************************************************** +void QTouchSlider :: init(){ //Abfrage bei mehereren Objekten über ifndef _TOUCHINIT_ + this-> _Offset1 = this->_QTouch1.getRawValue(10); + this-> _Offset2 = this->_QTouch2.getRawValue(10); + this-> _Offset3 = this->_QTouch3.getRawValue(10); +} + + +//setter +//****************************************************************** +void QTouchSlider :: setHysteresis(uint8_t hysteresis){ + this-> _hysteresis = hysteresis; + } + +void QTouchSlider :: setOffset(uint8_t Offset1, uint8_t Offset2, uint8_t Offset3){ + this-> _Offset1 = Offset1; + this-> _Offset2 = Offset2; + this-> _Offset3 = Offset3; + } + + void QTouchSlider :: setThreshold(uint8_t threshold){ + this-> _threshold = threshold; + } + + +//getter +//****************************************************************** +uint8_t QTouchSlider :: getHysteresis(){ + return this-> _hysteresis; + } + +uint8_t QTouchSlider :: getThreshold(){ + return this-> _threshold; +} + +int QTouchSlider :: getOffset(uint8_t num){ + switch(num){ + case 1: + return this-> _Offset1; + break; + case 2: + return this-> _Offset2; + break; + case 3: + return this-> _Offset3; + break; + } +} + + + +int QTouchSlider :: getRawTouch(uint8_t field){ + switch(field){ + case 1: + return this->_QTouch1.getRawValue(4); + break; + case 2: + return this->_QTouch2.getRawValue(4); + break; + case 3: + return this->_QTouch3.getRawValue(4); + break; + } +} + +uint8_t QTouchSlider :: getTouchPosition(){ + bool _mode = true; + + int raw1, raw2, raw3; + int map1, map2, map3; + uint8_t sliderPosition = 0; + + raw1 = getRawTouch(1)- getOffset(1); + raw2 = getRawTouch(2)- getOffset(2); + raw3 = getRawTouch(3)- getOffset(3); + + this-> _maxVal1 = max(this-> _maxVal1, raw1); + this-> _maxVal2 = max(this-> _maxVal2, raw2); + this-> _maxVal3 = max(this-> _maxVal3, raw3); + + map1 = constrain(map( raw1, this-> _threshold, this-> _maxVal1, 0, 100), 0, 100); + map2 = constrain(map( raw2, this-> _threshold, this-> _maxVal2, 0, 100), 0, 100); + map3 = constrain(map( raw3, this-> _threshold, this-> _maxVal3, 0, 100), 0, 100); + + //not touched + if( map1 <= 0 && map2 <= 0 && map2 <= 0){ + if(_mode) + sliderPosition = _lastSliderPos; + else + sliderPosition = 0; + } + //upper half is touched + else if( (map1 - map3) > 0 ){ + if( map2 > 0){ + sliderPosition = map(map2 - map1, -100, 100, 0, 50); + _lastSliderPos = sliderPosition; + } + else{ + sliderPosition = 0; + _lastSliderPos = sliderPosition; + } + } + //middle is touched + else if( map2 > 0 && map1 == 0 && map3 == 0 ){ + sliderPosition = 50; + _lastSliderPos = sliderPosition; + } + //lower half is touched + else if((map1 - map3) < 0){ + if( map2 > 0){ + sliderPosition = map(map3 - map2, -100, 100, 50, 100); + _lastSliderPos = sliderPosition; + } + else{ + sliderPosition = 100; + _lastSliderPos = sliderPosition; + } + } + + return sliderPosition; +} diff --git a/src/main/resources/com/ardublock/libraries/LGI_QTouch.h b/src/main/resources/com/ardublock/libraries/LGI_QTouch.h new file mode 100644 index 0000000000000000000000000000000000000000..bd4817beeba311c6ae60ca081802b3f2a578e88e --- /dev/null +++ b/src/main/resources/com/ardublock/libraries/LGI_QTouch.h @@ -0,0 +1,149 @@ +/* +Library zur Verwendung der Analogeingänge für Touchsensoren +Diese Librarie basiert auf dem ATMEL QTouch-Prinzip und dem Basis Code von +J.Geisler -> https://github.com/jgeisler0303/QTouchADCArduino + +Hardware: +Kupferfläche oder sonstige leitfähige Fläche über 1kOhm Widerstand an AnalogIn + +Anian Bühler 01.09.2014 +*/ +#include "Arduino.h" + + +#ifndef _LGIQTOUCH_ +#define _LGIQTOUCH_ + +#define ADMUX_MASK 0b00001111 // mask the mux bits in the ADMUX register +#define MUX_GND 0b00001111 // mux value for connecting the ADC unit internally to GND +#define CHARGE_DELAY 5 // time it takes for the capacitor to get charged/discharged in microseconds +#define TRANSFER_DELAY 5 // time it takes for the capacitors to exchange charge + + // IF Attiny 8Pin is used +#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) + #define ADC_PORT PORTB //USE PORT B + #define ADC_DIR DDRB // + #define ADMUX_SETUP 0b10010000// Set Vref to Vcc + #define MUX_REF_VCC 0b10010000 // value to set the ADC reference to Vcc + #define ADC_SETUP 0b10000100//0b11000011 // Set ADC pre-scaler to 8 +#else // IF Standard Arduino Boards are used + #define ADC_PORT PORTC //USE PORT C + #define ADC_DIR DDRC // + #define ADMUX_SETUP 0b01000000// Set Vref to AVcc with ext Cap + #define MUX_REF_VCC 0b01000000 // value to set the ADC reference to Vcc + #define ADC_SETUP 0b11000100 // Set ADC pre-scaler to 16 +#endif; + +class QTouch +{ + private: + uint8_t _TouchPin, + _PartnerPin; + + const uint8_t _tiny8AnalogToMux[10] = { + 0, //D0 + 0, //D1 + 1, //D2 Ain1 + 3, //D3 Ain3 + 2, //D4 Ain2 + 0, //RESET D5 Ain0 + 0, //A0->Ain0(Reset) + 1, //A1->Ain1 + 2, //A2->Ain2 + 3, //A3->Ain3 + }; + + + public: + + + + //Konstruktoren + //****************************************************************** + QTouch(); + + //setup + //****************************************************************** + void init(uint8_t TouchPin1, uint8_t PartnerPin); + + //getter + //****************************************************************** + int getRawValue(uint8_t iterMeasure); + uint16_t touch_probe(uint8_t pin, uint8_t partner, bool dir); +}; + + +class QTouchButton +{ +private: + + QTouch _QTouch; + uint8_t _hysteresis; + + int _Offset; + + + + +public: + //Konstruktoren + //****************************************************************** + QTouchButton(uint8_t TouchPin1, uint8_t PartnerPin); + + //setup + //****************************************************************** + void init(); + + //setter + //****************************************************************** + void setHysteresis(uint8_t hysteresis); + void setOffset(uint8_t Offset); + + //getter + //****************************************************************** + uint8_t getHysteresis(); + int getOffset(); + bool isTouched(); + int getRawTouch(); +}; + + +class QTouchSlider +{ +private: + + QTouch _QTouch1, _QTouch2, _QTouch3; + uint8_t _hysteresis, _threshold, + _maxVal1, _maxVal2, _maxVal3, + _lastSliderPos; + + int _Offset1, _Offset2, _Offset3; + + + + +public: + //Konstruktoren + //****************************************************************** + QTouchSlider(uint8_t TouchPin1, uint8_t TouchPin2, uint8_t TouchPin3); + + //setup + //****************************************************************** + void init(); + + //setter + //****************************************************************** + void setHysteresis(uint8_t hysteresis); + void setOffset(uint8_t Offset1, uint8_t Offset2, uint8_t Offset3); + void setThreshold(uint8_t threshold); + + //getter + //****************************************************************** + uint8_t getHysteresis(); + uint8_t getThreshold(); + int getOffset(uint8_t num); + int getRawTouch(uint8_t field); + uint8_t getTouchPosition(); +}; + +#endif; \ No newline at end of file diff --git a/src/main/resources/com/ardublock/libraries/didacticNet.cpp b/src/main/resources/com/ardublock/libraries/didacticNet.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f14ace21992e70b7d533ffd00d95831966f92b66 --- /dev/null +++ b/src/main/resources/com/ardublock/libraries/didacticNet.cpp @@ -0,0 +1,497 @@ +/**************************************************************************/ +/*! + @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(_waitingTime <= millis()){ + if(checkData()){ + if(recieveData()){ + //Serial.print("Message filter: ");Serial.println(_readBufferMessage[1]); + //Serial.print("Check Message filter: ");Serial.println(getMessageFilter(_readBufferMessage[1])); + if(getMessageFilter(_readBufferMessage[1])){ + handleData(); + } + } + _waitingTime = millis()+ random(CSMA_MIN_DELAY_MS, CSMA_MAX_DELAY_MS); + } + //else if(_dataToSend){ + if(_dataToSend && _waitingTime <= millis()){ + //send data to network + //TODO: test added CSMA_CHECKDELAY + 2nd checkData() + delayMicroseconds(CSMA_CHECK_DELAY_US); + if(!checkData()){ + if(!sendData()){ + return false; + } + else{ + _dataToSend = false; + //_waitingTime = millis()+ random(CSMA_MID_DELAY_MS, CSMA_MAX_DELAY_MS); + _waitingTime = millis()+ _intervalTime;//random(CSMA_MID_DELAY_MS, CSMA_MAX_DELAY_MS); + } + } + } + //} + return true; +} + +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){ + //TODO: check!!! + _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; + //if(msgCounter == NULL){ msgCounter = 0; } + //if(topicCounter == NULL){ topicCounter = 0; } + //if(dataCounter == NULL){ dataCounter = 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) { + msgCounter++; + _readBufferMessage[msgCounter] = localBuffer; + } + } + return false; +} + + void DidacticPSNet::setInterval(long intervalTime){ + _intervalTime = intervalTime; + } + + +//************************************************************************** +// CLIENT +//************************************************************************** +DidacticPSNetClient::DidacticPSNetClient(){ + _intervalTime = INTERVAL_CLIENT; +} + +DidacticPSNetClient::~DidacticPSNetClient(){} + +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; +} + +//TODO: TEST TEST TEST +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); + callback(_topic[currentTopicNr], topicLength, _payload[currentTopicNr], payloadLength); + } + } + 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) { //TODO: check ... or equal 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; +} + + +//************************************************************************** +//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; + 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; +} + + +//************************************************************************** +//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; +} \ No newline at end of file diff --git a/src/main/resources/com/ardublock/libraries/didacticNet.h b/src/main/resources/com/ardublock/libraries/didacticNet.h new file mode 100644 index 0000000000000000000000000000000000000000..596db882857b225191ab1d86fa05712d91df8856 --- /dev/null +++ b/src/main/resources/com/ardublock/libraries/didacticNet.h @@ -0,0 +1,203 @@ +/************************************************************************** + @file didacticNet.h + @author anian buehler @ letsgoING +**************************************************************************/ + + +#ifndef _DIDACTICNET_ +#define _DIDACTICNET_ + +#include "Arduino.h" +//callback(topic, topicLength, data, dataLength) +#define PSNET_CALLBACK_SIGNATURE void (*callback)(char*, int, char*, int) + +#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 '*' + +//<@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 5 +#define MAX_NR_TOPICS_BROKER 20 +#define MAX_LEN_TOPICS 10 +#define MAX_LEN_PAYLOAD 20 + +#define DN_PUBLISH_SUCCESSULL 1 +#define DN_ERROR_NO_ERROR 0 +#define DN_ERROR_TOPIC_LEN -1 +#define DN_ERROR_PAYLOAD_LEN -2 + + +//little helpers +#define DN_ASCII_CR 13 +#define DN_ASCII_NL 10 + + +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 +{ + private: + Stream* _port; + int charCounter = 0; + + public: + SerialReader(); + ~SerialReader(); + void begin(Stream&); + int readSerialData(char*, 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 _waitingTime = 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 savePayload(char*, int); + bool getMessageFilter(char); + bool handleData(); + int getTopicNr(char*); + int getFreeTopicNr(); + + int edgeDetected(bool); + bool valueChanged(int, int); + + public: + DidacticPSNetClient(); + ~DidacticPSNetClient(); + + 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: + char _topic[MAX_NR_TOPICS_BROKER][MAX_LEN_TOPICS+1] = { { 0 } }; + char _data[MAX_NR_TOPICS_BROKER][MAX_LEN_PAYLOAD+1] = { { 0 } }; + + bool savePayload(char*, int); + bool getMessageFilter(char); + void writeDataToTopic(int, char*, char*); + bool handleData(); + int getTopicNr(char*); + int getFreeTopicNr(); + + public: + DidacticPSNetBroker(); + ~DidacticPSNetBroker(); + + bool update(char*, int, char*, int); + + +}; + + +#endif