From 0a70e43a23996ce61d1529a185d5f16dd5a68b35 Mon Sep 17 00:00:00 2001 From: Antoine Date: Sat, 6 Oct 2018 08:48:38 -0400 Subject: [PATCH] Added v2.0.179 source code ported from http://www.end2endzone.com/anyrtttl-a-feature-rich-arduino-library-for-playing-rtttl-melodies/ Note that AnyRtttl library is a port from my NonBlockingRtttl arduino library which is now deprecated since development has stopped. --- anyrtttl.cpp | 634 ++++++++++++++++ anyrtttl.h | 241 ++++++ binrtttl.cpp | 137 ++++ binrtttl.h | 157 ++++ examples/Basic/Basic.ino | 22 + .../BlockingWithNonBlocking.ino | 38 + examples/NonBlockingDemo/NonBlockingDemo.ino | 53 ++ .../NonBlockingInterruptedDemo.ino | 42 ++ examples/Play10Bits/Play10Bits.ino | 44 ++ examples/Play16Bits/Play16Bits.ino | 27 + examples/Rtttl2Code/Rtttl2Code.ino | 52 ++ keywords.txt | 8 + pitches.h | 272 +++++++ tests/BitReader/LICENSE LGPL-3.0.txt | 165 ++++ tests/BitReader/bitreader.cpp | 242 ++++++ tests/BitReader/bitreader.h | 127 ++++ tests/BitReader/keywords.txt | 10 + tests/testAnyRtttl.sln | 34 + tests/testAnyRtttl/expected_call_stack.log | 124 +++ tests/testAnyRtttl/gtesthelper.cpp | 711 +++++++++++++++++ tests/testAnyRtttl/gtesthelper.h | 237 ++++++ tests/testAnyRtttl/main.cpp | 20 + tests/testAnyRtttl/targetver.h | 13 + tests/testAnyRtttl/testAnyRtttl.vcproj | 273 +++++++ tests/testAnyRtttl/tests.cpp | 247 ++++++ tests/testAnyRtttl/tests.h | 15 + tests/win32arduino/LICENSE LGPL-3.0.txt | 165 ++++ tests/win32arduino/arduino.cpp | 712 ++++++++++++++++++ tests/win32arduino/arduino.h | 336 +++++++++ tests/win32arduino/win32Arduino.vcproj | 303 ++++++++ 30 files changed, 5461 insertions(+) create mode 100644 anyrtttl.cpp create mode 100644 anyrtttl.h create mode 100644 binrtttl.cpp create mode 100644 binrtttl.h create mode 100644 examples/Basic/Basic.ino create mode 100644 examples/BlockingWithNonBlocking/BlockingWithNonBlocking.ino create mode 100644 examples/NonBlockingDemo/NonBlockingDemo.ino create mode 100644 examples/NonBlockingInterruptedDemo/NonBlockingInterruptedDemo.ino create mode 100644 examples/Play10Bits/Play10Bits.ino create mode 100644 examples/Play16Bits/Play16Bits.ino create mode 100644 examples/Rtttl2Code/Rtttl2Code.ino create mode 100644 keywords.txt create mode 100644 pitches.h create mode 100644 tests/BitReader/LICENSE LGPL-3.0.txt create mode 100644 tests/BitReader/bitreader.cpp create mode 100644 tests/BitReader/bitreader.h create mode 100644 tests/BitReader/keywords.txt create mode 100644 tests/testAnyRtttl.sln create mode 100644 tests/testAnyRtttl/expected_call_stack.log create mode 100644 tests/testAnyRtttl/gtesthelper.cpp create mode 100644 tests/testAnyRtttl/gtesthelper.h create mode 100644 tests/testAnyRtttl/main.cpp create mode 100644 tests/testAnyRtttl/targetver.h create mode 100644 tests/testAnyRtttl/testAnyRtttl.vcproj create mode 100644 tests/testAnyRtttl/tests.cpp create mode 100644 tests/testAnyRtttl/tests.h create mode 100644 tests/win32arduino/LICENSE LGPL-3.0.txt create mode 100644 tests/win32arduino/arduino.cpp create mode 100644 tests/win32arduino/arduino.h create mode 100644 tests/win32arduino/win32Arduino.vcproj diff --git a/anyrtttl.cpp b/anyrtttl.cpp new file mode 100644 index 0000000..fea9ae9 --- /dev/null +++ b/anyrtttl.cpp @@ -0,0 +1,634 @@ +// --------------------------------------------------------------------------- +// AnyRtttl Library - v2.0 - 05/21/2016 +// Copyright (C) 2016 Antoine Beauchamp +// The code & updates for the library can be found on http://end2endzone.com +// +// See "AnyRtttl.h" for license, purpose, syntax, version history, links, and more. +// --------------------------------------------------------------------------- + +#include "arduino.h" +#include "anyrtttl.h" +#include "binrtttl.h" + +/********************************************************* + * RTTTL Library data + *********************************************************/ + +namespace anyrtttl +{ + +const uint16_t notes[] = { NOTE_SILENT, +NOTE_C4, NOTE_CS4, NOTE_D4, NOTE_DS4, NOTE_E4, NOTE_F4, NOTE_FS4, NOTE_G4, NOTE_GS4, NOTE_A4, NOTE_AS4, NOTE_B4, +NOTE_C5, NOTE_CS5, NOTE_D5, NOTE_DS5, NOTE_E5, NOTE_F5, NOTE_FS5, NOTE_G5, NOTE_GS5, NOTE_A5, NOTE_AS5, NOTE_B5, +NOTE_C6, NOTE_CS6, NOTE_D6, NOTE_DS6, NOTE_E6, NOTE_F6, NOTE_FS6, NOTE_G6, NOTE_GS6, NOTE_A6, NOTE_AS6, NOTE_B6, +NOTE_C7, NOTE_CS7, NOTE_D7, NOTE_DS7, NOTE_E7, NOTE_F7, NOTE_FS7, NOTE_G7, NOTE_GS7, NOTE_A7, NOTE_AS7, NOTE_B7 +}; + +#define isdigit(n) (n >= '0' && n <= '9') +typedef uint16_t TONE_DURATION; +static const byte NOTES_PER_OCTAVE = 12; + +const char * buffer = ""; +int bufferIndex = -32760; +byte default_dur = 4; +byte default_oct = 5; +RTTTL_BPM bpm = 63; +RTTTL_DURATION wholenote; +byte pin = -1; +unsigned long delayToNextNote = 0; //milliseconds before playing the next note +bool playing = false; +TONE_DURATION duration; +byte noteOffset; +RTTTL_OCTAVE_VALUE scale; +int tmpNumber; + +const char * readNumber(const char * iBuffer, int & oValue) +{ + oValue = 0; + while(isdigit(*iBuffer)) + { + oValue = (oValue * 10) + (*iBuffer++ - '0'); + } + return iBuffer; +} + +/**************************************************************************** + * Custom functions + ****************************************************************************/ + +ToneFuncPtr _tone = &tone; +NoToneFuncPtr _noTone = &noTone; +DelayFuncPtr _delay = &delay; +MillisFuncPtr _millis = &millis; + +void setToneFunction(ToneFuncPtr iFunc) { + _tone = iFunc; +} + +void setNoToneFunction(NoToneFuncPtr iFunc) { + _noTone = iFunc; +} + +void setDelayFunction(DelayFuncPtr iFunc) { + _delay = iFunc; +} + +void setMillisFunction(MillisFuncPtr iFunc) { + _millis = iFunc; +} + + +/**************************************************************************** + * Blocking API + ****************************************************************************/ +namespace blocking +{ + +void play(byte iPin, const char * iBuffer) { + // Absolutely no error checking in here + + default_dur = 4; + default_oct = 6; + bpm = 63; + + // format: d=N,o=N,b=NNN: + // find the start (skip name, etc) + + while(*iBuffer != ':') iBuffer++; // ignore name + iBuffer++; // skip ':' + + // get default duration + if(*iBuffer == 'd') + { + iBuffer++; iBuffer++; // skip "d=" + iBuffer = readNumber(iBuffer, tmpNumber); + if(tmpNumber > 0) + default_dur = tmpNumber; + iBuffer++; // skip comma + } + + #ifdef ANY_RTTTL_INFO + Serial.print("ddur: "); Serial.println(default_dur, 10); + #endif + + // get default octave + if(*iBuffer == 'o') + { + iBuffer++; iBuffer++; // skip "o=" + iBuffer = readNumber(iBuffer, tmpNumber); + if(tmpNumber >= 3 && tmpNumber <= 7) + default_oct = tmpNumber; + iBuffer++; // skip comma + } + + #ifdef ANY_RTTTL_INFO + Serial.print("doct: "); Serial.println(default_oct, 10); + #endif + + // get BPM + if(*iBuffer == 'b') + { + iBuffer++; iBuffer++; // skip "b=" + iBuffer = readNumber(iBuffer, tmpNumber); + bpm = tmpNumber; + iBuffer++; // skip colon + } + + #ifdef ANY_RTTTL_INFO + Serial.print("bpm: "); Serial.println(bpm, 10); + #endif + + // BPM usually expresses the number of quarter notes per minute + wholenote = (60 * 1000L / bpm) * 4; // this is the time for whole noteOffset (in milliseconds) + + #ifdef ANY_RTTTL_INFO + Serial.print("wn: "); Serial.println(wholenote, 10); + #endif + + // now begin note loop + while(*iBuffer) + { + // first, get note duration, if available + iBuffer = readNumber(iBuffer, tmpNumber); + + if(tmpNumber) + duration = wholenote / tmpNumber; + else + duration = wholenote / default_dur; // we will need to check if we are a dotted noteOffset after + + // now get the note + noteOffset = getNoteOffsetFromLetter(*iBuffer); + iBuffer++; + + // now, get optional '#' sharp + if(*iBuffer == '#') + { + noteOffset++; + iBuffer++; + } + + // now, get optional '.' dotted note + if(*iBuffer == '.') + { + duration += duration/2; + iBuffer++; + } + + // now, get scale + if(isdigit(*iBuffer)) + { + scale = *iBuffer - '0'; + iBuffer++; + } + else + { + scale = default_oct; + } + + if(*iBuffer == ',') + iBuffer++; // skip comma for next note (or we may be at the end) + + // now play the note + if(noteOffset) + { + uint16_t frequency = notes[(scale - 4) * NOTES_PER_OCTAVE + noteOffset]; + + #ifdef ANY_RTTTL_INFO + Serial.print("Playing: "); + Serial.print(scale, 10); Serial.print(' '); + Serial.print(noteOffset, 10); Serial.print(" ("); + Serial.print(frequency, 10); + Serial.print(") "); + Serial.println(duration, 10); + #endif + + _tone(iPin, frequency, duration); + _delay(duration+1); + _noTone(iPin); + } + else + { + #ifdef ANY_RTTTL_INFO + Serial.print("Pausing: "); + Serial.println(duration, 10); + #endif + _delay(duration); + } + } +} + + +void play16Bits(int iPin, const unsigned char * iBuffer, int iNumNotes) { + // Absolutely no error checking in here + + RTTTL_DEFAULT_VALUE_SECTION * defaultSection = (RTTTL_DEFAULT_VALUE_SECTION *)iBuffer; + RTTTL_NOTE * notesBuffer = (RTTTL_NOTE *)iBuffer; + + bpm = defaultSection->bpm; + + #ifdef ANY_RTTTL_DEBUG + Serial.print("numNotes="); + Serial.println(iNumNotes); + // format: d=N,o=N,b=NNN: + Serial.print("d="); + Serial.print(getNoteDurationFromIndex(defaultSection->durationIdx)); + Serial.print(",o="); + Serial.print(getNoteOctaveFromIndex(defaultSection->octaveIdx)); + Serial.print(",b="); + Serial.println(bpm); + #endif + + // BPM usually expresses the number of quarter notes per minute + wholenote = (60 * 1000L / bpm) * 4; // this is the time for whole noteOffset (in milliseconds) + + // now begin note loop + for(int i=0; i= 0 && iIndex < gNoteLettersCount) + return gNoteLetters[iIndex]; + return -1; +} + +uint16_t getNoteLettersCount() +{ + return gNoteLettersCount; +} + +NOTE_LETTER_INDEX findNoteLetterIndex(RTTTL_NOTE_LETTER n) +{ + for(NOTE_LETTER_INDEX i=0; i= 0 && iIndex < gNoteLettersCount) + return gNoteOffsets[iIndex]; + return 0; +} + +int getNoteOffsetFromLetter(RTTTL_NOTE_LETTER n) +{ + NOTE_LETTER_INDEX index = findNoteLetterIndex(n); + return getNoteOffsetFromLetterIndex(index); +} + +RTTTL_DURATION getNoteDurationFromIndex(DURATION_INDEX iIndex) +{ + if (iIndex >= 0 && iIndex < gNoteDurationsCount) + return gNoteDurations[iIndex]; + return -1; +} + +uint16_t getNoteDurationsCount() +{ + return gNoteDurationsCount; +} + +DURATION_INDEX findNoteDurationIndex(RTTTL_DURATION n) +{ + for(DURATION_INDEX i=0; i= 0 && iIndex < gNoteOctavesCount) + return gNoteOctaves[iIndex]; + return -1; +} + +uint16_t getNoteOctavesCount() +{ + return gNoteOctavesCount; +} + +OCTAVE_INDEX findNoteOctaveIndex(RTTTL_OCTAVE_VALUE n) +{ + for(OCTAVE_INDEX i=0; i= 0 && iIndex < gNoteBpmsCount) + return gNoteBpms[iIndex]; + return -1; +} + +uint16_t getBpmsCount() +{ + return gNoteBpmsCount; +} + +BPM_INDEX findBpmIndex(RTTTL_BPM n) +{ + for(BPM_INDEX i=0; i +#include +#include + +//project's contants +#define BUZZER_PIN 8 +const char * tetris = "tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a"; + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + Serial.begin(115200); + Serial.println(); +} + +void loop() { + anyrtttl::blocking::play(BUZZER_PIN, tetris); + + while(true) + { + } +} diff --git a/examples/BlockingWithNonBlocking/BlockingWithNonBlocking.ino b/examples/BlockingWithNonBlocking/BlockingWithNonBlocking.ino new file mode 100644 index 0000000..1ea5ec9 --- /dev/null +++ b/examples/BlockingWithNonBlocking/BlockingWithNonBlocking.ino @@ -0,0 +1,38 @@ +#include +#include +#include + +//project's contants +#define BUZZER_PIN 8 +const char * tetris = "tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a"; +const char * arkanoid = "Arkanoid:d=4,o=5,b=140:8g6,16p,16g.6,2a#6,32p,8a6,8g6,8f6,8a6,2g6"; +const char * mario = "mario:d=4,o=5,b=100:16e6,16e6,32p,8e6,16c6,8e6,8g6,8p,8g,8p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,16p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16c7,16p,16c7,16c7,p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16d#6,8p,16d6,8p,16c6"; + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + Serial.begin(115200); + Serial.println(); +} + +void loop() { + + anyrtttl::nonblocking::begin(BUZZER_PIN, mario); + while( !anyrtttl::nonblocking::done() ) + { + anyrtttl::nonblocking::play(); + } + + anyrtttl::nonblocking::begin(BUZZER_PIN, arkanoid); + while( !anyrtttl::nonblocking::done() ) + { + anyrtttl::nonblocking::play(); + } + + anyrtttl::nonblocking::begin(BUZZER_PIN, tetris); + while( !anyrtttl::nonblocking::done() ) + { + anyrtttl::nonblocking::play(); + } + +} diff --git a/examples/NonBlockingDemo/NonBlockingDemo.ino b/examples/NonBlockingDemo/NonBlockingDemo.ino new file mode 100644 index 0000000..fabb694 --- /dev/null +++ b/examples/NonBlockingDemo/NonBlockingDemo.ino @@ -0,0 +1,53 @@ +#include +#include +#include + +//project's contants +#define BUZZER_PIN 8 +const char * tetris = "tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a"; +const char * arkanoid = "Arkanoid:d=4,o=5,b=140:8g6,16p,16g.6,2a#6,32p,8a6,8g6,8f6,8a6,2g6"; +const char * mario = "mario:d=4,o=5,b=100:16e6,16e6,32p,8e6,16c6,8e6,8g6,8p,8g,8p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,16p,8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16c7,16p,16c7,16c7,p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,8p,16d#6,8p,16d6,8p,16c6"; +byte songIndex = 0; //which song to play when the previous one finishes + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + Serial.begin(115200); + Serial.println(); +} + +void loop() { + if ( !anyrtttl::nonblocking::isPlaying() ) + { + if (songIndex == 0) + { + anyrtttl::nonblocking::begin(BUZZER_PIN, mario); + songIndex++; //ready for next song + + //play for 5 sec then stop. + //note: this is a blocking code section + //use to demonstrate the use of stop() + unsigned long start = millis(); + while( millis() - start < 5000 ) + { + anyrtttl::nonblocking::play(); + } + anyrtttl::nonblocking::stop(); + + } + else if (songIndex == 1) + { + anyrtttl::nonblocking::begin(BUZZER_PIN, arkanoid); + songIndex++; //ready for next song + } + else if (songIndex == 2) + { + anyrtttl::nonblocking::begin(BUZZER_PIN, tetris); + songIndex++; //ready for next song + } + } + else + { + anyrtttl::nonblocking::play(); + } +} diff --git a/examples/NonBlockingInterruptedDemo/NonBlockingInterruptedDemo.ino b/examples/NonBlockingInterruptedDemo/NonBlockingInterruptedDemo.ino new file mode 100644 index 0000000..22c34b0 --- /dev/null +++ b/examples/NonBlockingInterruptedDemo/NonBlockingInterruptedDemo.ino @@ -0,0 +1,42 @@ +#include +#include +#include + +//project's contants +#define BUZZER_PIN 8 +const char * tetris = "tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a"; + +unsigned long playStart = 0; + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + Serial.begin(115200); + Serial.println(); +} + +void loop() { + static bool firstPass = true; + + if (firstPass) { + anyrtttl::nonblocking::begin(BUZZER_PIN, tetris); + + //remember when we started playing the song + playStart = millis(); + } + + //if we are playing something + if ( anyrtttl::nonblocking::isPlaying() ) { + + //does the melody been playing for more than 5 seconds ? + if ( millis() - playStart > 5000 ) + { + anyrtttl::nonblocking::stop(); + } + } + + //if anything available for playing, play it + anyrtttl::nonblocking::play(); + + firstPass = false; +} diff --git a/examples/Play10Bits/Play10Bits.ino b/examples/Play10Bits/Play10Bits.ino new file mode 100644 index 0000000..171ed49 --- /dev/null +++ b/examples/Play10Bits/Play10Bits.ino @@ -0,0 +1,44 @@ +#include +#include +#include + +#include + +//project's contants +#define BUZZER_PIN 8 + +//RTTTL 10 bits binary format for the following: tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a +//Compatible with AnyRtttl library v2.0 +//The code & updates for the AnyRtttl library can be found on http://end2endzone.com +const unsigned char tetris10[] = {0x0A, 0x14, 0x12, 0xCE, 0x34, 0xE0, 0x82, 0x14, 0x32, 0x38, 0xE0, 0x4C, 0x2A, 0xAD, 0x34, 0xA0, 0x84, 0x0B, 0x0E, 0x28, 0xD3, 0x4C, 0x03, 0x2A, 0x28, 0xA1, 0x80, 0x2A, 0xA5, 0xB4, 0x93, 0x82, 0x1B, 0xAA, 0x38, 0xE2, 0x86, 0x12, 0x4E, 0x38, 0xA0, 0x84, 0x0B, 0x0E, 0x28, 0xD3, 0x4C, 0x03, 0x2A, 0x28, 0xA1, 0x80, 0x2A, 0xA9, 0x04}; +const int tetris10_length = 42; + +//bit reader support +#ifndef USE_BITADDRESS_READ_WRITE +BitReader bitreader; +#else +BitAddress bitreader; +#endif +uint16_t readNextBits(uint8_t numBits) +{ + uint16_t bits = 0; + bitreader.read(numBits, &bits); + return bits; +} + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + bitreader.setBuffer(tetris10); + + Serial.begin(115200); + Serial.println(); +} + +void loop() { + anyrtttl::blocking::play10Bits(BUZZER_PIN, tetris10_length, &readNextBits); + + while(true) + { + } +} diff --git a/examples/Play16Bits/Play16Bits.ino b/examples/Play16Bits/Play16Bits.ino new file mode 100644 index 0000000..22e377f --- /dev/null +++ b/examples/Play16Bits/Play16Bits.ino @@ -0,0 +1,27 @@ +#include +#include +#include + +//project's contants +#define BUZZER_PIN 8 + +//RTTTL 16 bits binary format for the following: tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a +//Compatible with AnyRtttl library v2.0 +//The code & updates for the AnyRtttl library can be found on http://end2endzone.com +const unsigned char tetris16[] = {0x0A, 0x14, 0x12, 0x02, 0x33, 0x01, 0x03, 0x02, 0x0B, 0x02, 0x14, 0x02, 0x0C, 0x02, 0x03, 0x02, 0x33, 0x01, 0x2A, 0x01, 0x2B, 0x01, 0x03, 0x02, 0x12, 0x02, 0x0B, 0x02, 0x03, 0x02, 0x32, 0x01, 0x33, 0x01, 0x03, 0x02, 0x0A, 0x02, 0x12, 0x02, 0x02, 0x02, 0x2A, 0x01, 0x29, 0x01, 0x3B, 0x01, 0x0A, 0x02, 0x1B, 0x02, 0x2A, 0x02, 0x23, 0x02, 0x1B, 0x02, 0x12, 0x02, 0x13, 0x02, 0x03, 0x02, 0x12, 0x02, 0x0B, 0x02, 0x03, 0x02, 0x32, 0x01, 0x33, 0x01, 0x03, 0x02, 0x0A, 0x02, 0x12, 0x02, 0x02, 0x02, 0x2A, 0x01, 0x2A, 0x01}; +const int tetris16_length = 42; + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + Serial.begin(115200); + Serial.println(); +} + +void loop() { + anyrtttl::blocking::play16Bits(BUZZER_PIN, tetris16, tetris16_length); + + while(true) + { + } +} diff --git a/examples/Rtttl2Code/Rtttl2Code.ino b/examples/Rtttl2Code/Rtttl2Code.ino new file mode 100644 index 0000000..0edabbf --- /dev/null +++ b/examples/Rtttl2Code/Rtttl2Code.ino @@ -0,0 +1,52 @@ +#include +#include +#include + +//project's contants +#define BUZZER_PIN 8 +const char * tetris = "tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a"; + +//******************************************************************************************************************* +// The following replacement functions prints the function call & parameters to the serial port. +//******************************************************************************************************************* +void serialTone(byte pin, uint16_t frequency, uint32_t duration) { + Serial.print("tone("); + Serial.print(pin); + Serial.print(","); + Serial.print(frequency); + Serial.print(","); + Serial.print(duration); + Serial.println(");"); +} + +void serialNoTone(byte pin) { + Serial.print("noTone("); + Serial.print(pin); + Serial.println(");"); +} + +void serialDelay(uint32_t duration) { + Serial.print("delay("); + Serial.print(duration); + Serial.println(");"); +} + +void setup() { + pinMode(BUZZER_PIN, OUTPUT); + + Serial.begin(115200); + Serial.println(); + + //Use custom functions + anyrtttl::setToneFunction(&serialTone); + anyrtttl::setNoToneFunction(&serialNoTone); + anyrtttl::setDelayFunction(&serialDelay); +} + +void loop() { + anyrtttl::blocking::play(BUZZER_PIN, tetris); + + while(true) + { + } +} diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..44365c0 --- /dev/null +++ b/keywords.txt @@ -0,0 +1,8 @@ +anyrtttl KEYWORD1 +play16Bits KEYWORD2 +play10Bits KEYWORD2 +begin KEYWORD2 +play KEYWORD2 +stop KEYWORD2 +isPlaying KEYWORD2 +done KEYWORD2 diff --git a/pitches.h b/pitches.h new file mode 100644 index 0000000..f17d155 --- /dev/null +++ b/pitches.h @@ -0,0 +1,272 @@ +// --------------------------------------------------------------------------- +// AnyRtttl Library - v2.0 - 05/21/2016 +// Copyright (C) 2016 Antoine Beauchamp +// The code & updates for the library can be found on http://end2endzone.com +// +// AUTHOR/LICENSE: +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License (LGPL-3.0) for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// DISCLAIMER: +// This software is furnished "as is", without technical support, and with no +// warranty, express or implied, as to its usefulness for any purpose. +// +// PURPOSE: +// After publishing my NonBlockingRtttl arduino library, I started using the +// library in more complex projects which was requiring other libraries. +// I quickly ran into the hell of library dependencies and library conflicts. +// I realized that I needed more features that could help me prototype faster. +// +// Other libraries available which allows you to play a melody in RTTTL format +// suffer the same issue: they are based on blocking APIs or the RTTTL data is +// not optimized for space. +// +// AnyRtttl is different since it packs multiple RTTTL related features in a +// single library. It supports blocking & non-blocking API which makes it +// suitable to be used by more advanced algorithm. For instance, when using +// the non-blocking API, the melody can be stopped when a button is pressed. +// The library is also compatible with external Tone libraries and supports +// highly compressed RTTTL binary formats. +// +// USAGE: +// The library has multiple examples which shows how to use the library with +// each features. Refers the to examples details. More information is also +// available on the project's home page: http://end2endzone.com. +// +// Define ANY_RTTTL_INFO to enable the debugging of the library state +// on the serial port. +// +// Use ANY_RTTTL_VERSION to get the current version of the library. +// +// HISTORY: +// 03/19/2016 v1.0 - Initial release of NonBlockingRtttl. +// 05/21/2016 v2.0 - Library converted to AnyRtttl. +// +// --------------------------------------------------------------------------- + +#ifndef PITCHES_H +#define PITCHES_H + +/************************************************* + * Notes Constants + *************************************************/ +#define NOTE_REST 0 +#define NOTE_SILENT 0 + +#define NOTE_C0 16 +#define NOTE_CS0 17 +#define NOTE_D0 18 +#define NOTE_DS0 19 +#define NOTE_E0 21 +#define NOTE_F0 22 +#define NOTE_FS0 23 +#define NOTE_G0 24 +#define NOTE_GS0 26 +#define NOTE_A0 28 +#define NOTE_AS0 29 +#define NOTE_B0 31 +#define NOTE_C1 33 +#define NOTE_CS1 35 +#define NOTE_D1 37 +#define NOTE_DS1 39 +#define NOTE_E1 41 +#define NOTE_F1 44 +#define NOTE_FS1 46 +#define NOTE_G1 49 +#define NOTE_GS1 52 +#define NOTE_A1 55 +#define NOTE_AS1 58 +#define NOTE_B1 62 +#define NOTE_C2 65 +#define NOTE_CS2 69 +#define NOTE_D2 73 +#define NOTE_DS2 78 +#define NOTE_E2 82 +#define NOTE_F2 87 +#define NOTE_FS2 93 +#define NOTE_G2 98 +#define NOTE_GS2 104 +#define NOTE_A2 110 +#define NOTE_AS2 117 +#define NOTE_B2 123 +#define NOTE_C3 131 +#define NOTE_CS3 139 +#define NOTE_D3 147 +#define NOTE_DS3 156 +#define NOTE_E3 165 +#define NOTE_F3 175 +#define NOTE_FS3 185 +#define NOTE_G3 196 +#define NOTE_GS3 208 +#define NOTE_A3 220 +#define NOTE_AS3 233 +#define NOTE_B3 247 +#define NOTE_C4 262 +#define NOTE_CS4 277 +#define NOTE_D4 294 +#define NOTE_DS4 311 +#define NOTE_E4 330 +#define NOTE_F4 349 +#define NOTE_FS4 370 +#define NOTE_G4 392 +#define NOTE_GS4 415 +#define NOTE_A4 440 +#define NOTE_AS4 466 +#define NOTE_B4 494 +#define NOTE_C5 523 +#define NOTE_CS5 554 +#define NOTE_D5 587 +#define NOTE_DS5 622 +#define NOTE_E5 659 +#define NOTE_F5 698 +#define NOTE_FS5 740 +#define NOTE_G5 784 +#define NOTE_GS5 831 +#define NOTE_A5 880 +#define NOTE_AS5 932 +#define NOTE_B5 988 +#define NOTE_C6 1047 +#define NOTE_CS6 1109 +#define NOTE_D6 1175 +#define NOTE_DS6 1245 +#define NOTE_E6 1319 +#define NOTE_F6 1397 +#define NOTE_FS6 1480 +#define NOTE_G6 1568 +#define NOTE_GS6 1661 +#define NOTE_A6 1760 +#define NOTE_AS6 1865 +#define NOTE_B6 1976 +#define NOTE_C7 2093 +#define NOTE_CS7 2217 +#define NOTE_D7 2349 +#define NOTE_DS7 2489 +#define NOTE_E7 2637 +#define NOTE_F7 2794 +#define NOTE_FS7 2960 +#define NOTE_G7 3136 +#define NOTE_GS7 3322 +#define NOTE_A7 3520 +#define NOTE_AS7 3729 +#define NOTE_B7 3951 +#define NOTE_C8 4186 +#define NOTE_CS8 4435 +#define NOTE_D8 4699 +#define NOTE_DS8 4978 +#define NOTE_CS8 4435 +#define NOTE_D8 4699 +#define NOTE_DS8 4978 +#define NOTE_E8 5274 +#define NOTE_F8 5588 +#define NOTE_FS8 5920 +#define NOTE_G8 6272 +#define NOTE_GS8 6645 +#define NOTE_A8 7040 +#define NOTE_AS8 7459 +#define NOTE_B8 7902 +#define NOTE_C9 8372 +#define NOTE_CS9 8870 +#define NOTE_D9 9397 +#define NOTE_DS9 9956 +#define NOTE_E9 10548 +#define NOTE_F9 11175 +#define NOTE_FS9 11840 +#define NOTE_G9 12544 +#define NOTE_GS9 13290 +#define NOTE_A9 ? +#define NOTE_AS9 ? +#define NOTE_B9 ? + +//Duplicated note with same frequency. +//Obtained from the following code: +// for(char o='0'; o<='9'; o++) +// { +// for(char letter='A'; letter<='G'; letter++) +// { +// // #define NOTE_DB7 NOTE_CS7 +// printf("#define NOTE_%cB%c NOTE_%cS%c\n", (letter+1 == 'H'? 'A' : letter+1), o, letter, o); +// } +// } +#define NOTE_BB0 NOTE_AS0 +#define NOTE_CB0 NOTE_BS0 +#define NOTE_DB0 NOTE_CS0 +#define NOTE_EB0 NOTE_DS0 +#define NOTE_FB0 NOTE_ES0 +#define NOTE_GB0 NOTE_FS0 +#define NOTE_AB0 NOTE_GS0 +#define NOTE_BB1 NOTE_AS1 +#define NOTE_CB1 NOTE_BS1 +#define NOTE_DB1 NOTE_CS1 +#define NOTE_EB1 NOTE_DS1 +#define NOTE_FB1 NOTE_ES1 +#define NOTE_GB1 NOTE_FS1 +#define NOTE_AB1 NOTE_GS1 +#define NOTE_BB2 NOTE_AS2 +#define NOTE_CB2 NOTE_BS2 +#define NOTE_DB2 NOTE_CS2 +#define NOTE_EB2 NOTE_DS2 +#define NOTE_FB2 NOTE_ES2 +#define NOTE_GB2 NOTE_FS2 +#define NOTE_AB2 NOTE_GS2 +#define NOTE_BB3 NOTE_AS3 +#define NOTE_CB3 NOTE_BS3 +#define NOTE_DB3 NOTE_CS3 +#define NOTE_EB3 NOTE_DS3 +#define NOTE_FB3 NOTE_ES3 +#define NOTE_GB3 NOTE_FS3 +#define NOTE_AB3 NOTE_GS3 +#define NOTE_BB4 NOTE_AS4 +#define NOTE_CB4 NOTE_BS4 +#define NOTE_DB4 NOTE_CS4 +#define NOTE_EB4 NOTE_DS4 +#define NOTE_FB4 NOTE_ES4 +#define NOTE_GB4 NOTE_FS4 +#define NOTE_AB4 NOTE_GS4 +#define NOTE_BB5 NOTE_AS5 +#define NOTE_CB5 NOTE_BS5 +#define NOTE_DB5 NOTE_CS5 +#define NOTE_EB5 NOTE_DS5 +#define NOTE_FB5 NOTE_ES5 +#define NOTE_GB5 NOTE_FS5 +#define NOTE_AB5 NOTE_GS5 +#define NOTE_BB6 NOTE_AS6 +#define NOTE_CB6 NOTE_BS6 +#define NOTE_DB6 NOTE_CS6 +#define NOTE_EB6 NOTE_DS6 +#define NOTE_FB6 NOTE_ES6 +#define NOTE_GB6 NOTE_FS6 +#define NOTE_AB6 NOTE_GS6 +#define NOTE_BB7 NOTE_AS7 +#define NOTE_CB7 NOTE_BS7 +#define NOTE_DB7 NOTE_CS7 +#define NOTE_EB7 NOTE_DS7 +#define NOTE_FB7 NOTE_ES7 +#define NOTE_GB7 NOTE_FS7 +#define NOTE_AB7 NOTE_GS7 +#define NOTE_BB8 NOTE_AS8 +#define NOTE_CB8 NOTE_BS8 +#define NOTE_DB8 NOTE_CS8 +#define NOTE_EB8 NOTE_DS8 +#define NOTE_FB8 NOTE_ES8 +#define NOTE_GB8 NOTE_FS8 +#define NOTE_AB8 NOTE_GS8 +#define NOTE_BB9 NOTE_AS9 +#define NOTE_CB9 NOTE_BS9 +#define NOTE_DB9 NOTE_CS9 +#define NOTE_EB9 NOTE_DS9 +#define NOTE_FB9 NOTE_ES9 +#define NOTE_GB9 NOTE_FS9 +#define NOTE_AB9 NOTE_GS9 + +#endif //PITCHES_H diff --git a/tests/BitReader/LICENSE LGPL-3.0.txt b/tests/BitReader/LICENSE LGPL-3.0.txt new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/tests/BitReader/LICENSE LGPL-3.0.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/tests/BitReader/bitreader.cpp b/tests/BitReader/bitreader.cpp new file mode 100644 index 0000000..54726c2 --- /dev/null +++ b/tests/BitReader/bitreader.cpp @@ -0,0 +1,242 @@ +// --------------------------------------------------------------------------- +// BitReader Library - v1.1 - 03/24/2016 +// Copyright (C) 2016 Antoine Beauchamp +// The code & updates for the library can be found on http://end2endzone.com +// +// See "BitReader.h" for license, purpose, syntax, version history, links, and more. +// --------------------------------------------------------------------------- + +#include "bitreader.h" + +#define MIN_BETWEEN2(a,b) (a < b ? a : b) +#define MIN_BETWEEN3(a,b,c) MIN_BETWEEN2(MIN_BETWEEN2(a,b),c) +#define MAX_BETWEEN2(a,b) (a > b ? a : b) +#define MAX_BETWEEN3(a,b,c) MAX_BETWEEN2(MAX_BETWEEN2(a,b),c) + +#define BIT_SET(value, bitIdx) ((value) |= (1<<(bitIdx))) +#define BIT_CLEAR(value, bitIdx) ((value) &= ~(1<<(bitIdx))) +#define BIT_FLIP(value, bitIdx) ((value) ^= (1<<(bitIdx))) +#define BIT_CHECK1(value, bitIdx) ((value) & (1<<(bitIdx))) +#define BIT_CHECK2(value, bitIdx) ((value >> bitIdx) & (1)) +#define BIT_CHECK3(value, bitIdx) ((byte & (1<> bitIdx) +#define BIT_UPDATE1(value, bitIdx, bitBoolValue) ( (bitBoolValue) ? BIT_SET((value), (bitIdx)) : BIT_CLEAR((value), (bitIdx)) ) +#define BIT_UPDATE2(value, bitIdx, bitBoolValue) BIT_CLEAR((value), (bitIdx)); (value) |= ((bitBoolValue)<= BITS_PER_BYTE) + { + byte++; + bit -= BITS_PER_BYTE; + } +} + +void BitAddress::next() +{ + bit++; + if (bit == BITS_PER_BYTE) + { + byte++; + bit = 0; + } +} + +unsigned int BitAddress::toAbsBitOffset() +{ + return byte*BITS_PER_BYTE + bit; +} + +#ifdef WIN32 +std::string BitAddress::toString() +{ + static const int BUFFER_SIZE = 24; + char buffer[BUFFER_SIZE]; + sprintf(buffer, "[%d,%d](bit %d)", byte, bit, toAbsBitOffset()); + return buffer; +} +#endif //WIN32 + +#ifdef USE_BITADDRESS_READ_WRITE +void BitAddress::setBuffer(const void * iBuffer) +{ + mBuffer = (const unsigned char *)iBuffer; +} + +void BitAddress::read(unsigned int iNumBits, void * iTargetBits) +{ + BitAddress output; + for(unsigned int i=0; i +#include //for stringstream +#include //for std::hex +#include //for random +#include //for random + +bool initRandomProvider(); +static bool foo = initRandomProvider(); + +std::string subString2(const std::string & iString, size_t iStart, size_t iCount) +{ + std::string tmp = iString; + tmp.substr(iStart, iCount); + return tmp; +} + +bool substringEquals(const char * iValue, const char * iSearchValue, size_t iIndex, size_t iCount) +{ + for(size_t i=iIndex; i<=iCount; i++) + { + if (iValue[i] == '\0') + return false; //reached end of iValue + if (iValue[i] != iSearchValue[i-iIndex]) + return false; //this character is not equal + if (i == iIndex+iCount) + return true;//equals + } + return false; //not found +} + +void splitString(gTestHelper::StringVector & oList, const char * iValue, const char * iSplitPattern) +{ + oList.clear(); + std::string accumulator; + std::string value = iValue; + std::string pattern = iSplitPattern; + for(size_t i=0; i 2) + { + //something went wrong filter contains multiple '-' character + //return the first 2 elements either way + oPositiveFilter = filters[0].c_str(); + oNegativeFilter = filters[1].c_str(); + } + else if (filters.size() == 2) + { + //positive and negative filter found + oPositiveFilter = filters[0].c_str(); + oNegativeFilter = filters[1].c_str(); + } + else if (filters.size() == 1) + { + //positive only filter found + oPositiveFilter = filters[0].c_str(); + oNegativeFilter = ""; + } + else + { + //something went wrong + } +} + +gTestHelper::StringVector gTestHelper::getTestList(const char * iTestCasePath) +{ + //check that file exists + if (!fileExists(iTestCasePath)) + return StringVector(); + + static const std::string logFilename = "gTestHelper.tmp"; + + std::string commandLine; + commandLine.append("cmd /c \""); + commandLine.append("\""); + commandLine.append(iTestCasePath); + commandLine.append("\""); + commandLine.append(" --gtest_list_tests"); + commandLine.append(" 2>NUL"); + commandLine.append(" 1>"); + commandLine.append("\""); + commandLine.append(logFilename); + commandLine.append("\""); + + //exec + system(commandLine.c_str()); + + if (!fileExists(logFilename.c_str())) + return StringVector(); + + //load test case list from log filename + StringVector testlist; + static const std::string disabledTestCaseHeader = " DISABLED_"; + static const std::string disabledTestSuiteHeader = "DISABLED_"; + std::string testSuiteName; + std::string testCaseName; + FILE * f = fopen(logFilename.c_str(), "r"); + if (!f) + return StringVector(); + + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + while( fgets(buffer, BUFFER_SIZE, f) ) + { + std::string line = buffer; + line.substr(0, line.size()-1); //remove CRLF at the end of the + if (subString2(line, 0, disabledTestCaseHeader.size()) == disabledTestCaseHeader) + { + //do nothing + } + else if (subString2(line, 0, 2) == " ") + { + //test case + std::string fullTestCaseName; + fullTestCaseName.append(testSuiteName); + fullTestCaseName.append(subString2(line, 2, 999)); + testlist.push_back(fullTestCaseName); + } + else + { + //test suite name + testSuiteName = ""; + if (subString2(line, 0, disabledTestSuiteHeader.size()) == disabledTestSuiteHeader) + { + //disabled test suite + } + else + { + testSuiteName = line; + } + } + } + + fclose(f); + + //delete log file + commandLine = ""; + commandLine.append("del "); + commandLine.append("\""); + commandLine.append(logFilename); + commandLine.append("\" 1>NUL 2>NUL"); + + //exec + system(commandLine.c_str()); + + return testlist; +} + + + + + + + + + + + + + + + + + + +gTestHelper::FileWrapper::FileWrapper(const char * iPath, const char * iMode) +{ + mPointer = fopen(iPath, iMode); +} +gTestHelper::FileWrapper::~FileWrapper() +{ + close(); +} +bool gTestHelper::FileWrapper::isEOF() +{ + if (mPointer == NULL) + return true; + //http://www.cplusplus.com/reference/cstdio/feof/ + return (feof(mPointer) != 0); +} +void gTestHelper::FileWrapper::close() +{ + if (mPointer) + { + fclose(mPointer); + mPointer = NULL; + } +} + +long gTestHelper::getFileSize(const char* iPath) +{ + FILE * f = fopen(iPath, "rb"); + if (!f) + return 0; + long size = getFileSize(f); + fclose(f); + return size; +} + +long gTestHelper::getFileSize(FILE * f) +{ + if (f == NULL) + return 0; + long currentPos = ftell(f); + fseek(f, 0, SEEK_END); + long size = ftell(f); + fseek(f, currentPos, SEEK_SET); + return size; +} + +bool gTestHelper::isFileEquals(const char* iFile1, const char* iFile2) +{ + std::string reason; + return isFileEquals(iFile1, iFile2, reason, 1 /*return ASAP*/ ); +} + +bool gTestHelper::isFileEquals(const char* iFile1, const char* iFile2, std::string & oReason) +{ + return isFileEquals(iFile1, iFile2, oReason, 1 /*return ASAP*/ ); +} + +bool gTestHelper::isFileEquals(const char* iFile1, const char* iFile2, std::string & oReason, size_t iMaxDifferences) +{ + //Build basic message + oReason = ""; + std::stringstream ss; + ss << "Comparing first file \"" << iFile1 << "\" with second file \"" << iFile2 << "\". "; + + FileWrapper f1(iFile1, "rb"); + if (f1.mPointer == NULL) + { + ss << "First file is not found."; + oReason = ss.str(); + return false; + } + FileWrapper f2(iFile2, "rb"); + if (f2.mPointer == NULL) + { + ss << "Second file is not found."; + oReason = ss.str(); + return false; + } + + //Compare by size + long size1 = getFileSize(f1.mPointer); + long size2 = getFileSize(f2.mPointer); + if (size1 != size2) + { + if (size1 < size2) + ss << "First file is smaller than Second file: " << size1 << " vs " << size2 << "."; + else + ss << "First file is bigger than Second file: " << size1 << " vs " << size2 << "."; + oReason = ss.str(); + return false; + } + + //Compare content + f1.close(); + f2.close(); + std::vector differences; + bool success = getFileDifferences(iFile1, iFile2, differences, iMaxDifferences+1); //search 1 more record to differentiate between exactly iMaxDifferences differences and more than iMaxDifferences differences + if (!success) + { + ss << "Unable to determine if content is identical..."; + oReason = ss.str(); + return false; + } + + if (differences.size() == 0) + { + //no diffences. Files are identicals + return true; + } + + //Build error message from differences + ss << "Content is different: "; + for(size_t i=0; i= 1) + ss << ", "; + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + sprintf(buffer, "{address %d(0x%X) is 0x%02X instead of 0x%02X}", d.offset, d.offset, d.c1, d.c2); + ss << buffer; + //ss << "{at offset " << (d.offset) << "(0x" << std::hex << (int)d.offset << ") has 0x" << std::hex << (int)d.c1 << " vs 0x" << std::hex << (int)d.c2 << "}"; + } + if (differences.size() > iMaxDifferences) + ss << ", ..."; + oReason = ss.str(); + return false; +} + +bool gTestHelper::getFileDifferences(const char* iFile1, const char* iFile2, std::vector & oDifferences, size_t iMaxDifferences) +{ + FileWrapper f1(iFile1, "rb"); + if (f1.mPointer == NULL) + return false; + FileWrapper f2(iFile2, "rb"); + if (f2.mPointer == NULL) + return false; + + //Check by size + long size1 = getFileSize(f1.mPointer); + long size2 = getFileSize(f2.mPointer); + if (size1 != size2) + { + return false; //unsupported + } + + //Compare content + static const size_t BUFFER_SIZE = 1024; + char buffer1[BUFFER_SIZE]; + char buffer2[BUFFER_SIZE]; + size_t offsetRead = 0; + + //while there is data to read in files + while( !f1.isEOF() && !f2.isEOF() ) + { + size_t readSize1 = fread(buffer1, 1, BUFFER_SIZE, f1.mPointer); + size_t readSize2 = fread(buffer2, 1, BUFFER_SIZE, f2.mPointer); + if (readSize1 != readSize2) + { + //this should not happend since both files are identical in length. + return false; //failed + } + bool contentEquals = memcmp(buffer1, buffer2, readSize1) == 0; + if (!contentEquals) + { + //current buffers are different + + //Find differences and build file diff info. + for(size_t i = 0; i < BUFFER_SIZE; i++) + { + unsigned char c1 = (unsigned char)buffer1[i]; + unsigned char c2 = (unsigned char)buffer2[i]; + if (c1 != c2) + { + FILE_DIFF d; + d.offset = offsetRead+i; + d.c1 = c1; + d.c2 = c2; + oDifferences.push_back(d); + + //check max differences found + if (oDifferences.size() == iMaxDifferences) + return true; + } + } + } + offsetRead += readSize1; + } + return true; +} + +bool initRandomProvider() +{ + srand(unsigned int(time(0))); + return true; +} + +int gTestHelper::getRandomInt() +{ + return rand(); +} + +int gTestHelper::getRandomInt(int iMin, int iMax) +{ + int range = (iMax - iMin)+1; + return iMin+int(range * rand() / (RAND_MAX + 1.0)); +} + +bool gTestHelper::findInFile(const char* iFilename, const char* iValue, int & oLine, int & oCharacter) +{ + if (!fileExists(iFilename)) + return false; + + oLine = -1; + oCharacter = -1; + + gTestHelper::StringVector lines; + bool success = getTextFileContent( iFilename, lines ); + if (!success) + return false; + + for(size_t i=0; i 1) + { + char & last = line[line.size()-1]; + if (last == '\n') + last = '\0'; + } + + oLines.push_back(line); + } + fclose(f); + return true; + } + return false; +} + +void gTestHelper::createFile(const char * iFilePath, size_t iSize) +{ + FILE * f = fopen(iFilePath, "wb"); + if (!f) + return; + for(size_t i=0; i"); + + if (symbols.size() == 0) + { + oValue = ""; + return; + } + + getRandomString(oValue, iMaxLen, symbols.c_str()); +} + +void gTestHelper::getRandomString(std::string & oValue, int iMaxLen, const char* iSymbols) +{ + std::string symbols = iSymbols; + int numSymbols = (int)symbols.size(); + + oValue.reserve(iMaxLen+1); + + while (oValue.size() < (size_t)iMaxLen) + { + //generate a random character from iSymbols + int index = getRandomInt(0, numSymbols-1); + char tmpStr[] = { iSymbols[index], 0 }; + + //add + oValue.append(tmpStr); + } +} + +bool gTestHelper::isProcessorX86() +{ + return !isProcessorX64(); +} + +bool gTestHelper::isProcessorX64() +{ +#ifdef _WIN64 + return true; +#else + return false; +#endif +} + +bool gTestHelper::isDebugCode() +{ +#ifdef _DEBUG + return true; +#else + return false; +#endif +} + +bool gTestHelper::isReleaseCode() +{ +#ifdef NDEBUG + return true; +#else + return false; +#endif +} diff --git a/tests/testAnyRtttl/gtesthelper.h b/tests/testAnyRtttl/gtesthelper.h new file mode 100644 index 0000000..42aa739 --- /dev/null +++ b/tests/testAnyRtttl/gtesthelper.h @@ -0,0 +1,237 @@ +#pragma once + +#include +#include + +class gTestHelper +{ + //------------------------ + // Singleton + //------------------------ +private: + gTestHelper(); +public: + ~gTestHelper(); + static gTestHelper & getInstance(); + +public: + //------------------------ + // Inner classes & structures + //------------------------ + + typedef std::vector StringVector; + + // + // Description: + // Wrapper class for FILE* instance. + // Which automatically closes the FILE* on wrapper's destruction + // + class FileWrapper + { + public: + FileWrapper(const char * iPath, const char * iMode); + ~FileWrapper(); + bool isEOF(); + void close(); + + FILE * mPointer; + }; + + class SymbolsFlags + { + public: + enum Flags + { + Letters=1, //lowercase letters + LETTERS=2, //uppercase letters + Numbers=4, //numbers from 0 to 9 + SpecialCharacters=8, //special characters like @ and ! + All=15, //all flags + }; + }; + + struct FILE_DIFF + { + size_t offset; //offset in files where a difference is located + unsigned char c1; //character of first file + unsigned char c2; //character of second file + }; + +public: + //------------------------ + // Methods + //------------------------ + + // + // Description: + // Returns true if a given file exists + // Arguments: + // iFilePath: The path of the file to test + // + bool fileExists(const char * iFilePath); + + // + // Description: + // Returns the size of a file. + // Arguments: + // iFilePath: The path of the file + // + long getFileSize(const char* iFilePath); + long getFileSize(FILE * iFile); + + // + // Description: + // Returns true if the content of two files is identical. + // Arguments: + // iFile1: The path of the first file. + // iFile2: The path of the second file. + // oReason: A textual reason why the files are not identical. Empty if files are identical. + // iMaxDifferences: The maximum number of textual differences. + // + bool isFileEquals(const char* iFile1, const char* iFile2); + bool isFileEquals(const char* iFile1, const char* iFile2, std::string & oReason); + bool isFileEquals(const char* iFile1, const char* iFile2, std::string & oReason, size_t iMaxDifferences); + + // + // Description: + // Returns the location of all differences in two files. + // Arguments: + // iFile1: The path of the first file. + // iFile2: The path of the second file. + // oDifferences: The list of all differences within both files. Empty if files are identical. + // iMaxDifferences: The maximum number of differences. + // + bool getFileDifferences(const char* iFile1, const char* iFile2, std::vector & oDifferences, size_t iMaxDifferences); + + // + // Description: + // Returns true if the given text is found in a file. + // Arguments: + // iFilename: The path of the search file. + // iValue: The search value. + // oLine: The line number where iValue is found. + // oCharacter: The character offset within the line. + // + bool findInFile(const char* iFilename, const char* iValue, int & oLine, int & oCharacter); + + // + // Description: + // Returns the content (each lines) of a file. + // Arguments: + // iFilename: The path of the file. + // oLines: The content of the file line by line. + // + bool getTextFileContent(const char* iFilename, gTestHelper::StringVector & oLines ); + + // + // Description: + // Creates a file of the given size. All bytes are sequential. + // Arguments: + // iFilePath: The path of the file. + // + void createFile(const char * iFilePath, size_t iSize); + + // + // Description: + // Modify a given byte with the specified value. + // Arguments: + // iFilePath: The path of the file. + // iOffset: The offset of the modified byte. + // iValue: The value of the replacement. + // + void changeFileContent(const char * iFilePath, size_t iOffset, unsigned char iValue); + + // + // Description: + // Finds the given arguments within an argument list. + // Returns true if the argument is found. Returns false otherwise. + // Arguments must be supplied with the following format: --name=value + // Arguments: + // iName: The name of the argument + // oValue: The value of the argument (without the name of the argument) + // argc: The number of arguments supplied in argv. + // argv: The argument list. + // + bool findArgument(const char * iName, std::string & oValue, int argc, char **argv); + + // + // Description: + // This function build a GTEST filter automatically based on a given positive and a negative filter. + // The result filter format is the following: + // positive[-negative] where + // [ ... ] is optionnal + // positive and negative are ':'-separated test case name lists + // Arguments: + // iPositiveFilter: positive filter + // iNegativeFilter: negative filter + // iExistingFilter: existing filter or a pre-build filter (build from a previous call to mergeFilter) + // Example: + // calling mergeFilter("", "C21XB1.testSniperA2GRanging25:C21XB1.testSniperA2GRanging40") return the filter + // "*-C21XB1.testSniperA2GRanging25:C21XB1.testSniperA2GRanging40" + // + std::string mergeFilter(const std::string & iPositiveFilter, const std::string & iNegativeFilter); + std::string mergeFilter(const std::string & iPositiveFilter, const std::string & iNegativeFilter, const char * iExistingFilter); + std::string mergeFilter(const std::string & iPositiveFilter, const std::string & iNegativeFilter, int argc, char **argv); + + // + // Description: + // This function split a GTEST filter into a positive and a negative filter. + // Notes: + // iFilter must not be NULL + // Example: + // calling splitFilter("", ..., ...) returns the "" as positive filter and "" as negative filter + // calling splitFilter("*", ..., ...) returns the "*" as positive filter and "" as negative filter + // calling splitFilter("-TestLib.testCase", ..., ...) returns the "" as positive filter and "TestLib.testCase" as negative filter + // calling splitFilter("TestFoo.testFoo-TestLib.testCase:TestMath.testMath", ..., ...) returns the "TestFoo.testFoo" as positive filter and "TestLib.testCase:TestMath.testMath" as negative filter + // + void splitFilter(const char * iFilter, std::string & oPositiveFilter, std::string & oNegativeFilter); + + // + // Description + // Returns a list of all runnable test cases in a gTest compatible test. + // Each test case is listed in the following format: testsuite.testcase + // Example: + // TestGTestHelper.testFilters + // Arguments: + // iTestCasePath: Path to the GTest compatible executable file + // + gTestHelper::StringVector getTestList(const char * iTestCasePath); + + // + // Description + // Returns a random number. + // + int getRandomInt(); + + // + // Description + // Returns a random number. + // Arguments: + // iMin: The minimum value that can be generated. + // iMax: The maximum value that can be generated. + // + int getRandomInt(int iMin, int iMax); + + // + // Description + // Returns a random string. + // Arguments: + // oValue: The generated output string if arguments are specified. + // iMaxLen: The maximum length of the generated string. + // iFlags: The flags that specify what kind of symbols is allowed for the generation of the string. + // iSymbols: The list of symbols that can be used for generating the string. + // + std::string getRandomString(); + void getRandomString(std::string & oValue, int iMaxLen); + void getRandomString(std::string & oValue, int iMaxLen, SymbolsFlags::Flags iFlags); + void getRandomString(std::string & oValue, int iMaxLen, const char* iSymbols); + + // + // Description + // Returns true if the specified processor flag is enabled + // + bool isProcessorX86(); + bool isProcessorX64(); + bool isDebugCode(); + bool isReleaseCode(); +}; diff --git a/tests/testAnyRtttl/main.cpp b/tests/testAnyRtttl/main.cpp new file mode 100644 index 0000000..7796ccc --- /dev/null +++ b/tests/testAnyRtttl/main.cpp @@ -0,0 +1,20 @@ +// testAnyRtttl.cpp : Defines the entry point for the console application. +// + +#include "targetver.h" + +#include +#include + +#include + +int main(int argc, char **argv) +{ + ::testing::GTEST_FLAG(output) = "xml:testAnyRtttl.testResults.xml"; + ::testing::GTEST_FLAG(print_time) = true; + + ::testing::InitGoogleTest(&argc, argv); + int wResult = RUN_ALL_TESTS(); //Find and run all tests + + return wResult; // returns 0 if all the tests are successful, or 1 otherwise +} diff --git a/tests/testAnyRtttl/targetver.h b/tests/testAnyRtttl/targetver.h new file mode 100644 index 0000000..6fe8eb7 --- /dev/null +++ b/tests/testAnyRtttl/targetver.h @@ -0,0 +1,13 @@ +#pragma once + +// The following macros define the minimum required platform. The minimum required platform +// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run +// your application. The macros work by enabling all features available on platform versions up to and +// including the version specified. + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. +#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. +#endif + diff --git a/tests/testAnyRtttl/testAnyRtttl.vcproj b/tests/testAnyRtttl/testAnyRtttl.vcproj new file mode 100644 index 0000000..3638ab7 --- /dev/null +++ b/tests/testAnyRtttl/testAnyRtttl.vcproj @@ -0,0 +1,273 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/testAnyRtttl/tests.cpp b/tests/testAnyRtttl/tests.cpp new file mode 100644 index 0000000..3671e0e --- /dev/null +++ b/tests/testAnyRtttl/tests.cpp @@ -0,0 +1,247 @@ +// testAnyRtttl.cpp : Defines the entry point for the console application. +// + +#include "targetver.h" + +#include +#include + +#include +#include "arduino.h" +#include "anyrtttl.h" +#include "bitreader.h" +#include "tests.h" +#include "gTestHelper.h" + +//rtttl native format +const char * tetris = "tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a"; + +//RTTTL 10 bits binary format for the following: tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a +//Compatible with AnyRtttl library v2.0 +//The code & updates for the AnyRtttl library can be found on http://end2endzone.com +const unsigned char tetris10[] = {0x0A, 0x14, 0x12, 0xCE, 0x34, 0xE0, 0x82, 0x14, 0x32, 0x38, 0xE0, 0x4C, 0x2A, 0xAD, 0x34, 0xA0, 0x84, 0x0B, 0x0E, 0x28, 0xD3, 0x4C, 0x03, 0x2A, 0x28, 0xA1, 0x80, 0x2A, 0xA5, 0xB4, 0x93, 0x82, 0x1B, 0xAA, 0x38, 0xE2, 0x86, 0x12, 0x4E, 0x38, 0xA0, 0x84, 0x0B, 0x0E, 0x28, 0xD3, 0x4C, 0x03, 0x2A, 0x28, 0xA1, 0x80, 0x2A, 0xA9, 0x04}; +const int tetris10_length = 42; + +//RTTTL 16 bits binary format for the following: tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a +//Compatible with AnyRtttl library v2.0 +//The code & updates for the AnyRtttl library can be found on http://end2endzone.com +const unsigned char tetris16[] = {0x0A, 0x14, 0x12, 0x02, 0x33, 0x01, 0x03, 0x02, 0x0B, 0x02, 0x14, 0x02, 0x0C, 0x02, 0x03, 0x02, 0x33, 0x01, 0x2A, 0x01, 0x2B, 0x01, 0x03, 0x02, 0x12, 0x02, 0x0B, 0x02, 0x03, 0x02, 0x32, 0x01, 0x33, 0x01, 0x03, 0x02, 0x0A, 0x02, 0x12, 0x02, 0x02, 0x02, 0x2A, 0x01, 0x29, 0x01, 0x3B, 0x01, 0x0A, 0x02, 0x1B, 0x02, 0x2A, 0x02, 0x23, 0x02, 0x1B, 0x02, 0x12, 0x02, 0x13, 0x02, 0x03, 0x02, 0x12, 0x02, 0x0B, 0x02, 0x03, 0x02, 0x32, 0x01, 0x33, 0x01, 0x03, 0x02, 0x0A, 0x02, 0x12, 0x02, 0x02, 0x02, 0x2A, 0x01, 0x2A, 0x01}; +const int tetris16_length = 42; + + +#define PIEZO_PIN 99 + +namespace arduino { namespace test +{ + + //bit reader support + #ifndef USE_BITADDRESS_READ_WRITE + BitReader bitreader; + #else + BitAddress bitreader; + #endif + uint16_t readNextBits(uint8_t numBits) + { + uint16_t bits = 0; + bitreader.read(numBits, &bits); + return bits; + } + + void filterToneFunctions(gTestHelper::StringVector & calls) + { + gTestHelper::StringVector copy = calls; + calls.clear(); + + for(size_t i=0; icurrent_test_info()->name()) + ".log"; + arduino_stub::setLogFile(logFile.c_str()); + + //play the actual content + anyrtttl::nonblocking::begin(PIEZO_PIN, tetris); + while( !anyrtttl::nonblocking::done() ) + { + anyrtttl::nonblocking::play(); + } + + //assert + + //load both files into memory + gTestHelper & helper = gTestHelper::getInstance(); + gTestHelper::StringVector expectedCalls; + ASSERT_TRUE (helper.getTextFileContent("expected_call_stack.log", expectedCalls)); + gTestHelper::StringVector actualCalls; + ASSERT_TRUE (helper.getTextFileContent(logFile.c_str(), actualCalls)); + + //assert that file ends with a noTone(); + std::string lastCall = actualCalls[actualCalls.size()-1]; + ASSERT_NE(lastCall.find("noTone("), std::string::npos); + + //compare tone() function calls with the "blocking" template + //file since its the only common thing betwwen blocking and + //non-blocking api. + + //filter out anything that is not tone(...) + filterToneFunctions(expectedCalls); + filterToneFunctions(actualCalls); + + ASSERT_EQ( expectedCalls.size(), actualCalls.size() ); + for(size_t i=0; icurrent_test_info()->name()) + ".log"; + arduino_stub::setLogFile(logFile.c_str()); + + anyrtttl::nonblocking::begin(PIEZO_PIN, tetris); + + //play for 5 sec then stop. + //note: this is a blocking code section + //use to demonstrate the use of stop() + unsigned long start = millis(); + while( millis() - start < 5000 ) + { + anyrtttl::nonblocking::play(); + } + anyrtttl::nonblocking::stop(); + + //assert + + //load output file into memory + gTestHelper & helper = gTestHelper::getInstance(); + gTestHelper::StringVector actualCalls; + ASSERT_TRUE (helper.getTextFileContent(logFile.c_str(), actualCalls)); + + //assert that file ends with a noTone(); + std::string lastCall = actualCalls[actualCalls.size()-1]; + ASSERT_NE(lastCall.find("noTone("), std::string::npos); + } + //-------------------------------------------------------------------------------------------------- + TEST_F(Tests, testBlockingPlay) + { + //setup log file + std::string logFile = std::string(::testing::UnitTest::GetInstance()->current_test_info()->name()) + ".log"; + arduino_stub::setLogFile(logFile.c_str()); + + //play the actual content + anyrtttl::blocking::play(PIEZO_PIN, tetris); + + //assert + gTestHelper & helper = gTestHelper::getInstance(); + std::string diffReason; + bool fileAreIdentical = helper.isFileEquals("expected_call_stack.log", logFile.c_str(), diffReason, 1); + ASSERT_TRUE( fileAreIdentical ) << diffReason.c_str(); + } + //-------------------------------------------------------------------------------------------------- + TEST_F(Tests, testBlockingPlay10) + { + //setup log file + std::string logFile = std::string(::testing::UnitTest::GetInstance()->current_test_info()->name()) + ".log"; + arduino_stub::setLogFile(logFile.c_str()); + + //play the actual content + bitreader.setBuffer(tetris10); + anyrtttl::blocking::play10Bits(PIEZO_PIN, tetris10_length, &readNextBits); + + //assert + gTestHelper & helper = gTestHelper::getInstance(); + std::string diffReason; + bool fileAreIdentical = helper.isFileEquals("expected_call_stack.log", logFile.c_str(), diffReason, 1); + ASSERT_TRUE( fileAreIdentical ) << diffReason.c_str(); + } + //-------------------------------------------------------------------------------------------------- + TEST_F(Tests, testBlockingPlay16) + { + //setup log file + std::string logFile = std::string(::testing::UnitTest::GetInstance()->current_test_info()->name()) + ".log"; + arduino_stub::setLogFile(logFile.c_str()); + + //play the actual content + anyrtttl::blocking::play16Bits(PIEZO_PIN, tetris16, tetris16_length); + + //assert + gTestHelper & helper = gTestHelper::getInstance(); + std::string diffReason; + bool fileAreIdentical = helper.isFileEquals("expected_call_stack.log", logFile.c_str(), diffReason, 1); + ASSERT_TRUE( fileAreIdentical ) << diffReason.c_str(); + } + //-------------------------------------------------------------------------------------------------- + static int toneCounts = 0; + static int noToneCounts = 0; + static int delayCounts = 0; + void myToneFunc(byte pin, uint16_t frequency, uint32_t duration) + { + toneCounts++; + } + void myNoToneFunc(byte pin) + { + noToneCounts++; + } + void myDelayFunc(uint32_t duration) + { + delayCounts++; + } + + TEST_F(Tests, testCustomFunctionsPlay) + { + //setup log file + std::string logFile = std::string(::testing::UnitTest::GetInstance()->current_test_info()->name()) + ".log"; + arduino_stub::setLogFile(logFile.c_str()); + + toneCounts = 0; + noToneCounts = 0; + delayCounts = 0; + + //use test's custom functions + anyrtttl::setToneFunction(&myToneFunc); + anyrtttl::setNoToneFunction(&myNoToneFunc); + anyrtttl::setDelayFunction(&myDelayFunc); + + //play the actual content + anyrtttl::blocking::play(PIEZO_PIN, tetris); + + //back to native arduino functions + anyrtttl::setToneFunction(&tone); + anyrtttl::setNoToneFunction(&noTone); + anyrtttl::setDelayFunction(&delay); + + //assert + ASSERT_GT( toneCounts , 0 ); + ASSERT_GT( noToneCounts, 0 ); + ASSERT_GT( delayCounts , 0 ); + } + //-------------------------------------------------------------------------------------------------- +} // End namespace test +} // End namespace arduino diff --git a/tests/testAnyRtttl/tests.h b/tests/testAnyRtttl/tests.h new file mode 100644 index 0000000..d4d113b --- /dev/null +++ b/tests/testAnyRtttl/tests.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace arduino { namespace test +{ + class Tests : public ::testing::Test + { + public: + virtual void SetUp(); + virtual void TearDown(); + }; + +} // End namespace test +} // End namespace arduino diff --git a/tests/win32arduino/LICENSE LGPL-3.0.txt b/tests/win32arduino/LICENSE LGPL-3.0.txt new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/tests/win32arduino/LICENSE LGPL-3.0.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/tests/win32arduino/arduino.cpp b/tests/win32arduino/arduino.cpp new file mode 100644 index 0000000..6281894 --- /dev/null +++ b/tests/win32arduino/arduino.cpp @@ -0,0 +1,712 @@ +// +// win32Arduino Library - v1.1 - 05/14/2016 +// Copyright (C) 2016 Antoine Beauchamp +// The code & updates for the library can be found on http://end2endzone.com +// +// AUTHOR/LICENSE: +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License (LGPL-3.0) for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// DISCLAIMER: +// This software is furnished "as is", without technical support, and with no +// warranty, express or implied, as to its usefulness for any purpose. +// +// PURPOSE: +// The win32Arduino is a win32 library that implementation of most used arduino +// functions which allows a library developer to unit test his code outside of +// the arduino platform. +// +// This library allows a windows user to easily test an arduino library using +// your testing framework of choice. For instance, the unit tests of win32Arduino +// library are executed using the Google Test framework. +// +// USAGE: +// The following instructions show how to easily test an arduino library using +// the Google Test framework. It assumes that you are already familiar with the +// test API. +// +// 1. Create an executable project and configure the main() function to launch +// Google Test's RUN_ALL_TESTS() macro. +// 2. Create a second static library project and add all the arduino files of +// the desired library you need to test. +// 3. The library files are expected to include arduino.h. Modify the project's +// Additionnal Include Directories to point to the win32Arduino library. +// +// The project should compile properly without errors or unresolved extensions +// allowing you to properly unit test each functions. +// +// HISTORY: +// 05/13/2016 v1.0 - Initial release. +// 05/14/2016 v1.1 - Implemented both simulated & realtime clock handling. The desired strategy is user selectable. +// + +#include +#include +#undef max +#undef min + +#include "arduino.h" + +namespace arduino_stub +{ + //millis() support + static clock_t app_clock_init() + { + return ::clock(); + } + static clock_t gClockAppStartTime = arduino_stub::app_clock_init(); + + static double diffclock(clock_t clockEnd,clock_t clockStart) + { + clock_t diffticks=clockEnd-clockStart; + double diffms=(diffticks)/(CLOCKS_PER_SEC/1000.0); + return diffms; + } + + //pins data + static uint8_t pinStates[256] = {0}; + + //last command support + static std::string gLogString; + const char * getLastCommand() + { + return gLogString.c_str(); + } + + //function logging support + std::string gLogFile = "arduino.log"; + std::string gPreviousLogFile; + void setLogFile(const char * iFilePath) + { + gLogFile = iFilePath; + } + void log(const char * iValue) + { + if (gLogFile == "") + { + exit(1); + } + + if (gPreviousLogFile == gLogFile) + { + //continue logging to the same file + FILE * f = fopen(gLogFile.c_str(), "a"); + fputs(iValue, f); + fclose(f); + } + else + { + //that is a new log file + FILE * f = fopen(gLogFile.c_str(), "w"); + fputs(iValue, f); + fclose(f); + } + + //remember last output file + gPreviousLogFile = gLogFile; + + //remember last command + gLogString = iValue; + } + + //clock hanlding + static int gClockStrategy = CLOCK_SIMULATION; + void setClockStrategy(int iClockStrategy) + { + gClockStrategy = iClockStrategy; + } + + int getClockStrategy() + { + return gClockStrategy; + } + +} + +void tone(byte iPin, uint16_t freq, uint32_t duration) +{ + char buffer[10240]; + sprintf(buffer, "tone(%d,%d,%d);\n", iPin, freq, duration); + arduino_stub::log(buffer); +} + +void noTone(byte iPin) +{ + char buffer[10240]; + sprintf(buffer, "noTone(%d);\n", iPin); + arduino_stub::log(buffer); +} + +void SerialPrinter::begin(uint16_t baudrate) +{ +} + +void SerialPrinter::printString(const char * iValue) +{ + printf("%s", iValue); + arduino_stub::log(iValue); +} + +void SerialPrinter::printlnString(const char * iValue) +{ + printf("%s\n",iValue); + std::string tmp = iValue; + tmp.append("\n"); + arduino_stub::log(tmp.c_str()); +} + +void SerialPrinter::println() +{ + printf("\n"); +} + +SerialPrinter Serial; + +//---------------------------------------------------------------------- + +//https://www.arduino.cc/en/Reference/HomePage + +//digital read/write +//#define HIGH 255 +//#define LOW 0 +const char * toDigitalPinString(uint8_t value) +{ + if (value == HIGH) + return "HIGH"; + else if (value == LOW) + return "LOW"; + return "EXPECTING_HIGH_OR_LOW"; +} + +//pin modes +//#define OUTPUT 0 +//#define INPUT 1 +//#define INPUT_PULLUP 2 +const char * toPinModeString(uint8_t value) +{ + if (value == OUTPUT) + return "OUTPUT"; + else if (value == INPUT) + return "INPUT"; + else if (value == INPUT_PULLUP) + return "INPUT_PULLUP"; + return "EXPECTING_OUTPUT_INPUT_OR_INPUT_PULLUP"; +} + +//shiftOut definition +//#define MSBFIRST 0 +//#define LSBFIRST 1 +const char * toBitOrderString(uint8_t value) +{ + if (value == MSBFIRST) + return "MSBFIRST"; + else if (value == LSBFIRST) + return "LSBFIRST"; + return "EXPECTING_MSBFIRST_OR_LSBFIRST"; +} + +void pinMode(uint8_t pin, uint8_t mode) +{ + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + const char * modeString = toPinModeString(mode); + sprintf(buffer, "%s(%d, %s);\n", __FUNCTION__, pin, modeString); + arduino_stub::log(buffer); +} + +void digitalWrite(uint8_t pin, uint8_t value) +{ + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + const char * digitalString = toDigitalPinString(value); + sprintf(buffer, "%s(%d, %s);\n", __FUNCTION__, pin, digitalString); + arduino_stub::log(buffer); + + //update pin state + arduino_stub::pinStates[pin] = value; +} + +uint8_t digitalRead(uint8_t pin) +{ + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + sprintf(buffer, "%s(%d);\n", __FUNCTION__, pin); + arduino_stub::log(buffer); + + //update pin state + return arduino_stub::pinStates[pin]; +} + +void analogWrite(uint8_t pin, uint8_t value) +{ + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + sprintf(buffer, "%s(%d, %d);\n", __FUNCTION__, pin, (int)value); + arduino_stub::log(buffer); + + //update pin state + arduino_stub::pinStates[pin] = value; +} + +uint16_t analogRead(uint8_t pin) +{ + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + sprintf(buffer, "%s(%d);\n", __FUNCTION__, pin); + arduino_stub::log(buffer); + + //update pin state + return arduino_stub::pinStates[pin]; +} + +void analogReadResolution(uint8_t bits) +{ + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + sprintf(buffer, "%s(%d);\n", __FUNCTION__, bits); + arduino_stub::log(buffer); +} + +void analogWriteResolution(uint8_t bits) +{ + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + sprintf(buffer, "%s(%d);\n", __FUNCTION__, bits); + arduino_stub::log(buffer); +} + +void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t data) +{ + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + sprintf(buffer, "%s(%d, %d, %s, %d);\n", __FUNCTION__, dataPin, clockPin, toBitOrderString(bitOrder), data); + arduino_stub::log(buffer); +} + +uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) +{ + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + sprintf(buffer, "%s(%d, %d, %s);\n", __FUNCTION__, dataPin, clockPin, toBitOrderString(bitOrder)); + arduino_stub::log(buffer); + + return 0; +} + +uint32_t pulseIn(uint8_t pin, uint8_t digitalState, uint32_t timeout) +{ + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + sprintf(buffer, "%s(%d, %s, %d);\n", __FUNCTION__, pin, toDigitalPinString(digitalState), timeout); + arduino_stub::log(buffer); + + return 200; //200 usec +} + +uint32_t pulseIn(uint8_t pin, uint8_t digitalState) +{ + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + sprintf(buffer, "%s(%d, %s);\n", __FUNCTION__, pin, toDigitalPinString(digitalState)); + arduino_stub::log(buffer); + + return 200; //200 usec +} + +namespace realtime +{ + +uint32_t micros() +{ + //based on millis() implementation. + + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + sprintf(buffer, "%s();\n", "micros"); + arduino_stub::log(buffer); + + //copy millis() implementation + //realtime + clock_t now = ::clock(); + double diffMs = arduino_stub::diffclock(now, arduino_stub::gClockAppStartTime); + double diffMicros = diffMs * 1000; + + static const uint32_t MAX_MICROS = (uint32_t)0xFFFFFFFF; + while(diffMicros > (double)MAX_MICROS) + { + diffMicros -= (double)(MAX_MICROS); + } + uint32_t finalMicros = (uint32_t)diffMicros; + return finalMicros; +} + +uint32_t millis() +{ + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + sprintf(buffer, "%s();\n", "millis"); + arduino_stub::log(buffer); + + //realtime + clock_t now = ::clock(); + double diffMs = arduino_stub::diffclock(now, arduino_stub::gClockAppStartTime); + uint32_t diffFinal = (uint32_t)diffMs; + return diffFinal; +} + +void delay(uint32_t value) +{ + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + sprintf(buffer, "%s(%d);\n", "delay", value); + arduino_stub::log(buffer); + + //realtime + Sleep(value); +} + +void delayMicroseconds(uint16_t value) +{ + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + sprintf(buffer, "%s();\n", "delayMicroseconds"); + arduino_stub::log(buffer); + + //realtime + uint32_t microSeconds = value; + uint32_t milliSeconds = microSeconds/1000; + if (milliSeconds == 0) + milliSeconds = 1; + Sleep(milliSeconds); +} + +}; //namespace realtime + +namespace simulation +{ + +static uint32_t gMicroResolution = 8; //8 usec resolution (increment for each calls) +static uint32_t gMicroCounter = 0; +uint32_t micros() +{ + //counter increments + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + sprintf(buffer, "%s();\n", "micros"); + arduino_stub::log(buffer); + + gMicroCounter += gMicroResolution; + return gMicroCounter; +} + +uint32_t millis() +{ + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + sprintf(buffer, "%s();\n", "millis"); + arduino_stub::log(buffer); + + //based on micro + gMicroCounter += gMicroResolution; + + uint32_t microSeconds = gMicroCounter; + uint32_t milliSeconds = microSeconds/1000; + return milliSeconds; +} + +void delay(uint32_t value) +{ + //based on millis() timing + uint32_t startTime = millis(); + uint32_t endTime = startTime + value; + while( millis() < endTime ) + { + } +} + +void delayMicroseconds(uint16_t value) +{ + //based on micros() timing + uint32_t startTime = micros(); + uint32_t endTime = startTime + value; + while( micros() < endTime ) + { + } +} + +}; //namespace simulation + +uint32_t micros() +{ + if (arduino_stub::gClockStrategy == arduino_stub::CLOCK_SIMULATION) + return simulation::micros(); + else + return realtime::micros(); +} + +uint32_t millis() +{ + if (arduino_stub::gClockStrategy == arduino_stub::CLOCK_SIMULATION) + return simulation::millis(); + else + return realtime::millis(); +} + +void delay(uint32_t value) +{ + if (arduino_stub::gClockStrategy == arduino_stub::CLOCK_SIMULATION) + return simulation::delay(value); + else + return realtime::delay(value); +} + +void delayMicroseconds(uint16_t value) +{ + if (arduino_stub::gClockStrategy == arduino_stub::CLOCK_SIMULATION) + return simulation::delayMicroseconds(value); + else + return realtime::delayMicroseconds(value); +} + +//pow(base, exponent) +//sqrt(x) + +//#define CHANGE 2 +//#define RISING 3 +//#define FALLING 4 +//typedef void (*ISR)(); +const char * toInterruptModeString(uint8_t value) +{ + if (value == HIGH) + return "HIGH"; + else if (value == LOW) + return "LOW"; + else if (value == CHANGE) + return "CHANGE"; + else if (value == RISING) + return "RISING"; + else if (value == FALLING) + return "FALLING"; + return "EXPECTING_HIGH_LOW_CHANGE_RISING_OR_FALLING"; +} + +void attachInterrupt(uint8_t pin, ISR func, uint8_t mode) +{ + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + sprintf(buffer, "%s(%d, ISR=0x%x, %s);\n", __FUNCTION__, pin, func, toInterruptModeString(mode)); + arduino_stub::log(buffer); +} + +void detachInterrupt(uint8_t pin) +{ + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + sprintf(buffer, "%s(%d);\n", __FUNCTION__, pin); + arduino_stub::log(buffer); +} + +void noInterrupts() +{ + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + sprintf(buffer, "%s();\n", __FUNCTION__); + arduino_stub::log(buffer); +} + +void interrupts() +{ + static const int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE]; + sprintf(buffer, "%s();\n", __FUNCTION__); + arduino_stub::log(buffer); +} + +/* +Characters +-isAlphaNumeric() +-isAlpha() +-isAscii() +-isWhitespace() +-isControl() +-isDigit() +-isGraph() +-isLowerCase() +-isPrintable() +-isPunct() +-isSpace() +-isUpperCase() +-isHexadecimalDigit() +*/ +bool isAlpha(int8_t value); +bool isDigit(int8_t value); +bool isAlphaNumeric(int8_t value) +{ + return isAlpha(value) || isDigit(value); +} + +bool isAlpha(int8_t value) +{ + bool alpha = value >= 'A' && value <= 'Z' || + value >= 'a' && value <= 'z'; + return alpha; +} + +bool isAscii(int8_t value) +{ + return true; +} + +bool isWhitespace(int8_t value) +{ + bool white = value == ' ' || + value == '\t' || + value == '\a' || + value == '\n'; + return white; +} + +bool isControl(int8_t value) +{ + //http://ascii.cl/control-characters.htm + return value >=0 && value <= 31; +} + +bool isDigit(int8_t value) +{ + bool digit = value >= '0' && value <= '9'; + return digit; +} + +bool isGraph(int8_t value) +{ + return false; +} + +bool isLowerCase(int8_t value) +{ + return value >= 'a' && value <= 'z'; +} + +bool isUpperCase(int8_t value); +bool isPunct(int8_t value); +bool isPrintable(int8_t value) +{ + return isAlphaNumeric(value) || + isWhitespace(value) || + isAscii(value) || + isLowerCase(value) || + isUpperCase(value) || + isPunct(value); +} + +bool isPunct(int8_t value) +{ +/* +Symbol ASCII +! 33 +" 34 +# 35 +$ 36 +% 37 +& 38 +' 39 +( 40 +) 41 +* 42 ++ 43 +, 44 +- 45 +. 46 +/ 47 +: 58 +; 59 +< 60 += 61 +> 62 +? 63 + +Symbol ASCII +@ 64 +[ 91 +\ 92 +] 93 +^ 94 +_ 95 +` 96 +{ 123 +¦ 124 +} 125 +~ 126 +0 48 +1 49 +2 50 +3 51 +4 52 +5 53 +6 54 +7 55 +8 56 +9 57 +*/ + return (value >= 33 && value <= 47) || + (value >= 58 && value <= 64) || + (value >= 91 && value <= 96) || + (value >= 123 && value <= 126); +} + +bool isSpace(int8_t value) +{ + return value == ' '; +} + +bool isUpperCase(int8_t value) +{ + return value >= 'A' && value <= 'Z'; +} + +bool isHexadecimalDigit(int8_t value) +{ + return isDigit(value) || + (value >= 'a' && value <= 'f') || + (value >= 'A' && value <= 'F') ; +} + +/* +Random Numbers +-randomSeed() +-random() +*/ +void randomSeed(int16_t value) +{ + ::srand(value); +} +void randomSeed(int32_t value) +{ + ::srand(value); +} + +int32_t random(int32_t min, int32_t max) +{ + int systemMax = RAND_MAX; + int value = rand(); //between 0 (inclusive) and RAND_MAX (exclusive). + int32_t width = max-min; + value = value%width; //from 0 (inclusive) to width (exclusive) + value += min; + return value; +} +int32_t random(int32_t max) +{ + return random(0, max); +} diff --git a/tests/win32arduino/arduino.h b/tests/win32arduino/arduino.h new file mode 100644 index 0000000..740be30 --- /dev/null +++ b/tests/win32arduino/arduino.h @@ -0,0 +1,336 @@ +// +// win32Arduino Library - v1.2 - 05/20/2016 +// Copyright (C) 2016 Antoine Beauchamp +// The code & updates for the library can be found on http://end2endzone.com +// +// AUTHOR/LICENSE: +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3.0 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License (LGPL-3.0) for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// DISCLAIMER: +// This software is furnished "as is", without technical support, and with no +// warranty, express or implied, as to its usefulness for any purpose. +// +// PURPOSE: +// The win32Arduino is a win32 library that implementation of most used arduino +// functions which allows a library developer to unit test his code outside of +// the arduino platform. +// +// This library allows a windows user to easily test an arduino library using +// your testing framework of choice. For instance, the unit tests of win32Arduino +// library are executed using the Google Test framework. +// +// USAGE: +// The following instructions show how to easily test an arduino library using +// the Google Test framework. It assumes that you are already familiar with the +// test API. +// +// 1. Create an executable project and configure the main() function to launch +// Google Test's RUN_ALL_TESTS() macro. +// 2. Create a second static library project and add all the arduino files of +// the desired library you need to test. +// 3. The library files are expected to include arduino.h. Modify the project's +// Additionnal Include Directories to point to the win32Arduino library. +// +// The project should compile properly without errors or unresolved extensions +// allowing you to properly unit test each functions. +// +// HISTORY: +// 05/13/2016 v1.0 - Initial release. +// 05/14/2016 v1.1 - Implemented both simulated & realtime clock handling. The desired strategy is user selectable. +// 05/20/2016 v1.2 - Fixed tone() signature to match arduino's IDE. +// + +#ifndef WIN32_ARDUINO_H +#define WIN32_ARDUINO_H + +#include +#include +#include + +typedef unsigned char byte; +typedef unsigned char uint8_t; +typedef char int8_t; +typedef unsigned short uint16_t; +typedef short int16_t; +typedef unsigned int uint32_t; +typedef int int32_t; +typedef unsigned __int64 uint64_t; +typedef __int64 int64_t; + +namespace arduino_stub +{ + //millis() support + static clock_t app_clock_init(); + static double diffclock(clock_t clockEnd,clock_t clockStart); + + //last command support + const char * getLastCommand(); + + //logging support + void setLogFile(const char * iFilePath); + void log(const char * iValue); + + //clock hanlding + static const int CLOCK_REALTIME = 0; + static const int CLOCK_SIMULATION = 1; + void setClockStrategy(int iClock); + int getClockStrategy(); +} + +void tone(byte iPin, uint16_t freq, uint32_t duration); +void noTone(byte iPin); + +class SerialPrinter +{ +public: + + static void begin(uint16_t baudrate); + static void printString(const char * iValue); + static void printlnString(const char * iValue); + + //Note: http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.2 + template + inline void print(const T & t, int /*iRadix*/) + { + std::stringstream out; + out << t; + printString(out.str().c_str()); + } + + template + inline void println(const T & t, int /*iRadix*/) + { + std::stringstream out; + out << t; + printlnString(out.str().c_str()); + } + + template + inline void print(const T & t) + { + std::stringstream out; + out << t; + printString(out.str().c_str()); + } + + template + inline void println(const T & t) + { + std::stringstream out; + out << t; + printlnString(out.str().c_str()); + } + + //specializations + template<> + inline void print(const uint8_t & t, int /*iRadix*/) + { + std::stringstream out; + out << (int)t; + printString(out.str().c_str()); + } + template<> + inline void println(const uint8_t & t, int /*iRadix*/) + { + std::stringstream out; + out << (int)t; + printlnString(out.str().c_str()); + } + template<> + inline void print(const uint8_t & t) + { + std::stringstream out; + out << (int)t; + printString(out.str().c_str()); + } + template<> + inline void println(const uint8_t & t) + { + std::stringstream out; + out << (int)t; + printlnString(out.str().c_str()); + } + + static void println(); +}; + +extern SerialPrinter Serial; + +//digital read/write +#define HIGH 255 +#define LOW 0 +const char * toDigitalPinString(uint8_t value); + +//pin modes +#define OUTPUT 0 +#define INPUT 1 +#define INPUT_PULLUP 2 +const char * toPinModeString(uint8_t value); + +//shiftOut definition +#define MSBFIRST 0 +#define LSBFIRST 1 +const char * toBitOrderString(uint8_t value); + +void pinMode(uint8_t pin, uint8_t mode); +void digitalWrite(uint8_t pin, uint8_t value); +uint8_t digitalRead(uint8_t pin); +void analogWrite(uint8_t pin, uint8_t value); +uint16_t analogRead(uint8_t pin); +void analogReadResolution(uint8_t bits); +void analogWriteResolution(uint8_t bits); +void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t data); +uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder); +uint32_t pulseIn(uint8_t pin, uint8_t digitalState, uint32_t timeout); +uint32_t pulseIn(uint8_t pin, uint8_t digitalState); +uint32_t micros(); +uint32_t millis(); +void delay(uint32_t value); +void delayMicroseconds(uint16_t value); + +template +T abs(T x) +{ + if (x < 0) + return -x; + else + return x; +} + +template +T max(T x, T y) +{ + if (x > y) + return x; + else + return y; +} + +template +T min(T x, T y) +{ + if (x < y) + return x; + else + return y; +} + +template +T constrain(T x, T a, T b) +{ + if (x < a) + return a; + else if (x > b) + return b; + else + return x; +} + +template +T map(T x, T in_min, T in_max, T out_min, T out_max) +{ + T value = (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; + return value; +} + +//pow(base, exponent) +//sqrt(x) + +#define CHANGE 2 +#define RISING 3 +#define FALLING 4 +typedef void (*ISR)(); +const char * toInterruptModeString(uint8_t value); +void attachInterrupt(uint8_t pin, ISR func, uint8_t mode); +void detachInterrupt(uint8_t pin); +void noInterrupts(); +void interrupts(); + +//Characters +bool isAlphaNumeric(int8_t value); +bool isAlpha(int8_t value); +bool isAscii(int8_t value); +bool isWhitespace(int8_t value); +bool isControl(int8_t value); +bool isDigit(int8_t value); +bool isGraph(int8_t value); +bool isLowerCase(int8_t value); +bool isPrintable(int8_t value); +bool isPunct(int8_t value); +bool isSpace(int8_t value); +bool isUpperCase(int8_t value); +bool isHexadecimalDigit(int8_t value); + +//Random Numbers +void randomSeed(int16_t value); +void randomSeed(int32_t value); +int32_t random(int32_t min, int32_t max); +int32_t random(int32_t max); + +//Bits and Bytes +//-lowByte() +//-highByte() +//-bitRead() +//-bitWrite() +//-bitSet() +//-bitClear() +//-bit() +template +uint8_t lowByte(T x) +{ + const uint8_t * bytes = (uint8_t*)&x; + uint8_t lowByteIndex = 0; + return bytes[lowByteIndex]; +} + +template +uint8_t highByte(T x) +{ + const uint8_t * bytes = (uint8_t*)&x; + uint8_t highByteIndex = sizeof(T)-1; + return bytes[highByteIndex]; +} + +template +T bitRead(const T & x, T n) +{ + return ( (x & (1<> n ); +} + +template +void bitWrite(T & x, T n, T b) +{ + x = (x & ~(1< +void bitSet(T & x, T n) +{ + x |= (1< +void bitClear(T & x, T n) +{ + x &= ~((T)1< +T bit(T n) +{ + return ((T)1< + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +