diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..95239ac --- /dev/null +++ b/.clang-format @@ -0,0 +1,238 @@ +# DO NOT EDIT +# USE https://clang-format-configurator.site/ +# Use version 17.0.6 + +--- +BasedOnStyle: Microsoft +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseColons: false +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: true + AfterControlStatement: Always + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: false + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAfterAttributes: Never +BreakAfterJavaFieldAnnotations: true +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Allman +BreakBeforeConceptDeclarations: Always +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +ColumnLimit: 0 +CommentPragmas: "^ IWYU pragma:" +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: ^"(llvm|llvm-c|clang|clang-c)/ + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: ^(<|"(gtest|gmock|isl|json)/) + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: .* + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: (Test)?$ +IncludeIsMainSourceRegex: "" +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: true +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: BeforeHash +IndentRequiresClause: true +IndentWidth: 4 +IndentWrappedFunctionNames: true +InsertBraces: true +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtEOF: false +KeepEmptyLinesAtTheStartOfBlocks: true +LambdaBodyIndentation: Signature +Language: Cpp +LineEnding: DeriveLF +MacroBlockBegin: "" +MacroBlockEnd: "" +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PPIndentWidth: -1 +PackConstructorInitializers: BinPack +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 1000 +PointerAlignment: Left +QualifierAlignment: Leave +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SortIncludes: Never +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDeclarationName: false + AfterFunctionDefinitionName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + InConditionalStatements: false + InCStyleCasts: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +UseTab: Never +VerilogBreakBetweenInstancePorts: true +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e6e4034 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +bin/ +obj/ +checks.json +gmon.out diff --git a/README.md b/README.md index 6a3696d..3dd7a85 100644 --- a/README.md +++ b/README.md @@ -17,47 +17,49 @@ This software is based on C program and requires gcc to compile the program from cd sainsmart-usb-relay make sudo make install - + The "make install" command copies the binary to /usr/local/bin. So the command can be utilized anywhere from the system. - The following works for both a Raspberry Pi (Debian Wheezy) and Ubuntu 16.04, getting ordinary users (e.g. ‘pi’ on the RPi) access to the FTDI device without needing root permissions: - Create a file /etc/udev/rules.d/99-libftdi.rules. You will need sudo access to create this file. + Create a file /etc/udev/rules.d/53-libftdi.rules. You will need sudo access to create this file. Put the following in the file: SUBSYSTEMS=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", GROUP="dialout", MODE="0660" SUBSYSTEMS=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6014", GROUP="dialout", MODE="0660" Some FTDI devices may use other USB PIDs. You could try removing the match on idProduct altogether, just matching on the FTDI vendor ID. Or Use lsusb or similar to determine the exact values to use (or try checking dmesg output on device insertion / removal). - + + See `udev/53-ftdi.rules` for example + Usage ============ This project provides a simple command to interact with the USB relay. To turn on the USB relay - sudo sainsmart --on RELAY_NUMBER + sudo sainsmartrelay --on RELAY_NUMBER For example to turn on the relay 1 we can use - sudo sainsmart --on 1 + sudo sainsmartrelay --on 1 To run on all the relays - sudo sainsmart --on all + sudo sainsmartrelay --on all To turn off the USB relay 1 - sudo sainsmart --off 1 + sudo sainsmartrelay --off 1 To run on all the relays - sudo sainsmart --off all + sudo sainsmartrelay --off all To get the status of the relays - sudo sainsmart --status + sudo sainsmartrelay --status To get more help information - sudo sainsmart --help + sudo sainsmartrelay --help Notes ============ @@ -67,4 +69,4 @@ The Sainsmart card uses the FTDI FT245RL chip. This chip is controlled directly To prevent automatic loading of the driver, add the following line to /etc/modprobe.d/blacklist.conf: blacklist ftdi_sio -Both 4 and 8 channel versions are supported. However, there seems to be no way to automatically detect which version of the card is used. Therefore the number of relay channles can be configured in the configuration file. +Both 4 and 8 channel versions are supported. However, there seems to be no way to automatically detect which version of the card is used. Therefore the number of relay channles can be configured in the configuration file. diff --git a/bin/Debug/gmon.out b/bin/Debug/gmon.out deleted file mode 100644 index ef6a390..0000000 Binary files a/bin/Debug/gmon.out and /dev/null differ diff --git a/bin/Debug/sainsmartrelay b/bin/Debug/sainsmartrelay deleted file mode 100755 index ee80d63..0000000 Binary files a/bin/Debug/sainsmartrelay and /dev/null differ diff --git a/bin/Release/sainsmartrelay b/bin/Release/sainsmartrelay deleted file mode 100755 index 1ecacb0..0000000 Binary files a/bin/Release/sainsmartrelay and /dev/null differ diff --git a/obj/Debug/sainsmartrelay.o b/obj/Debug/sainsmartrelay.o deleted file mode 100644 index 4f8ab2b..0000000 Binary files a/obj/Debug/sainsmartrelay.o and /dev/null differ diff --git a/obj/Release/sainsmartrelay.o b/obj/Release/sainsmartrelay.o deleted file mode 100644 index 8468d43..0000000 Binary files a/obj/Release/sainsmartrelay.o and /dev/null differ diff --git a/sainsmartrelay.c b/sainsmartrelay.c index 8624deb..3464e01 100644 --- a/sainsmartrelay.c +++ b/sainsmartrelay.c @@ -9,11 +9,19 @@ #include "sainsmartrelay.h" +#define ANSI_COLOR_RED "\x1b[31m" +#define ANSI_COLOR_GREEN "\x1b[32m" +#define ANSI_COLOR_RESET "\x1b[0m" -static struct ftdi_context *ftdi; -static uint8 g_num_relays=MAX_NUM_RELAYS; +#define MAX_STR_LEN_MANUFACTURER 128 +#define MAX_STR_LEN_DESCRIPTION 128 +#define MAX_STR_LEN_SERIAL 128 +#define MAX_STR_LEN_SAINSMARTBOARD 4 -static void usage(char *myName) +static struct ftdi_context* ftdi; +static uint8 g_num_relays = MAX_NUM_RELAYS; + +static void usage(char* myName) { fprintf(stderr, "\nUsage:\n"); fprintf(stderr, " %s --on [1|2|3|4|all]\n", myName); @@ -23,7 +31,7 @@ static void usage(char *myName) fprintf(stderr, " %s -h\n", myName); } -static void help(char *myName) +static void help(char* myName) { fprintf(stdout, "\nHelp:\n %s --on [1|2|3|4|all] | --off [1|2|3|4|all] | --status | --findall | [-h]\n", myName); fprintf(stdout, " --help|-h print this help.\n"); @@ -35,16 +43,14 @@ static void help(char *myName) static void checkPermission() { - if(geteuid() != 0) + if (geteuid() != 0) { - fprintf(stderr,"\nWarning:\n this program is currently not running with root priviledges !\n"); - fprintf(stderr,"Therefore it might not be able to access your relay cards communication port.\n"); - fprintf(stderr,"Consider invoking the program from the root account or use \"sudo ...\"\n"); + fprintf(stderr, "\nWarning:\n this program is currently not running with root priviledges !\n"); + fprintf(stderr, "Therefore it might not be able to access your relay cards communication port.\n"); + fprintf(stderr, "Consider invoking the program from the root account or use \"sudo ...\"\n"); } } - - /********************************************************** * Function strsplit() * @@ -56,21 +62,21 @@ static void checkPermission() * * Return: array - response array *********************************************************/ -char **strsplit(const char* str, const char* delim, size_t* numtokens) +char** strsplit(const char* str, const char* delim, size_t* numtokens) { // copy the original string so that we don't overwrite parts of it // (don't do this if you don't need to keep the old line, // as this is less efficient) - char *s = strdup(str); + char* s = strdup(str); // these three variables are part of a very common idiom to // implement a dynamically-growing array size_t tokens_alloc = 1; - size_t tokens_used = 0; - char **tokens = calloc(tokens_alloc, sizeof(char*)); + size_t tokens_used = 0; + char** tokens = calloc(tokens_alloc, sizeof(char*)); char *token, *strtok_ctx; for (token = strtok_r(s, delim, &strtok_ctx); - token != NULL; - token = strtok_r(NULL, delim, &strtok_ctx)) + token != NULL; + token = strtok_r(NULL, delim, &strtok_ctx)) { // check if we need to allocate more space for tokens if (tokens_used == tokens_alloc) @@ -95,7 +101,6 @@ char **strsplit(const char* str, const char* delim, size_t* numtokens) return tokens; } - /********************************************************** * Function remove_duplicate() * @@ -107,23 +112,22 @@ char **strsplit(const char* str, const char* delim, size_t* numtokens) * * Return: array - New array with deduped array content *********************************************************/ -int *remove_duplicate(int array[],int length, size_t* numtokens) +int* remove_duplicate(int array[], int length, size_t* numtokens) { size_t tokens_alloc = 1; - size_t tokens_used = 0; - int *tokens = calloc(tokens_alloc, sizeof(int*)); + size_t tokens_used = 0; + int* tokens = calloc(tokens_alloc, sizeof(int*)); - int *current , *end = array + length - 1; + int *current, *end = array + length - 1; int flag = 0; - for ( current = array + 1; array < end; array++, current = array + 1 ) + for (current = array + 1; array < end; array++, current = array + 1) { flag = 0; - while ( current <= end ) + while (current <= end) { - if ( *current == *array ) + if (*current == *array) { *current = *end--; - } else { @@ -131,7 +135,7 @@ int *remove_duplicate(int array[],int length, size_t* numtokens) current++; } } - if(flag == 1) + if (flag == 1) { if (tokens_used == tokens_alloc) { @@ -172,17 +176,17 @@ int *remove_duplicate(int array[],int length, size_t* numtokens) * * Return: array of relay id and it's status *********************************************************/ -int *get_bits(int n, int bitswanted) +int* get_bits(int n, int bitswanted) { - int *bits = malloc(sizeof(int) * bitswanted); + int* bits = malloc(sizeof(int) * bitswanted); int k; - for(k=0; k> k; - bits[k] = thebit; + int thebit = masked_n >> k; + bits[k] = thebit; } return bits; @@ -211,9 +215,11 @@ int detect_relay_card_sainsmart_4_8chan(char* portname, uint8* num_relays) return -1; } - /* Try to open FTDI USB device */ - if ((ftdi_usb_open(ftdi, VENDOR_ID, DEVICE_ID)) < 0) + /* Try to open FTDI USB device for sainsmart + relay card with description = SAINSMART_FTDI_CHIP_MODEL ("FT245R USB FIFO") */ + if (ftdi_usb_open_desc(ftdi, VENDOR_ID, DEVICE_ID, SAINSMART_FTDI_CHIP_MODEL, NULL) < 0) { + fprintf(stderr, "unable to open ftdi device: (%s)\n", ftdi_get_error_string(ftdi)); ftdi_free(ftdi); return -1; } @@ -227,10 +233,10 @@ int detect_relay_card_sainsmart_4_8chan(char* portname, uint8* num_relays) } /* Check if this is an R type chip - * Type 245RL = 5000 - */ - //printf("relay type:%d\n",ftdi->type); - if (ftdi->type != 5000 && ftdi->type != TYPE_R ) + * Type 245RL = 5000 + */ + // printf("relay type:%d\n",ftdi->type); + if (ftdi->type != 5000 && ftdi->type != TYPE_R) { fprintf(stderr, "unable to continue, not an R-type chip\n"); ftdi_free(ftdi); @@ -241,15 +247,17 @@ int detect_relay_card_sainsmart_4_8chan(char* portname, uint8* num_relays) ftdi_read_chipid(ftdi, &chipid); /* Return parameters */ - if (num_relays!=NULL) *num_relays = g_num_relays; + if (num_relays != NULL) + { + *num_relays = g_num_relays; + } sprintf(portname, "FTDI chipid %X", chipid); - //printf("DBG: portname %s\n", portname); + // printf("DBG: portname %s\n", portname); ftdi_usb_close(ftdi); return 0; } - /********************************************************** * Function find_device() * @@ -261,39 +269,51 @@ int detect_relay_card_sainsmart_4_8chan(char* portname, uint8* num_relays) *********************************************************/ int find_device(void) { - int ret, i; - struct ftdi_context *ftdi; + int ret; + struct ftdi_context* ftdi; struct ftdi_device_list *devlist, *curdev; - char manufacturer[128], description[128]; + char manufacturer[MAX_STR_LEN_MANUFACTURER], description[MAX_STR_LEN_DESCRIPTION], serial[MAX_STR_LEN_SERIAL], sainsmartboard[MAX_STR_LEN_SAINSMARTBOARD]; int retval = EXIT_SUCCESS; + memset(manufacturer, 0, sizeof(manufacturer)); + memset(description, 0, sizeof(description)); + memset(serial, 0, sizeof(serial)); + if ((ftdi = ftdi_new()) == 0) { fprintf(stderr, "ftdi_new failed\n"); return EXIT_FAILURE; } - if ((ret = ftdi_usb_find_all(ftdi, &devlist, 0, 0)) < 0) + if ((ret = ftdi_usb_find_all(ftdi, &devlist, VENDOR_ID, DEVICE_ID)) < 0) { fprintf(stderr, "ftdi_usb_find_all failed: %d (%s)\n", ret, ftdi_get_error_string(ftdi)); - retval = EXIT_FAILURE; + retval = EXIT_FAILURE; goto do_deinit; } - printf("Number of FTDI devices found: %d\n", ret); - i = 0; - for (curdev = devlist; curdev != NULL; i++) + // Iterate through the devices + for (curdev = devlist; curdev != NULL; curdev = curdev->next) { - printf("Checking device: %d\n", i); - if ((ret = ftdi_usb_get_strings(ftdi, curdev->dev, manufacturer, 128, description, 128, NULL, 0)) < 0) + if ((ret = ftdi_usb_get_strings(ftdi, curdev->dev, manufacturer, MAX_STR_LEN_MANUFACTURER, description, MAX_STR_LEN_DESCRIPTION, serial, MAX_STR_LEN_SERIAL)) < 0) { fprintf(stderr, "ftdi_usb_get_strings failed: %d (%s)\n", ret, ftdi_get_error_string(ftdi)); retval = EXIT_FAILURE; goto done; } - printf("Manufacturer: %s, Description: %s\n", manufacturer, description); - curdev = curdev->next; + if (0 == strncmp(description, SAINSMART_FTDI_CHIP_MODEL, strlen(SAINSMART_FTDI_CHIP_MODEL))) + { + snprintf(sainsmartboard, sizeof(sainsmartboard), "%s", "YES"); + printf("Manufacturer: %-6s, Description: %-20s, Serial: %-10s, Sainsmart Board: %s%s%s\n", + manufacturer, description, serial, ANSI_COLOR_GREEN, sainsmartboard, ANSI_COLOR_RESET); + } + else + { + snprintf(sainsmartboard, sizeof(sainsmartboard), "%s", "NO"); + printf("Manufacturer: %-6s, Description: %-20s, Serial: %-10s, Sainsmart Board: %s%s%s\n", + manufacturer, description, serial, ANSI_COLOR_RED, sainsmartboard, ANSI_COLOR_RESET); + } } done: ftdi_list_free(&devlist); @@ -317,14 +337,14 @@ int get_relay_sainsmart_4_8chan(uint8 relay, relay_state_t* relay_state) { unsigned char buf[1]; - if (relay(FIRST_RELAY+g_num_relays-1)) + if (relay < FIRST_RELAY || relay > (FIRST_RELAY + g_num_relays - 1)) { fprintf(stderr, "ERROR: Relay number out of range\n"); return -1; } /* Open FTDI USB device */ - if ((ftdi_usb_open(ftdi, VENDOR_ID, DEVICE_ID)) < 0) + if ((ftdi_usb_open_desc(ftdi, VENDOR_ID, DEVICE_ID, SAINSMART_FTDI_CHIP_MODEL, NULL)) < 0) { fprintf(stderr, "unable to open ftdi device: (%s)\n", ftdi_get_error_string(ftdi)); ftdi_free(ftdi); @@ -334,13 +354,13 @@ int get_relay_sainsmart_4_8chan(uint8 relay, relay_state_t* relay_state) /* Get relay state from the card */ if (ftdi_read_pins(ftdi, &buf[0]) < 0) { - fprintf(stderr,"read failed for 0x%x, error %s\n",buf[0], ftdi_get_error_string(ftdi)); + fprintf(stderr, "read failed for 0x%x, error %s\n", buf[0], ftdi_get_error_string(ftdi)); return -3; } - //printf("DBG: Read GPIO bits %02X\n", buf[0]); - int *bits = get_bits(buf[0], g_num_relays); + // printf("DBG: Read GPIO bits %02X\n", buf[0]); + int* bits = get_bits(buf[0], g_num_relays); - relay = relay-1; + relay = relay - 1; *relay_state = (bits[relay] > 0) ? ON : OFF; ftdi_usb_close(ftdi); @@ -357,12 +377,12 @@ int get_relay_sainsmart_4_8chan(uint8 relay, relay_state_t* relay_state) * Return: 0 - success * < 0 - fail *********************************************************/ -int get_relay_sainsmart_4_8chan_all(int *relay_states) +int get_relay_sainsmart_4_8chan_all(int* relay_states) { unsigned char buf[1]; /* Open FTDI USB device */ - if ((ftdi_usb_open(ftdi, VENDOR_ID, DEVICE_ID)) < 0) + if ((ftdi_usb_open_desc(ftdi, VENDOR_ID, DEVICE_ID, SAINSMART_FTDI_CHIP_MODEL, NULL)) < 0) { fprintf(stderr, "unable to open ftdi device: (%s)\n", ftdi_get_error_string(ftdi)); ftdi_free(ftdi); @@ -372,16 +392,16 @@ int get_relay_sainsmart_4_8chan_all(int *relay_states) /* Get relay state from the card */ if (ftdi_read_pins(ftdi, &buf[0]) < 0) { - fprintf(stderr,"read failed for 0x%x, error %s\n",buf[0], ftdi_get_error_string(ftdi)); + fprintf(stderr, "read failed for 0x%x, error %s\n", buf[0], ftdi_get_error_string(ftdi)); return -3; } - //printf("DBG: Read GPIO bits %02X\n", buf[0]); - int *bits = get_bits(buf[0], g_num_relays); + // printf("DBG: Read GPIO bits %02X\n", buf[0]); + int* bits = get_bits(buf[0], g_num_relays); int j; - for(j=0; j(FIRST_RELAY+g_num_relays-1)) + if (relay < FIRST_RELAY || relay > (FIRST_RELAY + g_num_relays - 1)) { fprintf(stderr, "ERROR: Relay number out of range\n"); return -1; } /* Open FTDI USB device */ - if ((ftdi_usb_open(ftdi, VENDOR_ID, DEVICE_ID)) < 0) + if ((ftdi_usb_open_desc(ftdi, VENDOR_ID, DEVICE_ID, SAINSMART_FTDI_CHIP_MODEL, NULL)) < 0) { fprintf(stderr, "unable to open ftdi device: (%s)\n", ftdi_get_error_string(ftdi)); ftdi_free(ftdi); @@ -453,29 +473,29 @@ int set_relay_sainsmart_4_8chan(uint8 relay, relay_state_t relay_state) /* Get relay state from the card */ if (ftdi_read_pins(ftdi, buf) < 0) { - fprintf(stderr,"read failed for 0x%x, error %s\n",buf[0], ftdi_get_error_string(ftdi)); + fprintf(stderr, "read failed for 0x%x, error %s\n", buf[0], ftdi_get_error_string(ftdi)); return -3; } /* Set the new relay state bit */ - relay = relay-1; + relay = relay - 1; if (relay_state == OFF) { /* Clear the relay bit in mask */ - buf[0] = buf[0] & ~(0x01< 0) ? "ON" : "OFF"); + int j; + for (j = 0; j < g_num_relays; j++) + { + fprintf(stdout, "%d: %s\n", j + 1, (relay_states[j] > 0) ? "ON" : "OFF"); + } + exit(EXIT_SUCCESS); } - exit(EXIT_SUCCESS); } - } - else if(isdigit(optarg[0])) - { - if (get_relay_sainsmart_4_8chan(atoi(optarg), &rstate) == 0) + else if (isdigit(optarg[0])) + { + if (get_relay_sainsmart_4_8chan(atoi(optarg), &rstate) == 0) + { + fprintf(stdout, "%d: %s\n", atoi(optarg), (rstate == ON) ? "ON" : "OFF"); + exit(EXIT_SUCCESS); + } + } + else { - fprintf(stdout, "%d: %s\n", atoi(optarg),(rstate==ON) ? "ON" : "OFF"); - exit(EXIT_SUCCESS); + fprintf(stderr, "invalid value is set to --status argument\n"); + exit(EXIT_FAILURE); } - } - else - { - fprintf(stderr, "invalid value is set to --status argument\n"); + break; + case 'h': + help(argv[0]); + exit(EXIT_SUCCESS); + break; + case ':': + fprintf(stderr, "%s: option `-%c' requires an argument\n", argv[0], optopt); exit(EXIT_FAILURE); - } - break; - case 'h' : - help(argv[0]); - exit(EXIT_SUCCESS); - break; - case ':': - fprintf(stderr, "%s: option `-%c' requires an argument\n",argv[0], optopt); - exit(EXIT_FAILURE); - break; - case '?': - /* getopt_long already printed an error message. */ - break; + break; + case '?': + /* getopt_long already printed an error message. */ + break; - default: - usage(argv[0]); - exit(EXIT_FAILURE); + default: + usage(argv[0]); + exit(EXIT_FAILURE); } } - - char **op_relay_list = {0}; - size_t numtokens = 0; - int i = 0; - size_t numtok = 0; - int *relays; + char** op_relay_list = {0}; + size_t numtokens = 0; + int i = 0; + size_t numtok = 0; + int* relays; /* - * Get the current status of the relay - */ + * Get the current status of the relay + */ uint8 relay_data; if (get_relay_sainsmart_4_8chan_raw(&relay_data) != 0) { @@ -757,23 +773,22 @@ int main(int argc, char *argv[]) } /* - * Process all the multiple relay ON state operation - */ + * Process all the multiple relay ON state operation + */ - if(opOn == ID_ON_ALL) + if (opOn == ID_ON_ALL) { - for(i=1; i<= g_num_relays; i++) + for (i = 1; i <= g_num_relays; i++) { - relay_data = relay_data | (0x01<<(i-1)); - + relay_data = relay_data | (0x01 << (i - 1)); } } - else if(opOn == ID_ON) + else if (opOn == ID_ON) { - relay_data = relay_data | (0x01<<(atoi(op_relay_on)-1)); + relay_data = relay_data | (0x01 << (atoi(op_relay_on) - 1)); } - else if(opOn == ID_ON_MULTIPLE) + else if (opOn == ID_ON_MULTIPLE) { op_relay_list = strsplit(op_relay_on, ", \t\n", &numtokens); @@ -783,37 +798,33 @@ int main(int argc, char *argv[]) relay_list[i] = atoi(strdup(op_relay_list[i])); free(op_relay_list[i]); } - relays = remove_duplicate(relay_list,numtokens,&numtok); + relays = remove_duplicate(relay_list, numtokens, &numtok); - for(i=0; i< numtok; i++) + for (i = 0; i < numtok; i++) { - if(relays[i] != 0) + if (relays[i] != 0) { - relay_data = relay_data | (0x01<<(relays[i]-1)); + relay_data = relay_data | (0x01 << (relays[i] - 1)); } - } - - } /* - * Process all the multiple relay OFF state operation - */ - if(opOff == ID_OFF_ALL) + * Process all the multiple relay OFF state operation + */ + if (opOff == ID_OFF_ALL) { - for(i=1; i<= g_num_relays; i++) + for (i = 1; i <= g_num_relays; i++) { - relay_data = relay_data & ~(0x01<<(i-1)); - + relay_data = relay_data & ~(0x01 << (i - 1)); } } - else if(opOff == ID_OFF) + else if (opOff == ID_OFF) { - relay_data = relay_data & ~(0x01<<(atoi(op_relay_off)-1)); + relay_data = relay_data & ~(0x01 << (atoi(op_relay_off) - 1)); } - else if(opOff == ID_OFF_MULTIPLE) + else if (opOff == ID_OFF_MULTIPLE) { op_relay_list = strsplit(op_relay_off, ", \t\n", &numtokens); int relay_list[numtokens]; @@ -822,31 +833,31 @@ int main(int argc, char *argv[]) relay_list[i] = atoi(strdup(op_relay_list[i])); free(op_relay_list[i]); } - relays = remove_duplicate(relay_list,numtokens,&numtok); + relays = remove_duplicate(relay_list, numtokens, &numtok); - for(i=0; i< numtok; i++) + for (i = 0; i < numtok; i++) { - if(relays[i] != 0) + if (relays[i] != 0) { - relay_data = relay_data & ~(0x01<<(relays[i]-1)); + relay_data = relay_data & ~(0x01 << (relays[i] - 1)); } } } /* - * Write the final state data into the relay - */ - if(opOn != -1 || opOff != -1) + * Write the final state data into the relay + */ + if (opOn != -1 || opOff != -1) { if (set_relay_sainsmart_4_8chan_write(relay_data) == 0) { - int relay_states[g_num_relays-1]; + int relay_states[g_num_relays - 1]; if (get_relay_sainsmart_4_8chan_all(relay_states) == 0) { int j; - for(j=0; j 0) ? "ON" : "OFF"); + fprintf(stdout, "%d: %s\n", j + 1, (relay_states[j] > 0) ? "ON" : "OFF"); } exit(EXIT_SUCCESS); } @@ -856,9 +867,6 @@ int main(int argc, char *argv[]) fprintf(stderr, "Error writing data to the relay.\n"); exit(EXIT_FAILURE); } - - } exit(EXIT_SUCCESS); } - diff --git a/sainsmartrelay.h b/sainsmartrelay.h index 930368c..04e0387 100644 --- a/sainsmartrelay.h +++ b/sainsmartrelay.h @@ -1,26 +1,26 @@ #ifndef sainsmartrelay_h #define sainsmartrelay_h -#define VENDOR_ID 0x0403 -#define DEVICE_ID 0x6001 +#define VENDOR_ID 0x0403 +#define DEVICE_ID 0x6001 +#define SAINSMART_FTDI_CHIP_MODEL "FT245R USB FIFO" -#define FIRST_RELAY 1 -#define MAX_NUM_RELAYS 4 +#define FIRST_RELAY 1 +#define MAX_NUM_RELAYS 4 #define MAX_RELAY_CARD_NAME_LEN 40 -#define MAX_COM_PORT_NAME_LEN 32 +#define MAX_COM_PORT_NAME_LEN 32 -typedef unsigned char uint8; +typedef unsigned char uint8; typedef unsigned short uint16; -typedef unsigned long uint32; +typedef unsigned long uint32; typedef enum { - OFF=0, - ON, - PULSE, - INVALID -} -relay_state_t; + OFF = 0, + ON, + PULSE, + INVALID +} relay_state_t; typedef enum { @@ -32,7 +32,6 @@ typedef enum ID_OFF_MULTIPLE, ID_GET, ID_GET_ALL -} -operations; +} operations; #endif diff --git a/udev/53-ftdi.rules b/udev/53-ftdi.rules new file mode 100644 index 0000000..0102c2d --- /dev/null +++ b/udev/53-ftdi.rules @@ -0,0 +1,3 @@ +SUBSYSTEMS=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", GROUP="dialout", MODE="0660" +SUBSYSTEMS=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6014", GROUP="dialout", MODE="0660" +