diff --git a/source/FirmataClient/.DS_Store b/source/FirmataClient/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..11d2d6b11c977e2840593bc3be9dad203f6047e0 Binary files /dev/null and b/source/FirmataClient/.DS_Store differ diff --git a/source/FirmataClient/FirmataClient.xcodeproj/project.pbxproj b/source/FirmataClient/FirmataClient.xcodeproj/project.pbxproj new file mode 100644 index 0000000000000000000000000000000000000000..cabd357012902c4875ca1362bccf161a609efa78 --- /dev/null +++ b/source/FirmataClient/FirmataClient.xcodeproj/project.pbxproj @@ -0,0 +1,312 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 0C7409A71FC65FD100259E45 /* maxlib.h in Headers */ = {isa = PBXBuildFile; fileRef = 0C7409A31FC65FD100259E45 /* maxlib.h */; }; + 0C7409A81FC65FD100259E45 /* firmatalib.h in Headers */ = {isa = PBXBuildFile; fileRef = 0C7409A41FC65FD100259E45 /* firmatalib.h */; }; + 0C7409A91FC65FD100259E45 /* maxlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 0C7409A51FC65FD100259E45 /* maxlib.c */; }; + 0C7409AA1FC65FD100259E45 /* firmatalib.c in Sources */ = {isa = PBXBuildFile; fileRef = 0C7409A61FC65FD100259E45 /* firmatalib.c */; }; + 0C7CC0D61F904D120011BFAE /* serial.c in Sources */ = {isa = PBXBuildFile; fileRef = 0C7CC0D21F904D120011BFAE /* serial.c */; }; + 0C7CC0D71F904D120011BFAE /* serial.h in Headers */ = {isa = PBXBuildFile; fileRef = 0C7CC0D31F904D120011BFAE /* serial.h */; }; + 0C7CC1051F9058D30011BFAE /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C7CC1041F9058D30011BFAE /* IOKit.framework */; }; + 0C7CC1071F9058E00011BFAE /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C7CC1061F9058E00011BFAE /* CoreFoundation.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 0C7409A31FC65FD100259E45 /* maxlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = maxlib.h; sourceTree = ""; }; + 0C7409A41FC65FD100259E45 /* firmatalib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = firmatalib.h; sourceTree = ""; }; + 0C7409A51FC65FD100259E45 /* maxlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = maxlib.c; sourceTree = ""; }; + 0C7409A61FC65FD100259E45 /* firmatalib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = firmatalib.c; sourceTree = ""; }; + 0C7CC0C91F904CDA0011BFAE /* libFirmataClient.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libFirmataClient.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + 0C7CC0D21F904D120011BFAE /* serial.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = serial.c; sourceTree = ""; }; + 0C7CC0D31F904D120011BFAE /* serial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = serial.h; sourceTree = ""; }; + 0C7CC1041F9058D30011BFAE /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; + 0C7CC1061F9058E00011BFAE /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 0C7CC0C61F904CDA0011BFAE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0C7CC1071F9058E00011BFAE /* CoreFoundation.framework in Frameworks */, + 0C7CC1051F9058D30011BFAE /* IOKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0C7CC0C01F904CDA0011BFAE = { + isa = PBXGroup; + children = ( + 0C7CC10C1F906E2F0011BFAE /* Test */, + 0C7CC1081F906DC90011BFAE /* Source */, + 0C7CC1091F906DEC0011BFAE /* Include */, + 0C7CC0CA1F904CDA0011BFAE /* Products */, + 0C7CC1031F9058D30011BFAE /* Frameworks */, + ); + sourceTree = ""; + }; + 0C7CC0CA1F904CDA0011BFAE /* Products */ = { + isa = PBXGroup; + children = ( + 0C7CC0C91F904CDA0011BFAE /* libFirmataClient.dylib */, + ); + name = Products; + sourceTree = ""; + }; + 0C7CC1031F9058D30011BFAE /* Frameworks */ = { + isa = PBXGroup; + children = ( + 0C7CC1061F9058E00011BFAE /* CoreFoundation.framework */, + 0C7CC1041F9058D30011BFAE /* IOKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 0C7CC1081F906DC90011BFAE /* Source */ = { + isa = PBXGroup; + children = ( + 0C7409A51FC65FD100259E45 /* maxlib.c */, + 0C7409A61FC65FD100259E45 /* firmatalib.c */, + 0C7CC0D21F904D120011BFAE /* serial.c */, + ); + name = Source; + sourceTree = ""; + }; + 0C7CC1091F906DEC0011BFAE /* Include */ = { + isa = PBXGroup; + children = ( + 0C7409A41FC65FD100259E45 /* firmatalib.h */, + 0C7409A31FC65FD100259E45 /* maxlib.h */, + 0C7CC0D31F904D120011BFAE /* serial.h */, + ); + name = Include; + sourceTree = ""; + }; + 0C7CC10C1F906E2F0011BFAE /* Test */ = { + isa = PBXGroup; + children = ( + ); + name = Test; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 0C7CC0C71F904CDA0011BFAE /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 0C7CC0D71F904D120011BFAE /* serial.h in Headers */, + 0C7409A81FC65FD100259E45 /* firmatalib.h in Headers */, + 0C7409A71FC65FD100259E45 /* maxlib.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 0C7CC0C81F904CDA0011BFAE /* FirmataClient */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0C7CC0CD1F904CDA0011BFAE /* Build configuration list for PBXNativeTarget "FirmataClient" */; + buildPhases = ( + 0C7CC0C51F904CDA0011BFAE /* Sources */, + 0C7CC0C61F904CDA0011BFAE /* Frameworks */, + 0C7CC0C71F904CDA0011BFAE /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = FirmataClient; + productName = FirmataClient; + productReference = 0C7CC0C91F904CDA0011BFAE /* libFirmataClient.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0C7CC0C11F904CDA0011BFAE /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0830; + ORGANIZATIONNAME = "Orly Natan"; + TargetAttributes = { + 0C7CC0C81F904CDA0011BFAE = { + CreatedOnToolsVersion = 8.3.3; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 0C7CC0C41F904CDA0011BFAE /* Build configuration list for PBXProject "FirmataClient" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 0C7CC0C01F904CDA0011BFAE; + productRefGroup = 0C7CC0CA1F904CDA0011BFAE /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 0C7CC0C81F904CDA0011BFAE /* FirmataClient */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 0C7CC0C51F904CDA0011BFAE /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0C7CC0D61F904D120011BFAE /* serial.c in Sources */, + 0C7409AA1FC65FD100259E45 /* firmatalib.c in Sources */, + 0C7409A91FC65FD100259E45 /* maxlib.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 0C7CC0CB1F904CDA0011BFAE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 0C7CC0CC1F904CDA0011BFAE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 0C7CC0CE1F904CDA0011BFAE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + EXECUTABLE_PREFIX = lib; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 0C7CC0CF1F904CDA0011BFAE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + EXECUTABLE_PREFIX = lib; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0C7CC0C41F904CDA0011BFAE /* Build configuration list for PBXProject "FirmataClient" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0C7CC0CB1F904CDA0011BFAE /* Debug */, + 0C7CC0CC1F904CDA0011BFAE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0C7CC0CD1F904CDA0011BFAE /* Build configuration list for PBXNativeTarget "FirmataClient" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0C7CC0CE1F904CDA0011BFAE /* Debug */, + 0C7CC0CF1F904CDA0011BFAE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0C7CC0C11F904CDA0011BFAE /* Project object */; +} diff --git a/source/FirmataClient/FirmataClient.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/source/FirmataClient/FirmataClient.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000000000000000000000000000000000..ec1e730715f4ccb180b22be9912d32fdd6f529cf --- /dev/null +++ b/source/FirmataClient/FirmataClient.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/source/FirmataClient/FirmataClient.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/UserInterfaceState.xcuserstate b/source/FirmataClient/FirmataClient.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..8635d2da5164da88c4916dff5212ac36e2343206 Binary files /dev/null and b/source/FirmataClient/FirmataClient.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/source/FirmataClient/FirmataClient.xcodeproj/xcuserdata/orly.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/source/FirmataClient/FirmataClient.xcodeproj/xcuserdata/orly.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000000000000000000000000000000000000..8b3995ec01a28fde9e2fe4414f2f9d3a0dab0728 --- /dev/null +++ b/source/FirmataClient/FirmataClient.xcodeproj/xcuserdata/orly.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + diff --git a/source/FirmataClient/FirmataClient.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/FirmataClient.xcscheme b/source/FirmataClient/FirmataClient.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/FirmataClient.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..24eb3e74b9b34f3279d6aa31a0eedc6c44183b45 --- /dev/null +++ b/source/FirmataClient/FirmataClient.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/FirmataClient.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/FirmataClient/FirmataClient.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/xcschememanagement.plist b/source/FirmataClient/FirmataClient.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000000000000000000000000000000000000..41d4ae528496d0d7daacf4c988d2e5cadc2a39d6 --- /dev/null +++ b/source/FirmataClient/FirmataClient.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + FirmataClient.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + 0C7CC0C81F904CDA0011BFAE + + primary + + + + + diff --git a/source/FirmataClient/firmatalib.c b/source/FirmataClient/firmatalib.c new file mode 100755 index 0000000000000000000000000000000000000000..7515098a3b666687849ab8044cc4a9cc210e2a67 --- /dev/null +++ b/source/FirmataClient/firmatalib.c @@ -0,0 +1,622 @@ +// © Copyright The University of New South Wales 2018 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// +// adapted from firmata_test +// https://github.com/firmata/firmata_test +// with permission to redistribute and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +#include "firmatalib.h" +#include +#include + +// Internals +void firmataclient_domessage(board_t *board); +void firmataclient_parse(board_t *board, const uint8_t *buf, long len); +int firmataclient_initdata(board_t *board); +void *firmataclient_looplistener(void* data); + + + +// Blink the on-board LED 3 times. +int firmataclient_blink(board_t *board) { + + if(board == NULL) return -1; + + // store current mode (in order to reverse it afterwards) + int current_mode = board->pin_info[LED_BUILTIN].mode; + firmataclient_changemode(board, LED_BUILTIN, MODE_OUTPUT); + + // blink 3 times + for (int i=0; i<3; ++i) { + // turn the LED on + firmataclient_senddigitalvalue(board, LED_BUILTIN, 1); + //sleep(1); // in seconds + for(long j=0; j<50000000; ++j); + // turn the LED off + firmataclient_senddigitalvalue(board, LED_BUILTIN, 0); + //sleep(1); // in seconds + for(long j=0; j<50000000; ++j); + } + + // reverse the mode change + firmataclient_changemode(board, LED_BUILTIN, current_mode); + + return 0; +} + +// Returns the Firmata version currently on the board. +char *firmataclient_getfirmware(board_t *board) { + if (board == NULL) return ""; + return board->firmata_name; +} + +// Opens a communiction port to the board. Returns 0 on success, -1 otherwise. +int firmataclient_initport(board_t *board, char *name, int baud) +{ + if (board == NULL || name == NULL) return -1; + + serial_close(board->port); + board->isBoardReady = false; + firmataclient_initdata(board); + serial_open(board->port, name); + + if(serial_open(board->port, name)) { + char *msg = board->port? board->port->error_msg : ""; + sprintf(board->err_msg, "firmataclient_initport: error opening port %s.\n%s", name, msg); + return -1; + } + + if (serial_setbaud(board->port, baud)) { + sprintf(board->err_msg, "firmataclient_initport: error setting baud rate for port %s.", name); + return -1; + } + + /* + The startup strategy is to open the port and immediately + send the REPORT_FIRMWARE message. When we receive the + firmware name reply, then we know the board is ready to + communicate. + + For boards like Arduino which use DTR to reset, they may + reboot the moment the port opens. They will not hear this + REPORT_FIRMWARE message, but when they finish booting up + they will send the firmware message. + + For boards that do not reboot when the port opens, they + will hear this REPORT_FIRMWARE request and send the + response. If this REPORT_FIRMWARE request isn't sent, + these boards will not automatically send this info. + + Arduino boards that reboot on DTR will act like a board + that does not reboot, if DTR is not raised when the + port opens. This program attempts to avoid raising + DTR on windows. (is this possible on Linux and Mac OS-X?) + + Either way, when we hear the REPORT_FIRMWARE reply, we + know the board is alive and ready to communicate. + + */ + + firmataclient_requestfirmware(board); + + sprintf(board->err_msg, ""); + return 0; +} + + +// Sends a firmware query to the board connected. +void firmataclient_requestfirmware(board_t *board) { + + if (board == NULL) return; + + uint8_t buf[3]; + buf[0] = START_SYSEX; + buf[1] = REPORT_FIRMWARE; // read firmata name & version + buf[2] = END_SYSEX; + long r = serial_write(board->port, buf, 3); + if(r > 0){ + board->tx_count += 3; + } + else { + sprintf(board->err_msg, "firmataclient_requestfirmware: error writing to board\n"); + return; + } +} + +// sends a new value update to the board. +void firmataclient_sendnewvalue(board_t *board, int pin, int newValue) +{ + if (board == NULL) return; + if (pin < 0 || pin >= TOTAL_PINS) return; + + board->pin_info[pin].value = newValue; + + if (pin <= 15 && newValue <= 16383) { + uint8_t buf[3]; + buf[0] = 0xE0 | pin; + buf[1] = newValue & 0x7F; + buf[2] = (newValue >> 7) & 0x7F; + serial_write(board->port, buf, 3); + board->tx_count += 3; + } else { + uint8_t buf[12]; + int len=4; + buf[0] = 0xF0; + buf[1] = 0x6F; + buf[2] = pin; + buf[3] = newValue & 0x7F; + if (newValue > 0x00000080) buf[len++] = (newValue >> 7) & 0x7F; + if (newValue > 0x00004000) buf[len++] = (newValue >> 14) & 0x7F; + if (newValue > 0x00200000) buf[len++] = (newValue >> 21) & 0x7F; + if (newValue > 0x10000000) buf[len++] = (newValue >> 28) & 0x7F; + buf[len++] = 0xF7; + serial_write(board->port, buf, len); + board->tx_count += len; + } +} + +// sends a new mode update to the board. +void firmataclient_changemode(board_t *board, int pin, int newMode) +{ + if (board == NULL) return; + if (pin < 0 || pin > TOTAL_PINS-1) return; + + //printf("Mode Change, pin=%d, %d\n", pin, newMode); + if (newMode != board->pin_info[pin].mode) { + // send the mode change message + uint8_t buf[4]; + buf[0] = 0xF4; + buf[1] = pin; + buf[2] = newMode; + serial_write(board->port, buf, 3); + board->tx_count += 3; + board->pin_info[pin].mode = newMode; + board->pin_info[pin].value = 0; + //firmataclient_updatestatus(board); + } +} + +// Sends a new digital value pin update to the board. +void firmataclient_senddigitalvalue(board_t *board, int pin, int newValue) +{ + if (board == NULL) return; + if (pin < 0 || pin >= TOTAL_PINS) return; + + board->pin_info[pin].value = newValue; + int port_num = pin / 8; + int port_val = 0; + for (int i=0; i<8; i++) { + int p = port_num * 8 + i; + if (board->pin_info[p].mode == MODE_OUTPUT || board->pin_info[p].mode == MODE_PULLUP) { + if (board->pin_info[p].value) port_val |= (1<> 7) & 0x7F; + serial_write(board->port, buf, 3); + board->tx_count += 3; +} + +/* +void firmataclient_setdigitalvalue(board_t *board, int pin, int newValue) +{ + if (board == NULL) return; + if (pin < 0 || pin >= TOTAL_PINS) return; + + board->pin_info[pin].value = newValue; + int port_num = pin / 8; + int port_val = 0; + for (int i=0; i<8; i++) { + int p = port_num * 8 + i; + if (board->pin_info[p].mode == MODE_OUTPUT || board->pin_info[p].mode == MODE_INPUT || board->pin_info[p].mode == MODE_PULLUP) { + if (board->pin_info[p].value) { + port_val |= (1<> 7) & 0x7F; + serial_write(board->port, buf, 3); + board->tx_count += 3; + //firmataclient_updatestatus(board); + +} +*/ + +// Returns true if the board is ready for communication. +bool firmataclient_isboardready(board_t *board) +{ + if (board == NULL) return false; + return board->isBoardReady; +} + +// Waits (blocks) until the board is ready for communication. +int firmataclient_waitForBoardReady(board_t *board) { + if (board == NULL) return -1; + + while(!board->isBoardReady) { + if (! board->isOK) return -1; + firmataclient_onidle(board); + //sleep(100); + } + return 0; +} + + +// Handles message received from board. +void firmataclient_domessage(board_t *board) +{ + if (board == NULL) return; + uint8_t cmd = (board->parse_buf[0] & 0xF0); + + // analog I/O message + if (cmd == 0xE0 && board->parse_count == 3) { //ANALOG_MESSAGE + int analog_ch = (board->parse_buf[0] & 0x0F); + int analog_val = board->parse_buf[1] | (board->parse_buf[2] << 7); + for (int pin=0; pin<128; pin++) { + if (board->pin_info[pin].analog_channel == analog_ch) { + board->pin_info[pin].value = analog_val; + //printf("pin %d is A%d = %d\n", pin, analog_ch, analog_val); //debugging + return; + } + } + return; + } + + // digital I/O message + if (cmd == 0x90 && board->parse_count == 3) { // DIGITAL_MESSAGE + int port_num = (board->parse_buf[0] & 0x0F); + int port_val = board->parse_buf[1] | (board->parse_buf[2] << 7); + int pin = port_num * 8; + //printf("pin= %d port_num = %d, port_val = %d\n", pin, port_num, port_val); //debugging + for (int mask=1; mask & 0xFF; mask <<= 1, pin++) { + if (board->pin_info[pin].mode == MODE_INPUT || board->pin_info[pin].mode == MODE_PULLUP) { + uint32_t val = (port_val & mask) ? 1 : 0; + if (board->pin_info[pin].value != val) { + //printf("pin %d is %d\n", pin, val); //debugging + board->pin_info[pin].value = val; + } + } + } + return; + } + + // Sysex message + if (board->parse_buf[0] == START_SYSEX && board->parse_buf[board->parse_count-1] == END_SYSEX) { + // Sysex message + if (board->parse_buf[1] == REPORT_FIRMWARE) { + int len=0; + for (int i=4; i < board->parse_count-2; i+=2) { + board->firmata_name[len++] = (board->parse_buf[i] & 0x7F) + | ((board->parse_buf[i+1] & 0x7F) << 7); + } + board->firmata_name[len++] = '-'; + board->firmata_name[len++] = board->parse_buf[2] + '0'; + board->firmata_name[len++] = '.'; + board->firmata_name[len++] = board->parse_buf[3] + '0'; + board->firmata_name[len++] = 0; + //printf("firmata name is %s\n", firmata_name); + + // query the board's capabilities only after hearing the + // REPORT_FIRMWARE message. For boards that reset when + // the port open (eg, Arduino with reset=DTR), they are + // not ready to communicate for some time, so the only + // way to reliably query their capabilities is to wait + // until the REPORT_FIRMWARE message is heard. + + uint8_t buf[80]; + len=0; + buf[len++] = START_SYSEX; + buf[len++] = ANALOG_MAPPING_QUERY; // read analog to pin # info + buf[len++] = END_SYSEX; + buf[len++] = START_SYSEX; + buf[len++] = CAPABILITY_QUERY; // read capabilities + buf[len++] = END_SYSEX; + for (int i=0; i<16; i++) { + buf[len++] = 0xC0 | i; // report analog REPORT_ANALOG + buf[len++] = 1; + buf[len++] = 0xD0 | i; // report digital REPORT_DIGITAL + buf[len++] = 1; + } + serial_write(board->port, buf, len); + board->tx_count += len; + board->tx_count += 3; + + } else if (board->parse_buf[1] == CAPABILITY_RESPONSE) { + int pin, i, n; + for (pin=0; pin < 128; pin++) { + board->pin_info[pin].supported_modes = 0; + } + for (i=2, n=0, pin=0; iparse_count; i++) { + if (board->parse_buf[i] == 127) { // default mode (off) + pin++; + n = 0; + continue; + } + if (n == 0) { + // first byte is supported mode + board->pin_info[pin].supported_modes |= (1<parse_buf[i]); + } + n = n ^ 1; + } + + /* commented out the state query so that the pin's initial values on board will be discarded: + // send a state query for every pin with any modes + for (pin=0; pin < 128; pin++) { + uint8_t buf[512]; + int len=0; + if (pin_info[pin].supported_modes) { + buf[len++] = START_SYSEX; + buf[len++] = PIN_STATE_QUERY; + buf[len++] = pin; + buf[len++] = END_SYSEX; + } + serial_write(buf, len); + tx_count += len; + } end commenting out */ + + // note that the board is now ready for communication + // after verfying board's pins capabilities + board->isBoardReady = true; + + } else if (board->parse_buf[1] == ANALOG_MAPPING_RESPONSE) { + int pin=0; + for (int i=2; iparse_count-1; i++) { + board->pin_info[pin].analog_channel = board->parse_buf[i]; + pin++; + } + return; + + } else if (board->parse_buf[1] == PIN_STATE_RESPONSE && board->parse_count >= 6) { + int pin = board->parse_buf[2]; + board->pin_info[pin].mode = board->parse_buf[3]; + board->pin_info[pin].value = board->parse_buf[4]; + if (board->parse_count > 6) board->pin_info[pin].value |= (board->parse_buf[5] << 7); + if (board->parse_count > 7) board->pin_info[pin].value |= (board->parse_buf[6] << 14); + //printf("pin: %d value: %u", pin, pin_info[pin].value); + //printSupportedModes(pin); + + } else if (board->parse_buf[1] == PULSE_IN && board->parse_count >= 5) { + int pin = board->parse_buf[2]; + int val = board->parse_buf[3]; + if (board->parse_count > 5) val |= (board->parse_buf[4] << 7); + if (board->parse_count > 6) val |= (board->parse_buf[5] << 14); + if (board->parse_count > 7) val |= (board->parse_buf[6] << 21); + if (board->parse_count > 8) val |= (board->parse_buf[7] << 28); + //todo? + //board->pin_info[pin].mode = PULSE_IN; + board->pin_info[pin].value = val; + } + + return; + } +} + + +void delayMicroseconds(int t); +long pulseIn(int Echo_pin, bool opp_observable_cond); + +void delayMicroseconds(int t) { } +long pulseIn(int Echo_pin, bool opp_observable_cond) { + return 0; +} + +long firmataclient_pulse_duration(board_t *board, int Trig_pin, int Echo_pin, bool observe) { // Ultrasonic::Timing() + + bool observable_cond = observe; //LOW + bool opp_observable_cond = !observe; //HIGH + + firmataclient_senddigitalvalue(board, Trig_pin, observable_cond); //LOW + delayMicroseconds(2); + firmataclient_senddigitalvalue(board, Trig_pin, opp_observable_cond); //HIGH + delayMicroseconds(10); + firmataclient_senddigitalvalue(board, Trig_pin, observable_cond); //LOW + long duration = pulseIn(Echo_pin, opp_observable_cond); //HIGH + return duration; +} + +// Parses the bytes received from serial. +void firmataclient_parse(board_t *board, const uint8_t *buf, long len) +{ + if (board == NULL) return; + + const uint8_t *p, *end; + + p = buf; + end = p + len; + for (p = buf; p < end; p++) { + uint8_t msn = *p & 0xF0; //0xF0 START_SYSEX; + if (msn == 0xE0 || msn == 0x90|| *p == 0xF9) {// 0xE0 ANALOG_MESSAGE; 0x90 DIGITAL_MESSAGE; 0xF9 REPORT_VERSION + board->parse_command_len = 3; + board->parse_count = 0; + } else if (msn == 0xC0 || msn == 0xD0) { //0xC0 REPORT_ANALOG; 0xD0 REPORT_DIGITAL + board->parse_command_len = 2; + board->parse_count = 0; + } else if (*p == START_SYSEX) { + board->parse_count = 0; + board->parse_command_len = sizeof(board->parse_buf); + } else if (*p == END_SYSEX) { + board->parse_command_len = board->parse_count + 1; + } else if (*p & 0x80) { + board->parse_command_len = 1; + board->parse_count = 0; + } + if (board->parse_count < (int)sizeof(board->parse_buf)) { + board->parse_buf[board->parse_count++] = *p; + } + if (board->parse_count == board->parse_command_len) { + firmataclient_domessage(board); + board->parse_count = board->parse_command_len = 0; + } + } +} + +// Listen for incoming bytes from serial. +void firmataclient_onidle(board_t *board) +{ + if (board == NULL) return; + + uint8_t buf[1024]; + long r; + + r = serial_inputwait(board->port, 40); + if (r > 0) { + r = serial_read(board->port, buf, sizeof(buf)); + if (r < 0) { + // error + board->isOK = false; + return; + } + if (r > 0) { + // parse + board->rx_count += r; + firmataclient_parse(board, buf, r); + //firmataclient_updatestatus(board); + } + } else if (r < 0) { + board->isOK = false; + } +} + +// Resets all fields. +int firmataclient_initdata(board_t *board) +{ + if (board == NULL) return -1; + + board->port = (serial_t *)malloc(sizeof(serial_t)); + if(board->port == NULL) { + sprintf(board->err_msg, "firmataclient_initdata: couldn't allocate new memory"); + return -1; + } + + for (int i=0; i < 128; i++) { + board->pin_info[i].mode = 255; + board->pin_info[i].analog_channel = 127; + board->pin_info[i].supported_modes = 0; + board->pin_info[i].value = 0; + } + + board->tx_count = board->rx_count = 0; + board->parse_count = 0; + board->parse_command_len = 0; + board->firmata_name[0] = '\0'; + board->isOK = true; + board->isBoardReady = false; + board->threadlistening = false; + board->err_msg[0] = '\0'; + + return 0; + +} + +int firmataclient_closeport(board_t *board) { + + if (board == NULL) return 0; + + if(firmataclient_destroylisteningthread(board)) { + char * msg = board->port? board->port->error_msg : ""; + sprintf(board->err_msg, "firmataclient_destroylisteningthread: %s", msg); + return -1; + } + + serial_close(board->port); + //free(board->port); //causing NULL pointer ERROR!!! do not free port. + // firmataclient_initdata(board); + return 0; +} + +// Reads pin values from the board. +void firmataclient_getinputvalues(board_t *board, long *indexArray, long *valuesArray, int pinsCount) { + if (board == NULL) return; + if(pinsCount >= TOTAL_PINS) return; + + for(int i=0; ipin_info[indexArray[i]].value; + } +} + +// Starts an infinite listening thread to recieve board messages. +int firmataclient_launchtlisteninghread(board_t *board) +{ + if (board == NULL) return false; + + // Create the thread using POSIX routines. + pthread_t posixThreadID; + int returnVal; + + returnVal = pthread_attr_init(&(board->attr)); + if(returnVal) { + strcpy(board->err_msg, "err in pthread_attr_init"); + return returnVal; + } + returnVal = pthread_attr_setdetachstate(&(board->attr), PTHREAD_CREATE_DETACHED); + if(returnVal) { + strcpy(board->err_msg, "err in pthread_attr_setdetachstate"); + return returnVal; + } + + // caution: listening thread expects a pointer to struct board_t as input + int threadError = pthread_create(&posixThreadID, &(board->attr), &firmataclient_looplistener, board); + if (threadError) + { + sprintf(board->err_msg, "err in pthread_create"); + return threadError; + } + + board->threadlistening = true; + return 0; + +} + +// Stops listening for incoming board messages (kill the listening thread). +int firmataclient_destroylisteningthread(board_t *board) +{ + if (board == NULL) return 0; + if( ! board->threadlistening) return 0; + + int returnVal = pthread_attr_destroy(&(board->attr)); + if (returnVal != 0) + { + sprintf(board->err_msg, "error killing listening thread"); + printf("error killing listening thread\n"); + return -1; + } + + board->threadlistening = false; + return 0; +} + +// continuously listen to incoming board messages +// and update the pin_info representation of the board pins +// caution: this thread expects a pointer to struct board_t as input +void* firmataclient_looplistener(void* data) +{ + if (data == NULL) return NULL; + + while(true) { + firmataclient_onidle((board_t *)data); + } +} + + + diff --git a/source/FirmataClient/firmatalib.h b/source/FirmataClient/firmatalib.h new file mode 100755 index 0000000000000000000000000000000000000000..e26437cab2d864e9406216430ff160e439f0e996 --- /dev/null +++ b/source/FirmataClient/firmatalib.h @@ -0,0 +1,146 @@ + +#ifndef firmatalib_h +#define firmatalib_h + +#include "serial.h" + +// Constants from the FIRMATA PROTOCOL: +// https://github.com/firmata/arduino + +#define MODE_INPUT 0x00 +#define MODE_OUTPUT 0x01 +#define MODE_ANALOG 0x02 +#define MODE_PWM 0x03 +#define MODE_SERVO 0x04 +#define MODE_SHIFT 0x05 +#define MODE_I2C 0x06 +#define MODE_ONEWIRE 0x07 +#define MODE_STEPPER 0x08 +#define MODE_ENCODER 0x09 +#define MODE_SERIAL 0x0A +#define MODE_PULLUP 0x0B +#define MODE_IGNORE 0x7F + +#define START_SYSEX 0xF0 // start a MIDI Sysex message +#define END_SYSEX 0xF7 // end a MIDI Sysex message +#define PIN_MODE_QUERY 0x72 // ask for current and supported pin modes +#define PIN_MODE_RESPONSE 0x73 // reply with current and supported pin modes +#define DIGITAL_MESSAGE 0x90 // send data for a digital port +#define ANALOG_MESSAGE 0xE0 // send data for an analog pin (or PWM) +#define REPORT_ANALOG 0xC0 // enable analog input by pin # +#define REPORT_DIGITAL 0xD0 // enable digital input by port +#define SET_PIN_MODE 0xF4 // set a pin to INPUT/OUTPUT/PWM/etc +#define REPORT_VERSION 0xF9 // report firmware version +#define SYSTEM_RESET 0xFF // reset from MIDI + +#define SERVO_CONFIG 0x70 // set max angle, minPulse, maxPulse, freq +#define STRING_DATA 0x71 // a string message with 14-bits per char +#define PULSE_IN 0x74 // send a pulse in command (34 bits) +#define SHIFT_DATA 0x75 // a bitstream to/from a shift register +#define I2C_REQUEST 0x76 // send an I2C read/write request +#define I2C_REPLY 0x77 // a reply to an I2C read request +#define I2C_CONFIG 0x78 // config I2C settings such as delay times and power pins +#define EXTENDED_ANALOG 0x6F // analog write (PWM, Servo, etc) to any pin +#define PIN_STATE_QUERY 0x6D // ask for a pin's current mode and value +#define PIN_STATE_RESPONSE 0x6E // reply with pin's current mode and value +#define CAPABILITY_QUERY 0x6B // ask for supported modes and resolution of all pins +#define CAPABILITY_RESPONSE 0x6C // reply with supported modes and resolution +#define ANALOG_MAPPING_QUERY 0x69 // ask for mapping of analog to pin numbers +#define ANALOG_MAPPING_RESPONSE 0x6A // reply with mapping info +#define REPORT_FIRMWARE 0x79 // report name and version of the firmware +#define SAMPLING_INTERVAL 0x7A // set the poll rate of the main loop +#define SYSEX_NON_REALTIME 0x7E // MIDI Reserved for non-realtime messages +#define SYSEX_REALTIME 0x7F // MIDI Reserved for realtime messages + +// Constants for the client +#define TOTAL_PINS 128 +#define BUF_SIZE 4096 +#define LED_BUILTIN 13 +#define MODE_OFF 255 + + +// this struct follows the firmata transmitted data protocol +typedef struct { + uint8_t mode; // current pin mode + uint8_t analog_channel; // corresponding analog channel on board + uint64_t supported_modes; // all possible modes for the pin + uint32_t value; // pin value +} pin_t; + + +// this struct supports the client library +typedef struct { + + // serial communication with the board + serial_t *port; + + // board pins information + pin_t pin_info[TOTAL_PINS]; + + // firmware name + char firmata_name[140]; + + // read and write counters (in bytes) + unsigned int rx_count, tx_count; + + // parser utilities + int parse_count; + int parse_command_len; + uint8_t parse_buf[BUF_SIZE]; + + // board state (for serial communication) + bool isOK; + bool isBoardReady; + + // POSIX thread - infinite listener + pthread_attr_t attr; + bool threadlistening; + + // latest error message + char err_msg[4000]; + +} board_t; + +// Blink the on-board LED 3 times. +int firmataclient_blink(board_t *board); + +// Sends a firmware query to the board connected. +void firmataclient_requestfirmware(board_t *board); + +// Opens a communiction port to the board. Returns 0 on success, -1 otherwise. +int firmataclient_initport(board_t *board, char *name, int baud); + +// Sends a new value pin update to the board. +void firmataclient_sendnewvalue(board_t *board, int pin, int newValue); + +// Sends a new mode pin update to the board. +void firmataclient_changemode(board_t *board, int pin, int newMode); + +// Sends a new digital value pin update to the board. +void firmataclient_senddigitalvalue(board_t *board, int pin, int newValue); + +// Returns true if the board is ready for communication. +bool firmataclient_isboardready(board_t *board); + +// Disconnects the board. +int firmataclient_closeport(board_t *board); + +// Waits (blocks) until the board is ready for communication. +int firmataclient_waitForBoardReady(board_t *board); + +// Reads pin values from the board. +void firmataclient_getinputvalues(board_t *board, long *indexArray, long *valuesArray, int pinsCount); + +// Returns the Firmata version uploaded to the board. +char *firmataclient_getfirmware(board_t *board); + +// Starts listening for incoming board messages (launch an infinite listening thread). +int firmataclient_launchtlisteninghread(board_t *board); + +// Listen for incoming bytes from serial. +void firmataclient_onidle(board_t *board); + +// Stops listening for incoming board messages (kill the listening thread). +int firmataclient_destroylisteningthread(board_t *board); + +#endif /* firmataclient_h */ diff --git a/source/FirmataClient/maxlib.c b/source/FirmataClient/maxlib.c new file mode 100644 index 0000000000000000000000000000000000000000..a7562a0594ab2101bb213609ceae2e225af333b9 --- /dev/null +++ b/source/FirmataClient/maxlib.c @@ -0,0 +1,259 @@ +// © Copyright The University of New South Wales 2018 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + + +#include "maxlib.h" + +void maxclient_setconfig(int *config); +void maxclient_listen(board_t *board); + +// restart listening to board after pausing (unchanged configuration) +void maxclient_restart(); + +// stop communication with the board via the serial port and disconnect +void maxclient_stop(board_t *board); + +// pause (ignore) board updates. +void maxclient_pause(); + +void maxclient_toggleListener(board_t *board); +void maxclient_listenerOn(void); +void maxclient_listenerOff(void); +void maxclient_configurePins(board_t *board, int len_config, int *pins, int *modes); + + +bool listenOn = false; + +// continuously listen to board and update info_pins values +void maxclient_listen(board_t *board) { + listenOn = true; + while(listenOn) { + firmataclient_onidle(board); + } +} + +// connect to board via a serial port, init board to requested configuration +// and start listening to updates from the board +board_t* maxclient_newboard(char *name, int baud, char *err) { + + if( !name || !err) { + err = "invalid arguments"; + return NULL; + } + + // allocate memory for new board + board_t *board = (board_t*)malloc(sizeof(board_t)); + if(board == NULL) { + strcpy(err, "firmataclient_initport: couldn't allocate new memory"); + return NULL; + } + + // open connection to the board, and request REPORT_FIRMATA + int result = firmataclient_initport(board, name, baud); + if (result) { + sprintf(err, "firmataclient_initport: couldn't open port: %s", board->err_msg); + return board; + } + + return board; +} + +// connect to board via a serial port, init board to requested configuration +// and start listening to updates from the board +int maxclient_connect(board_t *board, int len_config, int *pins, int *modes, int func) { + + if (board == NULL) return -1; + + // request firmware report + firmataclient_requestfirmware(board); + + // wait for board to be ready for communication + firmataclient_waitForBoardReady(board); + + // if it's a READ (boardin) connection, then + // launch infine listener thread + if(func == IN_BOARD) { + // start new thread for (continuous) listening to port updates + // should be *before* requesting FIRMATA_REPORT + int err = firmataclient_launchtlisteninghread(board); + if(err) { + sprintf(board->err_msg, "firmataclient_launchtlisteninghread: %s", board->err_msg); + return err; + } + } + + // now that the board is ready for communication + // set the correct pin configuration as requested (if supported), + // or default is MODE_OFF (255) + maxclient_configurePins(board, len_config, pins, modes); + + // now the max object is ready for work. + return 0; +} + +int maxclient_disconnect(board_t *board) { + + if (board == NULL) return -1; + + // for a READ (boardin) connection, kill listener thread + if(firmataclient_destroylisteningthread(board) == -1) { + sprintf(board->err_msg, "maxclient_disconnect: %s", board->err_msg); + return -1; + } + else + return 0; +} + +// update board with requested configuration +void maxclient_configurePins(board_t *board, int len_config, int *pins, int *modes) { + + // note: assumes that the board is ready for communication. + + // set the correct pin configuration as requested (if supported), + // or default is MODE_OFF (255) + if(len_config > 128) len_config = 128; + for(int i=0; i TOTAL_IN_PINS) return; + if(newVal > 180) newVal = 180; + if(newVal < 0) newVal = 0; + // update the board + firmataclient_sendnewvalue(board, pin, newVal); +} + +void maxclient_movePwm(board_t *board, int pin, int newVal) { + // check pin index + if (pin < 0 || pin > TOTAL_IN_PINS) return; + if(newVal > 255) newVal = 255; + if(newVal < 0) newVal = 0; + // update the board + firmataclient_sendnewvalue(board, pin, newVal); +} + +void maxclient_toggleDigital(board_t *board, int pin, int newVal) { + // check pin index + if (pin < 0 || pin > TOTAL_IN_PINS) return; + // update the board + firmataclient_senddigitalvalue(board, pin, newVal); +} + +void maxclient_changeMode(board_t *board, int pin, int mode) { + + // todo: check the mode is supported.. + + // update the board + firmataclient_changemode(board, pin, mode); +} + +void maxclient_toggleListener(board_t *board) { + // listenOn = !listenOn; + if(listenOn) { + //pause + listenOn = false; + } + else { + listenOn = true; + maxclient_listen(board); + } +} + +void maxclient_listenerOn(void) { + listenOn = true; +} + +void maxclient_listenerOff(void) { + listenOn = false; +} + +void maxclient_getInputValues(board_t *board, long *indexArray, long *valuesArray, int pinsCount) { + firmataclient_getinputvalues(board, indexArray, valuesArray, pinsCount); +} + +int maxclient_shutdown(board_t *board) { + return firmataclient_closeport(board); +} + +int maxclient_verifyconfig(board_t *board, int len, int *pins, int *modes) { + + if(board == NULL || pins== NULL || modes == NULL) return -1; + + // compare pins modes between the current (connected) board, and the board that wants to open a connection to the same port (reader vs. writer) + for (int i=0; ipin_info[pins[i]].mode; + if(board_mode != 255 && board_mode != modes[i]) { + sprintf(board->err_msg, "pin %d conflict between connected boards", pins[i]); + return -1; + } + } + + // no conflicts found. + return 0; +} + + +void maxclient_freemgr(board_mgr_t *mgr) { + if (!mgr) return; + mgr->board = NULL; + mgr->board_in = NULL; + mgr->board_out = NULL; + free(mgr); +} + + + + + + + + + + + + + + + + + + + + diff --git a/source/FirmataClient/maxlib.h b/source/FirmataClient/maxlib.h new file mode 100644 index 0000000000000000000000000000000000000000..316c1319721213361b9760fad2cde1f9184b378b --- /dev/null +++ b/source/FirmataClient/maxlib.h @@ -0,0 +1,63 @@ + +#ifndef maxlib_h +#define maxlib_h + +#include "firmatalib.h" + +#define TOTAL_IN_PINS 20 +#define TOTAL_OUT_PINS 12 + +#define DEFAUTLT_BAUD 57600 +#define MAX_PORTS 20 + +#define IN_BOARD 0 +#define OUT_BOARD 1 + + +typedef struct { + + // ARDUINO Uno board + board_t *board; + + // serial port full name + char *name; + + // double links to the Max interface objects + void *board_in, *board_out; + +} board_mgr_t; + +// connect to board via a serial port, init board to desired configuration +// and start listening to updates from the board. +int maxclient_connect(board_t *board, int len_config, int *pins, int *modes, int funct); + +// close communication with the board, +int maxclient_disconnect(board_t *board); + +// +int maxclient_shutdown(board_t *board); + +// reads input pin values from the board. +void maxclient_getInputValues(board_t *board, long *indexArray, long *valuesArray, int pinsCount); + +// +int maxclient_verifyconfig(board_t *board, int len, int *pins, int *modes); + +// toggle the digital pin value. +void maxclient_toggleDigital(board_t *board, int pin, int newVal); + +// update servo pin value. +// caps the values to range [0-180] degree angles. +void maxclient_moveServo(board_t *board, int pin, int newVal); + +// update pwm pin value. +void maxclient_movePwm(board_t *board, int pin, int newVal); + +// +board_t* maxclient_newboard(char *name, int baud, char *err); + +// Release resources allocated. +void maxclient_freemgr(board_mgr_t *mgr); + +#endif /* maxlib_h */ + diff --git a/source/FirmataClient/serial.c b/source/FirmataClient/serial.c new file mode 100755 index 0000000000000000000000000000000000000000..d589a91303ef631346a141fe8fcd44b40a84641d --- /dev/null +++ b/source/FirmataClient/serial.c @@ -0,0 +1,345 @@ +// © Copyright The University of New South Wales 2018 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// +// +// adapted from firmata_test project +// https://github.com/firmata/firmata_test +// with permission to redistribute and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + + +#include "serial.h" + +#include // required for: ioctl function +#include // required for: select function +#include // required for: kIOCalloutDeviceKey, kCFAllocatorDefault consts + +// Internals +void serial_inputdiscard(serial_t *port); +void serial_outputflush(serial_t *port); +int serial_setcontrol(serial_t *port, int dtr, int rts); +int macos_ports(io_iterator_t *PortIterator, char **list, int max_ports, int index); + + +int macos_ports(io_iterator_t *PortIterator, char **list, int max_ports, int index) +{ + io_object_t modemService; + CFTypeRef nameCFstring; + char s[MAXPATHLEN]; + int numPorts = index; + + if( ! PortIterator || ! list) return -1; + + + while ((modemService = IOIteratorNext(*PortIterator)) && numPorts < max_ports) { + nameCFstring = IORegistryEntryCreateCFProperty(modemService, + CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0); + if (nameCFstring) { + if (CFStringGetCString((const struct __CFString *)nameCFstring, + s, sizeof(s), kCFStringEncodingASCII)) { + list[numPorts] = (char*) malloc((MAXPATHLEN) * sizeof(char)); + strcpy(list[numPorts], s); + numPorts++; + } + CFRelease(nameCFstring); + } + IOObjectRelease(modemService); + } + return numPorts; +} + +// Return a list of all serial ports +int serial_portlist(char **list, int max_ports) +{ + int numPorts = 0; + + // adapted from SerialPortSample.c, by Apple + // http://developer.apple.com/samplecode/SerialPortSample/listing2.html + // and also testserial.c, by Keyspan + // http://www.keyspan.com/downloads-files/developer/macosx/KesypanTestSerial.c + // www.rxtx.org, src/SerialImp.c seems to be based on Keyspan's testserial.c + // neither keyspan nor rxtx properly release memory allocated. + // more documentation at: + // http://developer.apple.com/documentation/DeviceDrivers/Conceptual/WorkingWSerial/WWSerial_SerialDevs/chapter_2_section_6.html + + mach_port_t masterPort; + CFMutableDictionaryRef classesToMatch; + io_iterator_t serialPortIterator; + + if (IOMasterPort(0, &masterPort) != KERN_SUCCESS) return 0; + + // a usb-serial adaptor is usually considered a "modem", + // especially when it implements the CDC class spec + classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); + if (!classesToMatch) return 0; + CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), + CFSTR(kIOSerialBSDModemType)); + if (IOServiceGetMatchingServices(masterPort, classesToMatch, + &serialPortIterator) != KERN_SUCCESS) return 0; + numPorts += macos_ports(&serialPortIterator, list, max_ports, numPorts); + IOObjectRelease(serialPortIterator); + + /* + // but it might be considered a "rs232 port", so repeat this + // search for rs232 ports + classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); + if (!classesToMatch) return 0; + CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), + CFSTR(kIOSerialBSDRS232Type)); + if (IOServiceGetMatchingServices(masterPort, classesToMatch, + &serialPortIterator) != KERN_SUCCESS) return 0; + numPorts += macos_ports(&serialPortIterator, list, max_ports, numPorts); + IOObjectRelease(serialPortIterator); + */ + + return numPorts; +} + +int serial_isopen(serial_t *port) +{ + if(port == NULL) return 0; + return port->port_is_open; +} + +// Open a port, by name. Return 0 on success, non-zero for error +int serial_open(serial_t *port, char *name) +{ + if(port == NULL) return -1; + + if (name == NULL) { + sprintf(port->error_msg, "serial_open: No port name provided"); + return -1; + } + + serial_close(port); + + int bits = 0; + int port_fd = open(name, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (port_fd < 0) { + sprintf(port->error_msg, "serial_open: Unable to open %s, %s\n", name, strerror(errno)); + //printf("Unable to open %s, %s\n", name, strerror(errno)); + return -1; + } + if (ioctl(port_fd, TIOCEXCL) == -1) { + close(port_fd); + sprintf(port->error_msg, "serial_open: Unable to get exclussive access to port %s\n", name); + //printf("Unable to get exclussive access to port %s\n", name); + return -1; + } + + if (ioctl(port_fd, TIOCMGET, &bits) < 0) { + close(port_fd); + sprintf(port->error_msg, "serial_open: Unable to query serial port signals on %s\n", name); + //printf("Unable to query serial port signals on %s\n", name); + return -1; + } + + bits &= ~(TIOCM_DTR | TIOCM_RTS); + if (ioctl(port_fd, TIOCMSET, &bits) < 0) { + close(port_fd); + sprintf(port->error_msg, "serial_open: Unable to control serial port signals on %s\n", name); + //printf("Unable to control serial port signals on %s\n", name); + return -1; + } + if (tcgetattr(port_fd, &(port->settings_orig)) < 0) { + close(port_fd); + sprintf(port->error_msg, "serial_open: Unable to access baud rate on port %s\n", name); + //printf("Unable to access baud rate on port %s\n", name); + return -1; + } + + port->port_fd = port_fd; + memset(&(port->settings), 0, sizeof(port->settings)); + port->settings.c_cflag = CS8 | CLOCAL | CREAD | HUPCL; + port->settings.c_iflag = IGNBRK | IGNPAR; + serial_setbaud(port, port->baud_rate); + tcflush(port->port_fd, TCIFLUSH); + + strncpy(port->port_name, name, MAXPATHLEN); + port->port_is_open = 1; + + sprintf(port->error_msg, ""); + return 0; +} + +const char *serial_getname(serial_t *port) +{ + if ( ! port || ! port->port_is_open) return ""; + return port->port_name; +} + +// set the baud rate +int serial_setbaud(serial_t *port, int baud) +{ + if (port == NULL || baud <= 0) return -1; + if (!port->port_is_open) return -1; + + if (-1 == cfsetospeed(&(port->settings), (speed_t)baud)) { + sprintf(port->error_msg, "serial_setbaud: Couldn't set BAUD rate"); + return -1; + } + if (-1 == cfsetispeed(&(port->settings), (speed_t)baud)) { + sprintf(port->error_msg, "serial_setbaud: Couldn't set BAUD rate"); + return -1; + } + + if (-1 == tcsetattr(port->port_fd, TCSANOW, &(port->settings))) { + sprintf(port->error_msg, "serial_setbaud: Couldn't set BAUD rate"); + return -1; + } + + port->baud_rate = baud; + sprintf(port->error_msg, ""); + return 0; +} + +// Read from the serial port. Returns only the bytes that are +// already received, up to count. This always returns without delay, +// returning 0 if nothing has been received +long serial_read(serial_t *port, void *ptr, int count) +{ + if (port == NULL || !port->port_is_open) return -1; + if (count <= 0 || ptr == NULL) return 0; + + long n; + n = read(port->port_fd, ptr, count); + //printf("serial_read %ld bytes\n", n); + if (n < 0 && (errno == EAGAIN || errno == EINTR)) return 0; + return n; +} + +// Write to the serial port. This blocks until the data is +// sent (or in a buffer to be sent). All bytes are written, +// unless there is some sort of error. +long serial_write(serial_t *port, const void *ptr, int len) +{ + //printf("Write %d\n", len); + if (port == NULL || !port->port_is_open) return -1; + if (len == 0 || ptr == NULL) return 0; + + long n, written=0; + fd_set wfds; + struct timeval tv; + while (written < len) { + n = write(port->port_fd, (const char *)ptr + written, len - written); + if (n < 0 && (errno == EAGAIN || errno == EINTR)) n = 0; + //printf("Write, n = %d\n", n); + if (n < 0) return -1; + if (n > 0) { + written += n; + } else { + tv.tv_sec = 10; + tv.tv_usec = 0; + FD_ZERO(&wfds); + FD_SET(port->port_fd, &wfds); + n = select(port->port_fd+1, NULL, &wfds, NULL, &tv); + if (n < 0 && errno == EINTR) n = 1; + if (n <= 0) return -1; + } + } + return written; +} + +// Wait up to msec for data to become available for reading. +// return 0 if timeout, or non-zero if one or more bytes are +// received and can be read. -1 if an error occurs +int serial_inputwait(serial_t *port, int msec) +{ + if (port == NULL || !port->port_is_open) return -1; + + fd_set rfds; + struct timeval tv; + tv.tv_sec = msec / 1000; + tv.tv_usec = (msec % 1000) * 1000; + FD_ZERO(&rfds); + FD_SET(port->port_fd, &rfds); + return select(port->port_fd+1, &rfds, NULL, NULL, &tv); +} + +// Discard all received data that hasn't been read +void serial_inputdiscard(serial_t *port) +{ + if (port == NULL || !port->port_is_open) return; + tcflush(port->port_fd, TCIFLUSH); +} + +// Wait for all transmitted data with Write to actually leave the serial port +void serial_outputflush(serial_t *port) +{ + if (port == NULL || !port->port_is_open) return; + tcdrain(port->port_fd); +} + +// set DTR and RTS, 0 = low, 1=high, -1=unchanged +int serial_setcontrol(serial_t *port, int dtr, int rts) +{ + if (port == NULL || !port->port_is_open) return -1; + + // on Mac OS X, "man 4 tty" + int bits; + if (ioctl(port->port_fd, TIOCMGET, &bits) < 0) return -1; + if (dtr == 1) { + bits |= TIOCM_DTR; + } else if (dtr == 0) { + bits &= ~TIOCM_DTR; + } + if (rts == 1) { + bits |= TIOCM_RTS; + } else if (rts == 0) { + bits &= ~TIOCM_RTS; + } + if (ioctl(port->port_fd, TIOCMSET, &bits) < 0) return -1;; + + return 0; +} + +// Close the port +void serial_close(serial_t *port) +{ + if ( ! port || !port->port_is_open) return; + + serial_outputflush(port); + serial_inputdiscard(port); + tcsetattr(port->port_fd, TCSANOW, &(port->settings_orig)); + close(port->port_fd); + + port->port_is_open = 0; + port->baud_rate = 0; + sprintf(port->port_name, ""); + sprintf(port->error_msg, ""); + port->port_fd = 0; +} + + + + + + + + + + + + + + + + + + + + diff --git a/source/FirmataClient/serial.h b/source/FirmataClient/serial.h new file mode 100755 index 0000000000000000000000000000000000000000..8986839755ba28bec7429e9dabd2592278e716aa --- /dev/null +++ b/source/FirmataClient/serial.h @@ -0,0 +1,54 @@ +#ifndef serial_h +#define serial_h + +#include // required for: termios struct +#include // required for: MAXPATHLEN const +#include // required for: io_iterator_t + +#define DEBUG 0 + +typedef struct { + int port_is_open; + int baud_rate; + int port_fd; + char port_name[MAXPATHLEN]; + char error_msg[MAXPATHLEN]; + struct termios settings_orig; + struct termios settings; +} serial_t; + +// Returns 1 if port is open, 0 otherwise. +int serial_isopen(serial_t *port); + +// Opens a port, by name. Returns 0 on success, non-zero for error. +int serial_open(serial_t *port, char *name); + +// Sets the baud rate. Returns 0 on success, -1 otherwise. +int serial_setbaud(serial_t *port, int baud); + +// Reads from the serial port. Returns only the bytes that are +// already received, up to count. This always returns without delay, +// returning 0 if nothing has been received. +long serial_read(serial_t *port, void *ptr, int count); + +// Writes to the serial port. This blocks until the data is +// sent (or in a buffer to be sent). All bytes are written, +// unless there is some sort of error. +// Returns number of bytes written, or -1 otherwise. +long serial_write(serial_t *port, const void *ptr, int len); + +// Waits up to msec for data to become available for reading. +// return 0 if timeout, or non-zero if one or more bytes are +// received and can be read. -1 if an error occurs. +int serial_inputwait(serial_t *port, int msec); + +// Closes the port. +void serial_close(serial_t *port); + +// Return a list of all available serial ports. +int serial_portlist(char **list, int max_ports); + +// Returns the port name, or en empty string if err. +const char *serial_getname(serial_t *port); + +#endif /* serial_h */ diff --git a/source/Info.plist b/source/Info.plist new file mode 100755 index 0000000000000000000000000000000000000000..c40cc194a592784a4f6c41d7cbc94c48c6b1d3ba --- /dev/null +++ b/source/Info.plist @@ -0,0 +1,28 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${PRODUCT_NAME} + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + ${PRODUCT_VERSION} + CFBundleLongVersionString + ${PRODUCT_NAME} ${PRODUCT_VERSION}, Copyright 2014 Cycling '74 + CFBundlePackageType + iLaX + CFBundleShortVersionString + ${PRODUCT_VERSION} + CFBundleSignature + max2 + CFBundleVersion + ${PRODUCT_VERSION} + CSResourcesFileMapped + + + diff --git a/source/cr.boardin/.DS_Store b/source/cr.boardin/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..d3e6ed46f09e7d46f05b51924494f5d274235991 Binary files /dev/null and b/source/cr.boardin/.DS_Store differ diff --git a/source/cr.boardin/cr.boardin.c b/source/cr.boardin/cr.boardin.c new file mode 100755 index 0000000000000000000000000000000000000000..71723acd2fd9af899c291de0abc2fac05206c220 --- /dev/null +++ b/source/cr.boardin/cr.boardin.c @@ -0,0 +1,902 @@ +// © Copyright The University of New South Wales 2018 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + + +// Firmata-for-Max Library headers +#include "maxlib.h" + +// required Max core headers +#include "ext.h" // standard Max include, always required +#include "ext_obex.h" // required for new style Max object + + +////////////////////////// object struct +typedef struct boardin +{ + // the Max object itself (must be first) + t_object ob; + + // Arduino board connection + board_t *board_ob; + + // selected pins to read + void **outlets; + long pinsCount; + long pinsArray[TOTAL_IN_PINS]; + long valuesArray[TOTAL_IN_PINS]; + long modesArray[TOTAL_IN_PINS]; + int port_index; + + // boardin attributes: + t_symbol *att_portname; + t_symbol *att_baudrate; // Sets the data rate in bits per second (baud) for serial data transmission. + // For communicating with the computer, one of these rates: 300, 600, 1200, + // 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, or 115200. + long att_digitalPins[TOTAL_IN_PINS], digitalPinsCount; + long att_analogPins[TOTAL_IN_PINS], analogPinsCount; + + int isopen; + +} t_boardin; + +///////////////////////// function prototypes +//// Max standard set +void *boardin_new(t_symbol *s, long argc, t_atom *argv); +void boardin_free(t_boardin *x); +void boardin_assist(t_boardin *x, void *b, long m, long a, char *s); +void boardin_ports(t_boardin *x); +void boardin_pins(t_boardin *x); +void boardin_config(t_boardin *x); +void boardin_bang(t_boardin *x); +void boardin_test(t_boardin *x); +void boardin_open(t_boardin *x); +void boardin_close(t_boardin *x); + +//privates +void boardin_printAvailPorts(t_boardin *x); +void boardin_updatePin(t_boardin *x, int index, int val); +int boardin_parseArgs(t_boardin *x, long argc, t_atom *argv); +int boardin_getBaudRate(t_boardin *x); +bool boardin_pinNumAlreadyExists(t_boardin *x, int pin); // todo - check with boardout pins (handle duplicates)? +void boardin_init(t_boardin *x); +void boardin_connectBoard(t_boardin *x, int len, int *pins, int *modes); +int boardin_pinDigitalRange(int pin); +int boardin_pinAnalogRange(int pin); +void boardin_pinNumConvert(int pin, char *str); +void boardin_disconnect(t_boardin *x, char *err_msg); + + +//////////////////////// global class pointer variable +void *boardin_class; + +void ext_main(void *r) +{ + t_class *c; + + c = class_new("cr.boardin", (method)boardin_new, (method)boardin_free, + (long)sizeof(t_boardin), + 0L /* leave NULL!! */, A_GIMME, 0); + + /* you CAN'T call this from the patcher */ + class_addmethod(c, (method)boardin_assist, "assist", A_CANT, 0); + class_addmethod(c, (method)boardin_ports, "ports", 0); + class_addmethod(c, (method)boardin_pins, "pins", 0); + class_addmethod(c, (method)boardin_config, "config", 0); + class_addmethod(c, (method)boardin_bang, "bang", 0); + class_addmethod(c, (method)boardin_test, "test", 0); + class_addmethod(c, (method)boardin_open, "open", 0); + class_addmethod(c, (method)boardin_close, "close", 0); + + class_register(CLASS_BOX, c); /* CLASS_NOBOX */ + boardin_class = c; + + post("Arduino UNO boardin object created (Reader)."); +} + +void boardin_assist(t_boardin *x, void *b, long m, long index, char *s) +{ + int pin = x->pinsArray[index]; + char *str; + + if(pin<14) str = ""; + else { + str = "A"; + pin -= 14; + // analog pins are numbered as: + // Arduino: A0-A5 + // Firmata: (#14-#19) + } + + if (m == ASSIST_OUTLET) { // outlet messages + + switch(x->modesArray[index]) { + case MODE_INPUT: + sprintf(s, "pin %s%d mode DIGITAL values {0,1}", str, pin); + break; + case MODE_ANALOG: + sprintf(s, "pin A%d mode ANALOG range [0-1023]", pin); + break; + default: + sprintf(s, "pin %s%d is INVALID", str, pin); + } + + } + else { // inlet messages + sprintf(s, "msg inlet"); + } + +} + +void boardin_free(t_boardin *x) { + char err_msg[4000]; + boardin_disconnect(x, err_msg); + + // free allocated outlets + if (x->outlets) { + free(x->outlets); + } + + if (DEBUG) post("boardin object deleted."); //debug +} + +void *boardin_new(t_symbol *s, long argc, t_atom *argv) +{ + t_boardin *x = (t_boardin *)object_alloc(boardin_class); + + // verify memory allocated successfully + if (x == NULL ) { + post( "Max is out of memory, could not create new boardin object."); + return NULL; + } + + boardin_init(x); // todo - remove, for debug + + x->board_ob = NULL; + x->port_index = -1; + + // parse input arguments to set up the board configuration + if (boardin_parseArgs(x, argc, argv) == -1) { + //Error - Invalid arguments + object_error((t_object *)x, "Invalid args, could not create new boardin object."); + //object_free(x); + return (x); + } + + // debug: + //if (DEBUG) + // if(x->att_portname && x->att_baudrate) { + // object_post((t_object *)x, "port name: %s\t rate: %d", x->att_portname->s_name, atoi(x->att_baudrate->s_name)); + // } + + // create int outlets from right to left (highest-right to 1-left) + x->outlets = (void **)malloc(x->pinsCount*sizeof(void *)); + // verify memory allocated successfully + if (x->outlets == NULL ) { + object_error((t_object *)x, "Max is out of memory, could not create new boardin object."); + //boardin_free(x); + return (x); + } + for (int i = x->pinsCount; i > 0;) { + --i; + x->outlets[i] = intout(x); + } + + // boardin object is now all set up + return (x); +} + +int boardin_parseArgs(t_boardin *x, long argc, t_atom *argv) { + + x->port_index = -1; + + // set baud rate to default + char str[20]; + sprintf(str, "%d", DEFAUTLT_BAUD); + x->att_baudrate = gensym(str); + + // parse input arguments + int i = 0; + + // check if the first argument is the port index + if(argv->a_type == A_SYM){ + char *portCh = atom_getsym(argv)->s_name; + //object_post((t_object *)x, "port char: %c", portCh[0]); + int index = portCh[0]-'a'; + //object_post((t_object *)x, "port index: %d", index); + + if (index >= 0 && index <= 'z'-'a') { + x->port_index = index; + i = 1; + } + // check if the baud rate was entered as input + if((i)< argc && (argv+i)->a_type == A_LONG) { + long val = atom_getlong(argv+i); + sprintf(str, "%ld", val); + x->att_baudrate = gensym(str); + i = 2; + } + + } + + // Parse DIGITAL and ANALOG pins lists + bool analogFound = false, digitalFound = false, rangeFound = false; + x->pinsCount = 0; + x->analogPinsCount = 0; + x->digitalPinsCount = 0; + + for (; i < argc; i++) { + + if((argv+i)->a_type == A_SYM) { + + t_symbol *s = atom_getsym(argv+i); + + // if the symbol is digital + if( ! strcmp_case_insensitive(s->s_name, "@digital")) { + //post("Found DIGITAL"); + digitalFound = true; + analogFound = false; + rangeFound = false; + } + // if symbol is analog + else if( ! strcmp_case_insensitive(s->s_name, "@analog")) { + //post("Found ANALOG"); + analogFound = true; + digitalFound = false; + rangeFound = false; + } + // if symbol is range + else if( ! strcmp_case_insensitive(s->s_name, "@range")) { + //post("Found RANGE"); + rangeFound = true; + analogFound = false; + digitalFound = false; + } + else if( ! strcmp_case_insensitive(s->s_name, "A0")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 14; + argv[i] = a[0]; + --i; + } + else if( ! strcmp_case_insensitive(s->s_name, "A1")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 15; + argv[i] = a[0]; + --i; + } + else if( ! strcmp_case_insensitive(s->s_name, "A2")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 16; + argv[i] = a[0]; + --i; + } + else if( ! strcmp_case_insensitive(s->s_name, "A3")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 17; + argv[i] = a[0]; + --i; + } + else if( ! strcmp_case_insensitive(s->s_name, "A4")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 18; + argv[i] = a[0]; + --i; + } + else if( ! strcmp_case_insensitive(s->s_name, "A5")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 19; + argv[i] = a[0]; + --i; + } + else { + // otherwise, unknown attribute symbol + digitalFound = false; + analogFound = false; + rangeFound = false; + // error + object_error((t_object *)x, "forbidden argument %s", s->s_name); + //return -1; + } + } + + else if((argv+i)->a_type == A_LONG) { + + int pin = atom_getlong(argv+i); + + if (analogFound) { + if (!boardin_pinAnalogRange(pin)) { + // error + object_error((t_object *)x, "Invalid pin number %d, analog pins range [A0-A5]", pin); + return 0; + } + } + + if (digitalFound && !boardin_pinDigitalRange(pin)) { + // error + object_error((t_object *)x, "Invalid pin number %d, digital pins range [0-13, A0-A5]", pin); + return 0; + } + + if((analogFound || digitalFound) && x->pinsCount < TOTAL_IN_PINS) { + + // first check if the requested pin already exists + if(boardin_pinNumAlreadyExists(x, pin)) { + // error + object_error((t_object *)x, "forbidden argument, pin %d already exists", pin); + } + + // we verified the pin does not exist, add it now + else { + x->pinsArray[x->pinsCount] = pin; + x->valuesArray[x->pinsCount] = 0; + if(analogFound) { + x->modesArray[x->pinsCount] = MODE_ANALOG; + x->att_analogPins[x->analogPinsCount] = pin; + x->analogPinsCount++; + } + else if(digitalFound) { + x->modesArray[x->pinsCount] = MODE_INPUT; + x->att_digitalPins[x->digitalPinsCount] = pin; + x->digitalPinsCount++; + } + x->pinsCount++; + } + } + else { + // error + if(x->pinsCount >= TOTAL_IN_PINS) { + object_error((t_object *)x, "forbidden argument, too many pins requested"); + //return -1; + } + object_error((t_object *)x, "forbidden argument, number %d is out of context", pin); + //return -1; + } + } + + else { + // error + object_error((t_object *)x, "forbidden argument, expected format:"); + object_error((t_object *)x, "[port] [@digital pins_list] [@analog pins_list]"); + //return -1; + + } + } + + return 0; +} + +void boardin_bang(t_boardin *x){ + + // update input pin values + maxclient_getInputValues(x->board_ob, x->pinsArray, x->valuesArray, x->pinsCount); + + /* + // todo - remove (for debug only) + object_post((t_object *)x, "bang!"); + for(int i=0; ipinsCount; ++i) { + //x->valuesArray[i] = (i+1); + object_post((t_object *)x, "outlet %d=pin%d=%d", i, x->pinsArray[i], x->valuesArray[i]); + }*/ + + t_atom val; + for(int i=0; ipinsCount; ++i) { + atom_setlong(&val, x->valuesArray[i]); + outlet_int(x->outlets[i], atom_getlong(&val)); + } +} + +void boardin_ports(t_boardin *x){ + boardin_printAvailPorts(x); +} + +void boardin_config(t_boardin *x) { + + char str[4000], str_pin[10]; + long count, *arr; + + str[0] = '\0'; + + // Digital pins + count = x->digitalPinsCount; + arr = x->att_digitalPins; + if(count) { + str[0] = '\0'; + strcat(str, "Digital pins: "); + for (long i=0; ianalogPinsCount; + arr = x->att_analogPins; + if(count) { + str[0] = '\0'; + strcat(str, "Analog pins: "); + for (long i=0; iboard_ob) { + object_post((t_object *)x, "test: board is not connected."); + return; + } + + if ( ! x->board_ob->isBoardReady) { + object_post((t_object *)x, "test: board is not ready for communication."); + return; + } + + if( ! x->isopen) { + object_post((t_object *)x, "test: board is not connected."); + return; + } + object_post((t_object *)x, "test: board is connected."); + + char *firmware = firmataclient_getfirmware(x->board_ob); + object_post((t_object *)x, "test: firmware version %s.", firmware); + object_post((t_object *)x, "test: board is ready for communication."); + + object_post((t_object *)x, "test: onboard LED will now blink 3 times."); + firmataclient_blink(x->board_ob); + + object_post((t_object *)x, "test: Done."); +} + +void boardin_open(t_boardin *x){ + if (x == NULL) return; + + if(x->isopen) { + object_post((t_object *)x, "board is already connected."); + return; + } + + if ( ! x->att_baudrate) { + // set baud rate to default + char str[20]; + sprintf(str, "%d", DEFAUTLT_BAUD); + x->att_baudrate = gensym(str); + } + if(DEBUG) object_post((t_object *)x, "baud rate %s.", x->att_baudrate->s_name); + + // get updated list of available ports + char *ports_avail[MAX_PORTS]; + int num_ports = serial_portlist(ports_avail, MAX_PORTS); + + if (x->port_index == -1) { + object_error((t_object *)x, "port not selected."); + return; + } + if(DEBUG) object_post((t_object *)x, "index of port selected %d.", x->port_index); + + if (x->port_index < 0 || x->port_index >= num_ports) { + object_error((t_object *)x, "port selected is out of bounds of available ports."); + if(DEBUG) object_error((t_object *)x, "index of port selected %d.", x->port_index); + return; + } + + // select the port + x->att_portname = gensym(ports_avail[x->port_index]); + if(x->att_portname == NULL) { + if(DEBUG) object_error((t_object *)x, "port can't be found."); + return; + } + if(DEBUG) object_post((t_object *)x, "port selected %s to open", x->att_portname->s_name); // debug + + // check for other connections + if(x->att_portname->s_thing == NULL) { + + // the port is not in use by other boardin/out objects + if(DEBUG) object_post((t_object *)x, "port has no other connections, %s", x->att_portname->s_name); // debug + board_mgr_t *mgr = (board_mgr_t*)malloc(sizeof(board_mgr_t)); + if(mgr == NULL) { + object_error((t_object *)x, "port can't be opened, Max can't allocate memory."); + return; + } + mgr->board_out = NULL; + mgr->board_in = NULL; + mgr->board = NULL; + x->att_portname->s_thing = (t_object*)mgr; + } + else { + // the port is in use by another boardin/out object + if(DEBUG) object_post((t_object *)x, "OK, port has another connection, %s", x->att_portname->s_name); // debug + } + + + // setup pins configuration as requested + int len_config = x->digitalPinsCount + x->analogPinsCount; + int pins[len_config], modes[len_config]; + + int len = 0; + // digital pins + for(int i=0; idigitalPinsCount; ++i) { + pins[len] = x->att_digitalPins[i]; + modes[len] = MODE_INPUT; + len++; + } + // analog pins + for(int i=0; ianalogPinsCount; ++i) { + pins[len] = x->att_analogPins[i]; // + 14; // analog pins are numbered 14-19 corresponding to A0-A5 + modes[len] = MODE_ANALOG; + len++; + } + + //if(DEBUG) object_post((t_object *)x, "%d pins allocated", len); // debug + //if (DEBUG) { + // for(int i=0; iisopen) { + sprintf(msg, "close: board was not connected."); + return; + } + + if ( ! x->att_portname) { + sprintf(msg, "close: no port selected."); + return; + } + + if(x->att_portname->s_thing == NULL) { + sprintf(msg, "close: board was not connected."); + return; + } + + board_mgr_t *mgr = ((board_mgr_t *)(x->att_portname->s_thing)); + if(mgr->board_in == NULL) { + sprintf(msg, "close: board was not connected."); + return; + } + + int err = maxclient_disconnect(x->board_ob); // kill listener + if(err == -1) { + sprintf(msg, "close: error disconnecting"); + if (x->board_ob) asprintf(&msg, ": %s", x->board_ob->err_msg); + return; + } + + x->isopen = 0; + mgr->board_in = NULL; + object_post((t_object *)x, "close: diconnected from board."); + + if(mgr->board_out == NULL) { + + // no writer board is connected, release the port altogether + err = maxclient_shutdown(x->board_ob); // close port + if(err == -1) { + sprintf(msg, "close: error shutting serial port down"); + if (x->board_ob) asprintf(&msg, ": %s", x->board_ob->err_msg); + return; + } + + if (DEBUG) object_post((t_object *)x, "close: port %s was shut down", x->att_portname->s_name); + + x->att_portname->s_thing = NULL; + maxclient_freemgr(mgr); + } + + sprintf(msg, "close: boardin is closed."); + return; +} + +void boardin_close_orig(t_boardin *x){ + + if (x == NULL) return; + + if( ! x->isopen) { + object_post((t_object *)x, "close: board was not connected."); + return; + } + + if ( ! x->att_portname) { + object_post((t_object *)x, "close: no port selected."); + return; + } + + if(x->att_portname->s_thing == NULL) { + object_post((t_object *)x, "close: board was not connected."); + return; + } + + board_mgr_t *mgr = ((board_mgr_t *)(x->att_portname->s_thing)); + if(mgr->board_in == NULL) { + object_post((t_object *)x, "close: board was not connected."); + return; + } + + int err = maxclient_disconnect(x->board_ob); // kill listener + if(err == -1) { + object_error((t_object *)x, "close: error disconnecting"); + if (x->board_ob) object_error((t_object *)x, "%s", x->board_ob->err_msg); + return; + } + + x->isopen = 0; + mgr->board_in = NULL; + object_post((t_object *)x, "close: diconnected from board."); + + if(mgr->board_out == NULL) { + + // no writer board is connected, release the port altogether + err = maxclient_shutdown(x->board_ob); // close port + if(err == -1) { + object_error((t_object *)x, "close: error shutting serial port down"); + if (x->board_ob) object_error((t_object *)x, "%s", x->board_ob->err_msg); + return; + } + + if (DEBUG) object_post((t_object *)x, "close: port %s was shut down", x->att_portname->s_name); + + x->att_portname->s_thing = NULL; + maxclient_freemgr(mgr); + } + + return; +} + +void boardin_printAvailPorts(t_boardin *x) { + + char *ports_avail[MAX_PORTS]; + + int num_ports = serial_portlist(ports_avail, MAX_PORTS); + + for(int i=0; idigitalPinsCount; ++i) { + if(x->att_digitalPins[i] == pin) + return true; + } + + for (int i = 0; ianalogPinsCount; ++i) { + if(x->att_analogPins[i] == pin) + return true; + } + + return false; +} + +void boardin_init(t_boardin *x) { + + if(x == NULL) + return; + + x->outlets = NULL; + x->pinsCount = 0; + x->att_portname = NULL; + x->att_baudrate = NULL; + x->digitalPinsCount = 0; + x->analogPinsCount = 0; + x->isopen = 0; +} + +void boardin_connectBoard(t_boardin *x, int len, int *pins, int *modes) { + + if(!x || !x->att_portname || !x->att_portname->s_thing) return; + if( !pins || !modes) return; + + + // search symbol port_name. if found, then board is already connected to by a boardout + // object (writer object), then get the pointer to that board object. Otherwise, create + // a new board object and initialise it with the requested baud rate and the rest of + // the pins (read) configuration. + + board_mgr_t *mgr = ((board_mgr_t *)(x->att_portname->s_thing)); + if (mgr == NULL) { + if (DEBUG) object_error((t_object *)x, "can't connect to port, internal error"); + return; + } + + if(mgr->board_in != NULL) { + // error - there is already another boardin object connected + object_error((t_object *)x, "open: port %s is already occupied with another boardin (reader) object!", x->att_portname->s_name); + return; + } + + else if(mgr->board_out != NULL) { + + if (DEBUG) object_post((t_object *)x, "open: port %s is connected to a Writer (okay)", x->att_portname->s_name); // debug + + if(mgr->board == NULL) { + object_error((t_object *)x, "open: internal error. board can't be reached on %s.", x->att_portname->s_name); + return; + } + + // check if there is any configuration conflict + if(mgr->board == NULL) { + object_error((t_object *)x, "open: can't connect the board. internal error"); + return; + } + + // check baud rate conflict (between existing and the new board) + //if(x->att_baudrate) { + // long current_rate = atol(x->att_baudrate->s_name); + // if(mgr->board->port) { + // long port_rate = mgr->board->port->baud_rate; + // if(port_rate != current_rate) { + // object_error((t_object *)x, "open: can't connect the board, baud rate conflict. "); + // object_error((t_object *)x, "port baud rate %ld, but baud %ld requested. ", mgr->board->port->baud_rate, current_rate); + // return; + // } + // } + //} + + // check if there is any configuration conflict + if (maxclient_verifyconfig(mgr->board, len, pins, modes)) { + object_error((t_object *)x, "open: can't connect the board. "); + object_error((t_object *)x, "%s", mgr->board->err_msg); + return; + } + + x->board_ob = mgr->board; + mgr->board_in = x; + } + + else { + + // requested port is not connected to by any other board object, then + // create a new Arduino board object, initialise and connect to (serial) port + + if (DEBUG) object_post((t_object *)x, "open: port is not connected to any other board yet, establishing a new connection."); // debug + + char err_return[4000]; + long rate = x->att_baudrate? atol(x->att_baudrate->s_name) : DEFAUTLT_BAUD; + x->board_ob = maxclient_newboard(x->att_portname->s_name, rate, err_return); + if(x->board_ob == NULL) { + // error - there is already another boardin object connected + object_error((t_object *)x, "open: Max could not create new boardin object."); + object_error((t_object *)x, "%s", err_return); + return; + } + // debug + //else { + //if (DEBUG) post("maxclient_newboard succeedded"); + //} + + mgr->board = x->board_ob; + mgr->board_in = x; + } + + // start a new listener thread and + // set board configuration (i.e., pin modes) as requested + int err = maxclient_connect(x->board_ob, len, pins, modes, IN_BOARD); + if(err) { + object_error((t_object *)x, "open: Boardin couldn't connect to board."); + if(x->board_ob) object_error((t_object *)x, "%s", x->board_ob->err_msg); + return; + } + + // debug + //if (DEBUG) object_post((t_object *)x, "Listening thread started."); + + + x->isopen = 1; + object_post((t_object *)x, "open: boardin is now connected."); +} + +int boardin_pinDigitalRange(int pin) { + return ((pin < 20 && pin >=0)? 1 : 0); +} + +int boardin_pinAnalogRange(int pin) { + // Analog pins range: 14 [A0] .. 19 [A5] + return ((pin < 20 && pin >13)? 1 : 0); +} + +void boardin_pinNumConvert(int pin, char *str) { + if( ! str) { + str = ""; + return; + } + if(pin < 14) sprintf(str, "%d", pin); + else sprintf(str, "A%d", pin-14); + return; +} + + +// +// The Arduino board contains a 6 channel, 10-bit analog to digital converter. +// This means that it will map input voltages between 0 and 5 volts into integer values between 0 and 1023. +// 10 bits, pow(2, 10)=1024. +// + + + + + + + + diff --git a/source/cr.boardin/cr.boardin.xcodeproj/project.pbxproj b/source/cr.boardin/cr.boardin.xcodeproj/project.pbxproj new file mode 100755 index 0000000000000000000000000000000000000000..c1849b11a89e87749713a0e49623b507051e26af --- /dev/null +++ b/source/cr.boardin/cr.boardin.xcodeproj/project.pbxproj @@ -0,0 +1,233 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 0C3533DA1F87027000671127 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C3533D91F87027000671127 /* IOKit.framework */; }; + 0C3533DC1F87027E00671127 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C3533DB1F87027E00671127 /* CoreFoundation.framework */; }; + 0CDDA8BF2022D13700358057 /* firmatalib.h in Headers */ = {isa = PBXBuildFile; fileRef = 0CDDA8B92022D13700358057 /* firmatalib.h */; }; + 0CDDA8C02022D13700358057 /* firmatalib.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CDDA8BA2022D13700358057 /* firmatalib.c */; }; + 0CDDA8C12022D13700358057 /* maxlib.h in Headers */ = {isa = PBXBuildFile; fileRef = 0CDDA8BB2022D13700358057 /* maxlib.h */; }; + 0CDDA8C22022D13700358057 /* serial.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CDDA8BC2022D13700358057 /* serial.c */; }; + 0CDDA8C32022D13700358057 /* serial.h in Headers */ = {isa = PBXBuildFile; fileRef = 0CDDA8BD2022D13700358057 /* serial.h */; }; + 0CDDA8C42022D13700358057 /* maxlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CDDA8BE2022D13700358057 /* maxlib.c */; }; + 22CF11AE0EE9A8840054F513 /* cr.boardin.c in Sources */ = {isa = PBXBuildFile; fileRef = 22CF11AD0EE9A8840054F513 /* cr.boardin.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 0C3533D91F87027000671127 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; + 0C3533DB1F87027E00671127 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + 0CDDA8B92022D13700358057 /* firmatalib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = firmatalib.h; path = ../FirmataClient/firmatalib.h; sourceTree = ""; }; + 0CDDA8BA2022D13700358057 /* firmatalib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = firmatalib.c; path = ../FirmataClient/firmatalib.c; sourceTree = ""; }; + 0CDDA8BB2022D13700358057 /* maxlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = maxlib.h; path = ../FirmataClient/maxlib.h; sourceTree = ""; }; + 0CDDA8BC2022D13700358057 /* serial.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = serial.c; path = ../FirmataClient/serial.c; sourceTree = ""; }; + 0CDDA8BD2022D13700358057 /* serial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = serial.h; path = ../FirmataClient/serial.h; sourceTree = ""; }; + 0CDDA8BE2022D13700358057 /* maxlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = maxlib.c; path = ../FirmataClient/maxlib.c; sourceTree = ""; }; + 22CF10220EE984600054F513 /* maxmspsdk.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = maxmspsdk.xcconfig; path = ../maxmspsdk.xcconfig; sourceTree = SOURCE_ROOT; }; + 22CF11AD0EE9A8840054F513 /* cr.boardin.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cr.boardin.c; sourceTree = ""; }; + 2FBBEAE508F335360078DB84 /* cr.boardin.mxo */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = cr.boardin.mxo; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 2FBBEADC08F335360078DB84 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0C3533DC1F87027E00671127 /* CoreFoundation.framework in Frameworks */, + 0C3533DA1F87027000671127 /* IOKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 089C166AFE841209C02AAC07 /* iterator */ = { + isa = PBXGroup; + children = ( + 0CDDA8BD2022D13700358057 /* serial.h */, + 0CDDA8BC2022D13700358057 /* serial.c */, + 0CDDA8B92022D13700358057 /* firmatalib.h */, + 0CDDA8BA2022D13700358057 /* firmatalib.c */, + 0CDDA8BB2022D13700358057 /* maxlib.h */, + 0CDDA8BE2022D13700358057 /* maxlib.c */, + 22CF11AD0EE9A8840054F513 /* cr.boardin.c */, + 22CF10220EE984600054F513 /* maxmspsdk.xcconfig */, + 19C28FB4FE9D528D11CA2CBB /* Products */, + 0C3533D81F87027000671127 /* Frameworks */, + ); + name = iterator; + sourceTree = ""; + }; + 0C3533D81F87027000671127 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 0C3533DB1F87027E00671127 /* CoreFoundation.framework */, + 0C3533D91F87027000671127 /* IOKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 19C28FB4FE9D528D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 2FBBEAE508F335360078DB84 /* cr.boardin.mxo */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 2FBBEAD708F335360078DB84 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 0CDDA8BF2022D13700358057 /* firmatalib.h in Headers */, + 0CDDA8C32022D13700358057 /* serial.h in Headers */, + 0CDDA8C12022D13700358057 /* maxlib.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 2FBBEAD608F335360078DB84 /* max-external */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2FBBEAE008F335360078DB84 /* Build configuration list for PBXNativeTarget "max-external" */; + buildPhases = ( + 2FBBEAD708F335360078DB84 /* Headers */, + 2FBBEAD808F335360078DB84 /* Resources */, + 2FBBEADA08F335360078DB84 /* Sources */, + 2FBBEADC08F335360078DB84 /* Frameworks */, + 2FBBEADF08F335360078DB84 /* Rez */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "max-external"; + productName = iterator; + productReference = 2FBBEAE508F335360078DB84 /* cr.boardin.mxo */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 089C1669FE841209C02AAC07 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0830; + }; + buildConfigurationList = 2FBBEACF08F335010078DB84 /* Build configuration list for PBXProject "cr.boardin" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + en, + ); + mainGroup = 089C166AFE841209C02AAC07 /* iterator */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 2FBBEAD608F335360078DB84 /* max-external */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 2FBBEAD808F335360078DB84 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXRezBuildPhase section */ + 2FBBEADF08F335360078DB84 /* Rez */ = { + isa = PBXRezBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXRezBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 2FBBEADA08F335360078DB84 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0CDDA8C22022D13700358057 /* serial.c in Sources */, + 0CDDA8C02022D13700358057 /* firmatalib.c in Sources */, + 0CDDA8C42022D13700358057 /* maxlib.c in Sources */, + 22CF11AE0EE9A8840054F513 /* cr.boardin.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 2FBBEAD008F335010078DB84 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Development; + }; + 2FBBEAD108F335010078DB84 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Deployment; + }; + 2FBBEAE108F335360078DB84 /* Development */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 22CF10220EE984600054F513 /* maxmspsdk.xcconfig */; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_OPTIMIZATION_LEVEL = 0; + OTHER_LDFLAGS = "$(C74_SYM_LINKER_FLAGS)"; + PRODUCT_NAME = cr.boardin; + }; + name = Development; + }; + 2FBBEAE208F335360078DB84 /* Deployment */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 22CF10220EE984600054F513 /* maxmspsdk.xcconfig */; + buildSettings = { + COPY_PHASE_STRIP = YES; + OTHER_LDFLAGS = "$(C74_SYM_LINKER_FLAGS)"; + PRODUCT_NAME = cr.boardin; + }; + name = Deployment; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 2FBBEACF08F335010078DB84 /* Build configuration list for PBXProject "cr.boardin" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2FBBEAD008F335010078DB84 /* Development */, + 2FBBEAD108F335010078DB84 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 2FBBEAE008F335360078DB84 /* Build configuration list for PBXNativeTarget "max-external" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2FBBEAE108F335360078DB84 /* Development */, + 2FBBEAE208F335360078DB84 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; +/* End XCConfigurationList section */ + }; + rootObject = 089C1669FE841209C02AAC07 /* Project object */; +} diff --git a/source/cr.boardin/cr.boardin.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/source/cr.boardin/cr.boardin.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000000000000000000000000000000000..299f9e0e1aa3bb32096a2fa3421f527116a82a99 --- /dev/null +++ b/source/cr.boardin/cr.boardin.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/source/cr.boardin/cr.boardin.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/UserInterfaceState.xcuserstate b/source/cr.boardin/cr.boardin.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..d6e3079adfc5f8ccd3a5e0151597bc03ce92e6a5 Binary files /dev/null and b/source/cr.boardin/cr.boardin.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/source/cr.boardin/cr.boardin.xcodeproj/xcuserdata/orly.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/source/cr.boardin/cr.boardin.xcodeproj/xcuserdata/orly.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000000000000000000000000000000000000..e9cc5e8d62a9c1cc563a44560c5ca7a96ad0c3ce --- /dev/null +++ b/source/cr.boardin/cr.boardin.xcodeproj/xcuserdata/orly.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/cr.boardin/cr.boardin.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/max-external.xcscheme b/source/cr.boardin/cr.boardin.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/max-external.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..c7baf67fe39414570ca69d6d922c2614a4d57902 --- /dev/null +++ b/source/cr.boardin/cr.boardin.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/max-external.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/cr.boardin/cr.boardin.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/xcschememanagement.plist b/source/cr.boardin/cr.boardin.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000000000000000000000000000000000000..858be9e656792446b7768c572d07b59603ac475c --- /dev/null +++ b/source/cr.boardin/cr.boardin.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + max-external.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + 2FBBEAD608F335360078DB84 + + primary + + + + + diff --git a/source/cr.boardout/.DS_Store b/source/cr.boardout/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..4592977f7e926c844dd01f09cd3cd53d4df1cd94 Binary files /dev/null and b/source/cr.boardout/.DS_Store differ diff --git a/source/cr.boardout/cr.boardout.c b/source/cr.boardout/cr.boardout.c new file mode 100755 index 0000000000000000000000000000000000000000..33df304deef1d261ff5936c5bde46a8d230918fb --- /dev/null +++ b/source/cr.boardout/cr.boardout.c @@ -0,0 +1,1054 @@ +// © Copyright The University of New South Wales 2018 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/. +// + + +// Firmata-for-Max Library headers +#include "maxlib.h" + +// required Max core headers +#include "ext.h" // standard Max include, always required +#include "ext_obex.h" // required for new style Max object + + +////////////////////////// object struct +typedef struct boardout +{ + // the Max object itself (must be first) + t_object ob; + + // Arduino board connection + board_t *board_ob; + + // selected pins to write + long pinsCount; + long pinsArray[TOTAL_OUT_PINS]; + long valuesArray[TOTAL_OUT_PINS]; + long modesArray[TOTAL_OUT_PINS]; + int port_index; + + // boardin attributes: + t_symbol *att_portname; + t_symbol *att_baudrate; // Sets the data rate in bits per second (baud) for serial data transmission. + // For communicating with the computer, one of these rates: 300, 600, 1200, + // 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, or 115200. + long att_digitalPins[TOTAL_OUT_PINS], digitalPinsCount; + long att_servoPins[TOTAL_OUT_PINS], servoPinsCount; + long att_pwmPins[TOTAL_OUT_PINS], pwmPinsCount; + + int isopen; + + + // hardware attributes: + //t_symbol *att_pin_modes[TOTAL_OUT_PINS]; + +} t_boardout; + +///////////////////////// function prototypes +//// standard set +void *boardout_new(t_symbol *s, long argc, t_atom *argv); +void boardout_free(t_boardout *x); +void boardout_assist(t_boardout *x, void *b, long m, long a, char *s); +void boardout_ports(t_boardout *x); +void boardout_pins(t_boardout *x); +void boardout_config(t_boardout *x); +void boardout_test(t_boardout *x); +void boardout_open(t_boardout *x); +void boardout_close(t_boardout *x); + +//privates +void boardout_printAvailPorts(t_boardout *x); +void boardout_updatePin(t_boardout *x, int index, int val); +void boardout_updateServo(t_boardout *x, int index, int val); +void boardout_updateDigital(t_boardout *x, int index, int val); +void boardout_updatePwm(t_boardout *x, int index, int val); +int boardout_parseArgs(t_boardout *x, long argc, t_atom *argv); +int boardout_getBaudRate(t_boardout *x); +bool boardout_pinNumAlreadyExists(t_boardout *x, int pin); +int boardout_pinDigitalRange(int pin); +int boardout_pinServoRange(int pin); +int boardout_pinPwmRange(int pin); +int boardout_getBoard(t_boardout *x, int len, int *pins, int *modes); +void boardout_init(t_boardout *x); +void boardout_pinNumConvert(int pin, char *str); +void boardout_disconnect(t_boardout *x, char *msg); + +void boardout_in1(t_boardout *x, long n); +void boardout_in2(t_boardout *x, long n); +void boardout_in3(t_boardout *x, long n); +void boardout_in4(t_boardout *x, long n); +void boardout_in5(t_boardout *x, long n); +void boardout_in6(t_boardout *x, long n); +void boardout_in7(t_boardout *x, long n); +void boardout_in8(t_boardout *x, long n); +void boardout_in9(t_boardout *x, long n); +void boardout_in10(t_boardout *x, long n); +void boardout_in11(t_boardout *x, long n); +void boardout_in12(t_boardout *x, long n); + +//////////////////////// global class pointer variable +void *boardout_class; + +void ext_main(void *r) +{ + t_class *c; + + c = class_new("cr.boardout", (method)boardout_new, (method)boardout_free, + (long)sizeof(t_boardout), + 0L /* leave NULL!! */, A_GIMME, 0); + + /* you CAN'T call this from the patcher */ + class_addmethod(c, (method)boardout_assist, "assist", A_CANT, 0); + class_addmethod(c, (method)boardout_ports, "ports", 0); + class_addmethod(c, (method)boardout_pins, "pins", 0); + class_addmethod(c, (method)boardout_config, "config", 0); + class_addmethod(c, (method)boardout_test, "test", 0); + class_addmethod(c, (method)boardout_open, "open", 0); + class_addmethod(c, (method)boardout_close, "close", 0); + + class_addmethod(c, (method)boardout_in1, "in1", A_LONG, 0); + class_addmethod(c, (method)boardout_in2, "in2", A_LONG, 0); + class_addmethod(c, (method)boardout_in3, "in3", A_LONG, 0); + class_addmethod(c, (method)boardout_in4, "in4", A_LONG, 0); + class_addmethod(c, (method)boardout_in5, "in5", A_LONG, 0); + class_addmethod(c, (method)boardout_in6, "in6", A_LONG, 0); + class_addmethod(c, (method)boardout_in7, "in7", A_LONG, 0); + class_addmethod(c, (method)boardout_in8, "in8", A_LONG, 0); + class_addmethod(c, (method)boardout_in9, "in9", A_LONG, 0); + class_addmethod(c, (method)boardout_in10, "in10", A_LONG, 0); + class_addmethod(c, (method)boardout_in11, "in11", A_LONG, 0); + class_addmethod(c, (method)boardout_in12, "in12", A_LONG, 0); + + class_register(CLASS_BOX, c); /* CLASS_NOBOX */ + boardout_class = c; + + post("Arduino UNO boardout object created (Writer)."); + +} + +void boardout_assist(t_boardout *x, void *b, long m, long a, char *s) +{ + if (m == ASSIST_INLET) { // inlet + + if(a == 0) { + sprintf(s, "inlet %ld - no pins", a); + } + else { + + char *str; + + // pin index is inlet-1 + int index = a-1; + int pin = x->pinsArray[index]; + + if(pin<14) str = ""; + else { + str = "A"; + pin -= 14; + } + + switch(x->modesArray[index]) { + + case MODE_OUTPUT: + sprintf(s, "pin %s%d mode DIGITAL values {0,1}", str, pin); + break; + case MODE_SERVO: + sprintf(s, "pin %s%d mode SERVO range [0-180] degrees", str, pin); + break; + case MODE_PWM: + sprintf(s, "pin D%d mode PWM range [0-255]", pin); + break; + default: + sprintf(s, "pin %s%d mode UNKNOWN", str, pin); + } + } + } + else { // outlet + sprintf(s, "I am outlet %ld", a); + } +} + +void boardout_free(t_boardout *x) +{ + char msg[4000]; + boardout_disconnect(x, msg); + if (DEBUG) post("boardout object deleted"); //debug + +} + +void *boardout_new(t_symbol *s, long argc, t_atom *argv) +{ + t_boardout *x = (t_boardout *)object_alloc(boardout_class); + + // verify memory allocated successfully + if(x == NULL) { + post( "Max is out of memory, could not create new boardout object."); + return NULL; + } + + boardout_init(x); // todo - remove, for debug + + x->board_ob = NULL; + x->port_index = -1; + + // parse input arguments to set up object + if (boardout_parseArgs(x, argc, argv) == -1) { + //Error - Invalid arguments + object_error((t_object *)x, "Boardout args are invalid, could not create new boardout object."); + //object_free(x); + return (x); + } + + + /* init all pin modes to DIGITAL MODE at first + for (int i = 0; i < TOTAL_OUT_PINS; ++i) { + x->att_pin_modes[i] = gensym("DIGITAL"); + }*/ + + // debug: + //if(x->att_portname && x->att_baudrate) { + // object_post((t_object *)x, "port name: %s\t rate: %d", x->att_portname->s_name, atoi(x->att_baudrate->s_name)); + //} + + // create pin integer inlets + for (int i = x->pinsCount; i > 0; --i) { + // create int inlets from right to left (highest-right to 1-left): + intin(x, i); + } + + // boardout object is now all set up + return (x); +} + +void boardout_ports(t_boardout *x){ + boardout_printAvailPorts(x); +} + +void boardout_config(t_boardout *x) { + + char str[4000], str_pin[10]; + long count, *arr; + + str[0] = '\0'; + + // Digital pins + count = x->digitalPinsCount; + arr = x->att_digitalPins; + if(count) { + str[0] = '\0'; + strcat(str, "Digital pins: "); + for (long i=0; iservoPinsCount; + arr = x->att_servoPins; + if(count) { + str[0] = '\0'; + strcat(str, "Servo pins: "); + for (long i=0; ipwmPinsCount; + arr = x->att_pwmPins; + if(count) { + str[0] = '\0'; + strcat(str, "PWM pins: "); + for (long i=0; iboard_ob) { + object_post((t_object *)x, "test: board is not connected."); + return; + } + + if ( ! x->board_ob->isBoardReady) { + object_post((t_object *)x, "test: board is not ready for communication."); + return; + } + + if( ! x->isopen) { + object_post((t_object *)x, "test: board is not connected."); + return; + } + object_post((t_object *)x, "test: board is connected."); + + char *firmware = firmataclient_getfirmware(x->board_ob); + object_post((t_object *)x, "test: firmware version %s.", firmware); + object_post((t_object *)x, "test: board is ready for communication."); + + object_post((t_object *)x, "test: onboard LED will now blink 3 times."); + firmataclient_blink(x->board_ob); + + object_post((t_object *)x, "test: Done."); +} + +void boardout_open(t_boardout *x){ + if (x == NULL) return; + + if(x->isopen) { + object_post((t_object *)x, "board is already connected."); + return; + } + + if ( ! x->att_baudrate) { + // set baud rate to default + char str[20]; + sprintf(str, "%d", DEFAUTLT_BAUD); + x->att_baudrate = gensym(str); + } + if(DEBUG) object_post((t_object *)x, "baud rate %s.", x->att_baudrate->s_name); + + // get updated list of available ports + char *ports_avail[MAX_PORTS]; + int num_ports = serial_portlist(ports_avail, MAX_PORTS); + + if (x->port_index == -1) { + object_error((t_object *)x, "port not selected."); + return; + } + if(DEBUG) object_post((t_object *)x, "index of port selected %d.", x->port_index); + + if (x->port_index < 0 || x->port_index >= num_ports) { + object_error((t_object *)x, "port selected is out of bounds of available ports."); + if(DEBUG) object_error((t_object *)x, "index of port selected %d.", x->port_index); + return; + } + + // select the port + x->att_portname = gensym(ports_avail[x->port_index]); + if(x->att_portname == NULL) { + if(DEBUG) object_error((t_object *)x, "port can't be found, internal error."); + return; + } + if(DEBUG) object_post((t_object *)x, "port selected %s to open", x->att_portname->s_name); // debug + + // check for other connections + if(x->att_portname->s_thing == NULL) { + + // the port is not in use by other boardin/out objects + if(DEBUG) object_post((t_object *)x, "port has no other connections, %s", x->att_portname->s_name); // debug + board_mgr_t *mgr = (board_mgr_t*)malloc(sizeof(board_mgr_t)); + if(mgr == NULL) { + object_error((t_object *)x, "port can't be opened, Max can't allocate memory."); + return; + } + mgr->board_out = NULL; + mgr->board_in = NULL; + mgr->board = NULL; + x->att_portname->s_thing = (t_object*)mgr; + } + else { + // the port is in use by another boardin/out object + if(DEBUG) object_post((t_object *)x, "OK, port has another connection, %s", x->att_portname->s_name); // debug + } + + + // set board configuration + int len_config = x->digitalPinsCount + x->servoPinsCount + x->pwmPinsCount; + int pins[len_config]; + int modes[len_config]; + + int len = 0; + // digital pins + for(int i=0; idigitalPinsCount; ++i) { + pins[len] = x->att_digitalPins[i]; + modes[len] = MODE_OUTPUT; + len++; + } + // servo pins + for(int i=0; iservoPinsCount; ++i) { + pins[len] = x->att_servoPins[i]; + modes[len] = MODE_SERVO; + len++; + } + // pwm pins + for(int i=0; ipwmPinsCount; ++i) { + pins[len] = x->att_pwmPins[i]; + modes[len] = MODE_PWM; + len++; + } + + //if(DEBUG) object_post((t_object *)x, "%d pins allocated", len); // debug + //if (DEBUG) { + // for(int i=0; iboard_ob, len_config, pins, modes, OUT_BOARD); + if( err ) { + object_error((t_object *)x, "open: Boardout couldn't connect to board."); + if(x->board_ob) object_error((t_object *)x, "%s", x->board_ob->err_msg); + return; + } + + x->isopen = 1; + object_post((t_object *)x, "open: boardout is now connected."); +} + +void boardout_close_orig(t_boardout *x) { + + if(x == NULL) return; + + if( ! x->isopen) { + object_post((t_object *)x, "close: board was not connected."); + return; + } + + if ( ! x->att_portname) { + object_post((t_object *)x, "close: no port selected."); + return; + } + + if(x->att_portname->s_thing == NULL) { + object_post((t_object *)x, "close: board was not connected."); + return; + } + + board_mgr_t *mgr = ((board_mgr_t *)(x->att_portname->s_thing)); + if(mgr->board_out == NULL) { + object_post((t_object *)x, "close: board was not connected."); + return; + } + + x->isopen = 0; + mgr->board_out = NULL; + object_post((t_object *)x, "close: diconnected from board."); + + + if(mgr->board_in == NULL) { + + // no reader board is connected, release the port altogether + int err = maxclient_shutdown(x->board_ob); // close port + if(err == -1) { + object_error((t_object *)x, "close: error shutting serial port down"); + if (x->board_ob) object_error((t_object *)x, "%s", x->board_ob->err_msg); + return; + } + + if (DEBUG) object_post((t_object *)x, "close: port %s was shut down", x->att_portname->s_name); + + x->att_portname->s_thing = NULL; + maxclient_freemgr(mgr); + } + + return; +} + +void boardout_close(t_boardout *x) { + char msg[4000]; + boardout_disconnect(x, msg); + object_post((t_object *)x, "%s", msg); +} + +void boardout_disconnect(t_boardout *x, char *msg) { + + if( !x || ! x->isopen) { + sprintf(msg, "close: board was not connected."); + return; + } + + if ( ! x->att_portname) { + sprintf(msg, "close: no port selected."); + return; + } + + if(x->att_portname->s_thing == NULL) { + sprintf(msg, "close: board was not connected."); + return; + } + + board_mgr_t *mgr = ((board_mgr_t *)(x->att_portname->s_thing)); + if(mgr->board_out == NULL) { + sprintf(msg, "close: board was not connected."); + return; + } + + x->isopen = 0; + mgr->board_out = NULL; + + + if(mgr->board_in == NULL) { + + // no reader board is connected, release the port altogether + int err = maxclient_shutdown(x->board_ob); // close port + if(err == -1) { + sprintf(msg, "close: error shutting serial port down"); + if (x->board_ob) asprintf(&msg, "%s", x->board_ob->err_msg); + return; + } + + if (DEBUG) object_post((t_object *)x, "close: port %s was shut down", x->att_portname->s_name); + + x->att_portname->s_thing = NULL; + maxclient_freemgr(mgr); + } + + sprintf(msg, "close: boardout is closed."); + return; +} + + +void boardout_updatePin(t_boardout *x, int inlet, int val) { + + char *str; + + // pin index is inlet-1 + int index = inlet-1; + + switch(x->modesArray[index]) { + case MODE_OUTPUT: + boardout_updateDigital(x, index, val); + str = "DIGITAL"; + break; + case MODE_SERVO: + boardout_updateServo(x, index, val); + str = "SERVO"; + break; + case MODE_PWM: + boardout_updatePwm(x, index, val); + str = "PWM"; + break; + default: + str = "UNKNOWN"; + post("unidentified mode selected"); // error + } + + if (DEBUG) object_post((t_object *)x, "%s pin %ld: %ld", str, x->pinsArray[index], x->valuesArray[index]); //debug + +} + +void boardout_updateDigital(t_boardout *x, int index, int val) { + val = (val==0? 0 : 1); + x->valuesArray[index] = val; + int pin = x->pinsArray[index]; + maxclient_toggleDigital(x->board_ob, pin, val); +} + +void boardout_updateServo(t_boardout *x, int index, int val) { + x->valuesArray[index] = val; + int pin = x->pinsArray[index]; + maxclient_moveServo(x->board_ob, pin, val); +} + +void boardout_updatePwm(t_boardout *x, int index, int val) { + x->valuesArray[index] = val; + int pin = x->pinsArray[index]; + maxclient_movePwm(x->board_ob, pin, val); +} + + +/* + PRIVATE INTERNAL FUNCTIONS + */ + +int boardout_parseArgs(t_boardout *x, long argc, t_atom *argv) +{ + + x->port_index = -1; + + // set baud rate to default + char str[20]; + sprintf(str, "%d", DEFAUTLT_BAUD); + x->att_baudrate = gensym(str); + + // parse input arguments + int i = 0; + + // check if the first argument is the port index + if(argv->a_type == A_SYM){ + char *portCh = atom_getsym(argv)->s_name; + //object_post((t_object *)x, "port char: %c", portCh[0]); //debug + int index = portCh[0]-'a'; + //object_post((t_object *)x, "port index: %d", index); //debug + + if (index >= 0 && index <= 'z'-'a') { + x->port_index = index; + i = 1; + } + } + + // Parse DIGITAL SERVO PWM pins lists + bool servoFound = false, digitalFound = false, pwmFound = false; + x->pinsCount = 0; + x->pwmPinsCount = 0; + x->servoPinsCount = 0; + x->digitalPinsCount = 0; + + for (; i < argc; i++) { + + if((argv+i)->a_type == A_SYM) { + + t_symbol *s = atom_getsym(argv+i); + + // if the symbol is digital + if( ! strcmp_case_insensitive(s->s_name, "@digital")) { + //post("Found DIGITAL"); + digitalFound = true; + servoFound = false; + pwmFound = false; + } + // if symbol is servo + else if( ! strcmp_case_insensitive(s->s_name, "@servo")) { + //post("Found SERVO"); + servoFound = true; + digitalFound = false; + pwmFound = false; + } + // if symbol is pwm + else if( ! strcmp_case_insensitive(s->s_name, "@pwm")) { + //post("Found PWM"); + pwmFound = true; + servoFound = false; + digitalFound = false; + } + // if symbol is baud + else if( ! strcmp_case_insensitive(s->s_name, "@baud")) { + //post("Found BAUD"); + digitalFound = false; + servoFound = false; + pwmFound = false; + // check if the baud rate was entered as input + if((argv+i+1)->a_type == A_LONG) { + // move to the next input atom for the baud rate + i++; + //x->baudrate = atom_getlong(argv+i); + } + } + else if( ! strcmp_case_insensitive(s->s_name, "A0")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 14; + argv[i] = a[0]; + --i; + } + else if( ! strcmp_case_insensitive(s->s_name, "A1")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 15; + argv[i] = a[0]; + --i; + } + else if( ! strcmp_case_insensitive(s->s_name, "A2")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 16; + argv[i] = a[0]; + --i; + } + else if( ! strcmp_case_insensitive(s->s_name, "A3")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 17; + argv[i] = a[0]; + --i; + } + else if( ! strcmp_case_insensitive(s->s_name, "A4")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 18; + argv[i] = a[0]; + --i; + } + else if( ! strcmp_case_insensitive(s->s_name, "A5")) { + t_atom *a = (t_atom*)malloc(sizeof(t_atom)); + if(a == NULL) return -1; + a->a_type = A_LONG; + a->a_w.w_long = 19; + argv[i] = a[0]; + --i; + } + else { + // otherwise, unknown attribute symbol + digitalFound = false; + servoFound = false; + pwmFound = false; + // error + object_error((t_object *)x, "forbidden argument %s", s->s_name); + //return -1; + } + } + + else if((argv+i)->a_type == A_LONG) { + + int pin = atom_getlong(argv+i); + + if((servoFound || digitalFound || pwmFound) && x->pinsCount < TOTAL_OUT_PINS) { + + if(servoFound) { + if( ! boardout_pinServoRange(pin)) { + // error + object_error((t_object *)x, "Invalid pin number %d, servo pins range [2-13, A0-A5]", pin); + return 0; + } + } + + if(pwmFound && !boardout_pinPwmRange(pin)) { + // error + object_error((t_object *)x, "Invalid pin number %d, pwm pins range [3,5,6,9,10,11]", pin); + return 0; + } + + if(digitalFound && !boardout_pinDigitalRange(pin)) { + // error + object_error((t_object *)x, "Invalid pin number %d, digital pins range [0-13, A0-A5]", pin); + return 0; + } + + // also check if the requested pin already exists + if(boardout_pinNumAlreadyExists(x, pin)) { + // error + object_error((t_object *)x, "forbidden argument, pin %d already exists", pin); + } + + // we verified the pin does not exist, add it now + else { + x->pinsArray[x->pinsCount] = pin; + x->valuesArray[x->pinsCount] = 0; + if(servoFound) { + x->modesArray[x->pinsCount] = MODE_SERVO; + x->att_servoPins[x->servoPinsCount] = pin; + x->servoPinsCount++; + } + else if(digitalFound) { + x->modesArray[x->pinsCount] = MODE_OUTPUT; + x->att_digitalPins[x->digitalPinsCount] = pin; + x->digitalPinsCount++; + } + else if(pwmFound) { + x->modesArray[x->pinsCount] = MODE_PWM; + x->att_pwmPins[x->pwmPinsCount] = pin; + x->pwmPinsCount++; + } + + x->pinsCount++; + } + } + + else { + // error + if(x->pinsCount >= TOTAL_OUT_PINS) { + object_error((t_object *)x, "forbidden argument, too many pins requested"); + //return -1; + } + object_error((t_object *)x, "forbidden argument, number %d is out of context", pin); + //return -1; + } + } + + else { + // error + object_error((t_object *)x, "forbidden argument, expected format:"); + object_error((t_object *)x, "[port] [@digital pins_list] [@servo pins_list] [@pwm pins_list]"); + //return -1; + + } + } + return 0; +} + +void boardout_printAvailPorts(t_boardout *x) { + + char *ports_avail[MAX_PORTS]; + + int num_ports = serial_portlist(ports_avail, MAX_PORTS); + + for(int i=0; idigitalPinsCount; ++i) { + if(x->att_digitalPins[i] == pin) + return true; + } + + for (int i = 0; iservoPinsCount; ++i) { + if(x->att_servoPins[i] == pin) + return true; + } + + for (int i = 0; ipwmPinsCount; ++i) { + if(x->att_pwmPins[i] == pin) + return true; + } + + return false; +} + +int boardout_getBoard(t_boardout *x, int len, int *pins, int *modes) { + + if(!x || !x->att_portname || !x->att_portname->s_thing) return -1; + if( !pins || !modes) return -1; + + // search symbol port_name. if found, then board is already connected to by a boardin + // object (reader object), then get the pointer to that board object. Otherwise, create + // a new board object and initialise it with the requested baud rate and the rest of + // the pins (write) configuration. + + board_mgr_t *mgr = ((board_mgr_t *)(x->att_portname->s_thing)); + if (mgr == NULL) { + if (DEBUG) object_error((t_object *)x, "can't connect to port, internal error"); + return -1; + } + + if(mgr->board_out != NULL) { + // error - there is already another boardout object connected + object_error((t_object *)x, "open: port %s is already occupied with another boardout (writer) object!", x->att_portname->s_name); + return -1; + } + + else if(mgr->board_in != NULL) { + + if (DEBUG) object_post((t_object *)x, "open: port %s has another connection to boardin (okay)", x->att_portname->s_name); // debug + + if(mgr->board == NULL) { + object_error((t_object *)x, "open: can't connect the board. internal error."); + return -1; + } + + // check baud rate conflict (between existing and the new board) + //if(x->att_baudrate) { + // long current_rate = atol(x->att_baudrate->s_name); + // if(mgr->board->port) { + // long port_rate = mgr->board->port->baud_rate; + // if(port_rate != current_rate) { + // object_error((t_object *)x, "open: can't connect the board, baud rate conflict. "); + // object_error((t_object *)x, "port baud rate %ld, but baud %ld requested. ", mgr->board->port->baud_rate, current_rate); + // return -1; + // } + // } + // } + + // check if there is any configuration conflict + if (maxclient_verifyconfig(mgr->board, len, pins, modes)) { + object_error((t_object *)x, "open: can't connect the board. "); + object_error((t_object *)x, "%s", mgr->board->err_msg); + return -1; + } + + x->board_ob = mgr->board; + mgr->board_out = x; + } + + else { + + // requested port is not connected to by any other board object, then + // create a new Arduino board object, initialise and connect to (serial) port + + if (DEBUG) object_post((t_object *)x, "open: port is not connected to by any other board object yet, establishing a new connection."); // debug + + char err_return[4000]; + long rate = x->att_baudrate? atol(x->att_baudrate->s_name) : DEFAUTLT_BAUD; + x->board_ob = maxclient_newboard(x->att_portname->s_name, rate, err_return); + if(x->board_ob == NULL) { + // error there is already another boardout object connected + object_error((t_object *)x, "Max could not create new boardout object."); + object_error((t_object *)x, "%s", err_return); + return -1; + } + + //board_mgr_t *mgr = (board_mgr_t*)malloc(sizeof(board_mgr_t)); + mgr->board = x->board_ob; + mgr->board_out = x; + } + + return 0; +} + +int boardout_pinDigitalRange(int pin) { + return ((pin < 20 && pin >=0)? 1 : 0); +} + +int boardout_pinServoRange(int pin) { + return ((pin < 20 && pin >=2)? 1 : 0); +} + +int boardout_pinPwmRange(int pin) { + // range = {3, 5, 6, 9, 10, 11}; + return ((pin==3 || pin==5 || pin==6 || pin==9 || pin==10 || pin==11)? 1 : 0); +} + +void boardout_init(t_boardout *x) { + + if(x == NULL) + return; + + x->pinsCount = 0; + x->att_portname = NULL; + x->att_baudrate = NULL; + x->digitalPinsCount = 0; + x->pwmPinsCount = 0; + x->servoPinsCount = 0; + x->isopen = 0; +} + +/* + INTERNALS + */ + +t_max_err boardout_digitalPins_get(t_boardout *x, t_object *attr, long *argc, t_atom **argv) { + return MAX_ERR_NONE; +} + +t_max_err boardout_digitalPins_set(t_boardout *x, t_object *attr, long argc, t_atom *argv) { + return MAX_ERR_NONE; +} + +t_max_err boardout_servoPins_get(t_boardout *x, t_object *attr, long *argc, t_atom **argv) { + return MAX_ERR_NONE; +} + +t_max_err boardout_servoPins_set(t_boardout *x, t_object *attr, long argc, t_atom *argv) { + return MAX_ERR_NONE; +} + +t_max_err boardout_pwmPins_get(t_boardout *x, t_object *attr, long argc, t_atom *argv) { + return MAX_ERR_NONE; +} + +t_max_err boardout_pwmPins_set(t_boardout *x, t_object *attr, long argc, t_atom *argv) { + return MAX_ERR_NONE; +} + +void boardout_in1(t_boardout *x, long n) { + boardout_updatePin(x, 1, n); +} + +void boardout_in2(t_boardout *x, long n) { + boardout_updatePin(x, 2, n); +} + +void boardout_in3(t_boardout *x, long n) { + boardout_updatePin(x, 3, n); +} + +void boardout_in4(t_boardout *x, long n) { + boardout_updatePin(x, 4, n); +} + +void boardout_in5(t_boardout *x, long n) { + boardout_updatePin(x, 5, n); +} + +void boardout_in6(t_boardout *x, long n) { + boardout_updatePin(x, 6, n); +} + +void boardout_in7(t_boardout *x, long n) { + boardout_updatePin(x, 7, n); +} + +void boardout_in8(t_boardout *x, long n) { + boardout_updatePin(x, 8, n); +} + +void boardout_in9(t_boardout *x, long n) { + boardout_updatePin(x, 9, n); +} + +void boardout_in10(t_boardout *x, long n) { + boardout_updatePin(x, 10, n); +} + +void boardout_in11(t_boardout *x, long n) { + boardout_updatePin(x, 11, n); +} + +void boardout_in12(t_boardout *x, long n) { + boardout_updatePin(x, 12, n); +} + +void boardout_pinNumConvert(int pin, char *str) { + if( ! str) { + str = ""; + return; + } + if(pin < 14) sprintf(str, "%d", pin); + else sprintf(str, "A%d", pin-14); + return; +} + + +// Servo.Write() writes a value to the servo, controlling the shaft accordingly. On a standard servo, this will set the angle of the shaft (in degrees), moving the shaft to that orientation. On a continuous rotation servo, this will set the speed of the servo (with 0 being full-speed in one direction, 180 being full speed in the other, and a value near 90 being no movement). + +// PWM: call analogWrite(pin, dutyCycle), where dutyCycle is a value from 0 to 255. diff --git a/source/cr.boardout/cr.boardout.xcodeproj/project.pbxproj b/source/cr.boardout/cr.boardout.xcodeproj/project.pbxproj new file mode 100755 index 0000000000000000000000000000000000000000..a304117f4ecf57583e4ec47ec43f09a26fe6ff0a --- /dev/null +++ b/source/cr.boardout/cr.boardout.xcodeproj/project.pbxproj @@ -0,0 +1,236 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 0C91F5A91F73957300EF94C5 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C91F5A81F73957200EF94C5 /* CoreFoundation.framework */; }; + 0C91F5AB1F73958100EF94C5 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C91F5AA1F73958100EF94C5 /* IOKit.framework */; }; + 0CDDA8CB2022D8B500358057 /* firmatalib.h in Headers */ = {isa = PBXBuildFile; fileRef = 0CDDA8C52022D8B500358057 /* firmatalib.h */; }; + 0CDDA8CC2022D8B500358057 /* firmatalib.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CDDA8C62022D8B500358057 /* firmatalib.c */; }; + 0CDDA8CD2022D8B500358057 /* maxlib.h in Headers */ = {isa = PBXBuildFile; fileRef = 0CDDA8C72022D8B500358057 /* maxlib.h */; }; + 0CDDA8CE2022D8B500358057 /* serial.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CDDA8C82022D8B500358057 /* serial.c */; }; + 0CDDA8CF2022D8B500358057 /* serial.h in Headers */ = {isa = PBXBuildFile; fileRef = 0CDDA8C92022D8B500358057 /* serial.h */; }; + 0CDDA8D02022D8B500358057 /* maxlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 0CDDA8CA2022D8B500358057 /* maxlib.c */; }; + 22CF11AE0EE9A8840054F513 /* cr.boardout.c in Sources */ = {isa = PBXBuildFile; fileRef = 22CF11AD0EE9A8840054F513 /* cr.boardout.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 0C91F5A81F73957200EF94C5 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + 0C91F5AA1F73958100EF94C5 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; + 0CDDA8C52022D8B500358057 /* firmatalib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = firmatalib.h; path = ../FirmataClient/firmatalib.h; sourceTree = ""; }; + 0CDDA8C62022D8B500358057 /* firmatalib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = firmatalib.c; path = ../FirmataClient/firmatalib.c; sourceTree = ""; }; + 0CDDA8C72022D8B500358057 /* maxlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = maxlib.h; path = ../FirmataClient/maxlib.h; sourceTree = ""; }; + 0CDDA8C82022D8B500358057 /* serial.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = serial.c; path = ../FirmataClient/serial.c; sourceTree = ""; }; + 0CDDA8C92022D8B500358057 /* serial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = serial.h; path = ../FirmataClient/serial.h; sourceTree = ""; }; + 0CDDA8CA2022D8B500358057 /* maxlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = maxlib.c; path = ../FirmataClient/maxlib.c; sourceTree = ""; }; + 22CF10220EE984600054F513 /* maxmspsdk.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = maxmspsdk.xcconfig; path = ../maxmspsdk.xcconfig; sourceTree = SOURCE_ROOT; }; + 22CF11AD0EE9A8840054F513 /* cr.boardout.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cr.boardout.c; sourceTree = ""; }; + 2FBBEAE508F335360078DB84 /* cr.boardout.mxo */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = cr.boardout.mxo; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 2FBBEADC08F335360078DB84 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0C91F5AB1F73958100EF94C5 /* IOKit.framework in Frameworks */, + 0C91F5A91F73957300EF94C5 /* CoreFoundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 089C166AFE841209C02AAC07 /* iterator */ = { + isa = PBXGroup; + children = ( + 0CDDA8C52022D8B500358057 /* firmatalib.h */, + 0CDDA8C62022D8B500358057 /* firmatalib.c */, + 0CDDA8C72022D8B500358057 /* maxlib.h */, + 0CDDA8C82022D8B500358057 /* serial.c */, + 0CDDA8C92022D8B500358057 /* serial.h */, + 0CDDA8CA2022D8B500358057 /* maxlib.c */, + 22CF11AD0EE9A8840054F513 /* cr.boardout.c */, + 22CF10220EE984600054F513 /* maxmspsdk.xcconfig */, + 19C28FB4FE9D528D11CA2CBB /* Products */, + 0C91F5A71F73957200EF94C5 /* Frameworks */, + ); + name = iterator; + sourceTree = ""; + }; + 0C91F5A71F73957200EF94C5 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 0C91F5AA1F73958100EF94C5 /* IOKit.framework */, + 0C91F5A81F73957200EF94C5 /* CoreFoundation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 19C28FB4FE9D528D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 2FBBEAE508F335360078DB84 /* cr.boardout.mxo */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 2FBBEAD708F335360078DB84 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 0CDDA8CB2022D8B500358057 /* firmatalib.h in Headers */, + 0CDDA8CF2022D8B500358057 /* serial.h in Headers */, + 0CDDA8CD2022D8B500358057 /* maxlib.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 2FBBEAD608F335360078DB84 /* max-external */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2FBBEAE008F335360078DB84 /* Build configuration list for PBXNativeTarget "max-external" */; + buildPhases = ( + 2FBBEAD708F335360078DB84 /* Headers */, + 2FBBEAD808F335360078DB84 /* Resources */, + 2FBBEADA08F335360078DB84 /* Sources */, + 2FBBEADC08F335360078DB84 /* Frameworks */, + 2FBBEADF08F335360078DB84 /* Rez */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "max-external"; + productName = iterator; + productReference = 2FBBEAE508F335360078DB84 /* cr.boardout.mxo */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 089C1669FE841209C02AAC07 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0830; + }; + buildConfigurationList = 2FBBEACF08F335010078DB84 /* Build configuration list for PBXProject "cr.boardout" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + en, + ); + mainGroup = 089C166AFE841209C02AAC07 /* iterator */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 2FBBEAD608F335360078DB84 /* max-external */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 2FBBEAD808F335360078DB84 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXRezBuildPhase section */ + 2FBBEADF08F335360078DB84 /* Rez */ = { + isa = PBXRezBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXRezBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 2FBBEADA08F335360078DB84 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0CDDA8CE2022D8B500358057 /* serial.c in Sources */, + 0CDDA8CC2022D8B500358057 /* firmatalib.c in Sources */, + 0CDDA8D02022D8B500358057 /* maxlib.c in Sources */, + 22CF11AE0EE9A8840054F513 /* cr.boardout.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 2FBBEAD008F335010078DB84 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Development; + }; + 2FBBEAD108F335010078DB84 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Deployment; + }; + 2FBBEAE108F335360078DB84 /* Development */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 22CF10220EE984600054F513 /* maxmspsdk.xcconfig */; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_OPTIMIZATION_LEVEL = 0; + OTHER_LDFLAGS = "$(C74_SYM_LINKER_FLAGS)"; + PRODUCT_BUNDLE_IDENTIFIER = boardout; + PRODUCT_NAME = cr.boardout; + }; + name = Development; + }; + 2FBBEAE208F335360078DB84 /* Deployment */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 22CF10220EE984600054F513 /* maxmspsdk.xcconfig */; + buildSettings = { + COPY_PHASE_STRIP = YES; + OTHER_LDFLAGS = "$(C74_SYM_LINKER_FLAGS)"; + "OTHER_LDFLAGS[arch=*]" = "$(C74_SYM_LINKER_FLAGS)"; + PRODUCT_BUNDLE_IDENTIFIER = boardout; + PRODUCT_NAME = cr.boardout; + }; + name = Deployment; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 2FBBEACF08F335010078DB84 /* Build configuration list for PBXProject "cr.boardout" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2FBBEAD008F335010078DB84 /* Development */, + 2FBBEAD108F335010078DB84 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 2FBBEAE008F335360078DB84 /* Build configuration list for PBXNativeTarget "max-external" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2FBBEAE108F335360078DB84 /* Development */, + 2FBBEAE208F335360078DB84 /* Deployment */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; +/* End XCConfigurationList section */ + }; + rootObject = 089C1669FE841209C02AAC07 /* Project object */; +} diff --git a/source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000000000000000000000000000000000..ea47bfd3b3b572ff7f31560d72a301866685648b --- /dev/null +++ b/source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000000000000000000000000000000000000..54782e32fdc67a492a81269a1347cacb4a7f9f10 --- /dev/null +++ b/source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded + + + diff --git a/source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/UserInterfaceState.xcuserstate b/source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..23adeed508256cd50cf703457d3c880f401dedf9 Binary files /dev/null and b/source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/WorkspaceSettings.xcsettings b/source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/WorkspaceSettings.xcsettings new file mode 100644 index 0000000000000000000000000000000000000000..dd7403bf66e22a2b6c4af9325329f28060867051 --- /dev/null +++ b/source/cr.boardout/cr.boardout.xcodeproj/project.xcworkspace/xcuserdata/orly.xcuserdatad/WorkspaceSettings.xcsettings @@ -0,0 +1,16 @@ + + + + + BuildLocationStyle + UseAppPreferences + CustomBuildLocationType + RelativeToDerivedData + DerivedDataLocationStyle + Default + IssueFilterStyle + ShowActiveSchemeOnly + LiveSourceIssuesEnabled + + + diff --git a/source/cr.boardout/cr.boardout.xcodeproj/xcuserdata/orly.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/source/cr.boardout/cr.boardout.xcodeproj/xcuserdata/orly.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000000000000000000000000000000000000..fe2b45415124ec4c223006e19defd56850da95d9 --- /dev/null +++ b/source/cr.boardout/cr.boardout.xcodeproj/xcuserdata/orly.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,5 @@ + + + diff --git a/source/cr.boardout/cr.boardout.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/max-external.xcscheme b/source/cr.boardout/cr.boardout.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/max-external.xcscheme new file mode 100644 index 0000000000000000000000000000000000000000..c6b50aba5ff4361230a6462a68f5fc540d5b639b --- /dev/null +++ b/source/cr.boardout/cr.boardout.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/max-external.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/cr.boardout/cr.boardout.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/xcschememanagement.plist b/source/cr.boardout/cr.boardout.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000000000000000000000000000000000000..d70c3e6609bb71e9ae46e8593636c0e5eb5b6bc2 --- /dev/null +++ b/source/cr.boardout/cr.boardout.xcodeproj/xcuserdata/orly.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,24 @@ + + + + + SchemeUserState + + max-external.xcscheme + + isShown + + orderHint + 0 + + + SuppressBuildableAutocreation + + 2FBBEAD608F335360078DB84 + + primary + + + + + diff --git a/source/maxmspsdk.xcconfig b/source/maxmspsdk.xcconfig new file mode 100755 index 0000000000000000000000000000000000000000..0c126a9fad67879333eaf1f47ffe1a3e1dfadef1 --- /dev/null +++ b/source/maxmspsdk.xcconfig @@ -0,0 +1,70 @@ +// Xcode target configuration settings for the Max 6 SDK +// Used as the basis for Xcode projects to build Max externals. +// +// Changes to the settings in this file will be applied to all SDK examples +// To change settings for only one of the examples, override the settings using +// Xcode's target inspector. +// +// by Timothy Place +// Copyright © 2012, Cycling '74 + + +// Name & Version +PRODUCT_NAME = $(PROJECT_NAME) +PRODUCT_VERSION = 7.0.1 +ARCHS = i386 x86_64 + +// Paths +C74SUPPORT = /Applications/Max.app/Contents/Resources/C74/packages/max-sdk-7.3.3/source/c74support +HEADER_SEARCH_PATHS = "$(C74SUPPORT)/max-includes" "$(C74SUPPORT)/msp-includes" "$(C74SUPPORT)/jit-includes" +FRAMEWORK_SEARCH_PATHS = "$(C74SUPPORT)/max-includes" "$(C74SUPPORT)/msp-includes" "$(C74SUPPORT)/jit-includes" +DSTROOT = $(SRCROOT)/../../cr/externals +// (This next path is relative to DSTROOT) +INSTALL_PATH = / + + +// Special Files +GCC_PREFIX_HEADER = $(C74SUPPORT)/max-includes/macho-prefix.pch +INFOPLIST_FILE = $(SRCROOT)/../Info.plist + + +// Architecture and Deployment +ARCHS = i386 x86_64 + +// The following section sets the Mac SDK version to be used. +// For most projects this has little to no impact because there are no direct dependencies on OS function calls. +// In those projects with OS function calls, it should be okay to use the most recent SDK version because the +// MACOSX_DEPLOYMENT_TARGET will disable functionality that is unavailable in the older target OS. +// For this reason, the SDKROOT variable is commented out, telling Xcode to use the default (which is the most recent SDK). +// +// SDKROOT = macosx10.6 + +MACOSX_DEPLOYMENT_TARGET = 10.7 + + +// Compiler Version -- leave them all commented out to get the default version provided by Xcode +// GCC_VERSION = com.apple.compilers.llvmgcc42 +// GCC_VERSION = com.apple.compilers.llvm.clang.1_0 + + +// Preprocessor Defines +GCC_PREPROCESSOR_DEFINITIONS = "DENORM_WANT_FIX = 1" "NO_TRANSLATION_SUPPORT = 1" + + +// Static Configuration (don't change these) +WRAPPER_EXTENSION = mxo; +WARNING_CFLAGS = -Wmost -Wno-four-char-constants -Wno-unknown-pragmas +DEPLOYMENT_LOCATION = YES +GENERATE_PKGINFO_FILE = YES + + +// Flags to enforce some build-time checks for the symbols used while not actually performing a hard link +C74_SYM_LINKER_FLAGS = @$(C74SUPPORT)/max-includes/c74_linker_flags.txt + + +// hide all symbols by default +// mark a function to be exported with the C74_EXPORT macro +// most likely this will only apply to the ext_main() function which we've already prototyped for you +OTHER_CFLAGS = -fvisibility=hidden + +OTHER_LDFLAGS = -framework MaxAudioAPI -framework JitterAPI $(C74_SYM_LINKER_FLAGS) diff --git a/source/readme.md b/source/readme.md new file mode 100755 index 0000000000000000000000000000000000000000..200287a2903a26f8488686d874584f1a2aebd052 --- /dev/null +++ b/source/readme.md @@ -0,0 +1,22 @@ +© Copyright The University of New South Wales 2018 + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see http://www.gnu.org/licenses/. / + + + +The folder “c74support” was borrowed from the Max/MSP SDK by Cycling ’74, and it contains necessary header files. The project looks for these header files by relative location, therefore “c74support” must be in the same directory as the project folder. + +The compiled external (.mxo) will be created in the cr package “externals” folder. Make sure the “cr\externals” folder is in the Max Search Path. + +