├── readmeres ├── revhist.txt ├── 8-RELIND.jpg └── sequent.jpg ├── node-red ├── Sprinklers │ ├── Pic │ │ ├── Pic.md │ │ ├── Deploy.jpg │ │ ├── Import.jpg │ │ ├── Import1.jpg │ │ ├── Settings1.jpg │ │ ├── Settings2.jpg │ │ ├── Settings1_1.jpg │ │ ├── Stations1-8.jpg │ │ └── Stations9-16.jpg │ └── README.md ├── flows.json ├── single_relay_example.json └── cycle_relays_example.json ├── python ├── setup.cfg ├── res │ └── sequent.jpg ├── Makefile ├── LICENSE ├── setup.py ├── README.md └── lib8relind │ └── __init__.py ├── Sequent-3D-Enclosure.zip ├── node-red-contrib-sm-8relind ├── icons │ └── relay.png ├── package.json ├── LICENSE ├── README.md ├── 8relind.html └── 8relind.js ├── src ├── comm.h ├── thread.h ├── relay.h ├── comm.c ├── thread.c └── relay.c ├── .gitignore ├── LICENSE ├── Makefile └── README.md /readmeres/revhist.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /node-red/Sprinklers/Pic/Pic.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /python/setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal=1 3 | -------------------------------------------------------------------------------- /python/res/sequent.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/8relind-rpi/HEAD/python/res/sequent.jpg -------------------------------------------------------------------------------- /readmeres/8-RELIND.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/8relind-rpi/HEAD/readmeres/8-RELIND.jpg -------------------------------------------------------------------------------- /readmeres/sequent.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/8relind-rpi/HEAD/readmeres/sequent.jpg -------------------------------------------------------------------------------- /Sequent-3D-Enclosure.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/8relind-rpi/HEAD/Sequent-3D-Enclosure.zip -------------------------------------------------------------------------------- /node-red/Sprinklers/Pic/Deploy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/8relind-rpi/HEAD/node-red/Sprinklers/Pic/Deploy.jpg -------------------------------------------------------------------------------- /node-red/Sprinklers/Pic/Import.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/8relind-rpi/HEAD/node-red/Sprinklers/Pic/Import.jpg -------------------------------------------------------------------------------- /node-red/Sprinklers/Pic/Import1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/8relind-rpi/HEAD/node-red/Sprinklers/Pic/Import1.jpg -------------------------------------------------------------------------------- /node-red/Sprinklers/Pic/Settings1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/8relind-rpi/HEAD/node-red/Sprinklers/Pic/Settings1.jpg -------------------------------------------------------------------------------- /node-red/Sprinklers/Pic/Settings2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/8relind-rpi/HEAD/node-red/Sprinklers/Pic/Settings2.jpg -------------------------------------------------------------------------------- /node-red/Sprinklers/Pic/Settings1_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/8relind-rpi/HEAD/node-red/Sprinklers/Pic/Settings1_1.jpg -------------------------------------------------------------------------------- /node-red/Sprinklers/Pic/Stations1-8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/8relind-rpi/HEAD/node-red/Sprinklers/Pic/Stations1-8.jpg -------------------------------------------------------------------------------- /node-red/Sprinklers/Pic/Stations9-16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/8relind-rpi/HEAD/node-red/Sprinklers/Pic/Stations9-16.jpg -------------------------------------------------------------------------------- /node-red-contrib-sm-8relind/icons/relay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/8relind-rpi/HEAD/node-red-contrib-sm-8relind/icons/relay.png -------------------------------------------------------------------------------- /node-red/flows.json: -------------------------------------------------------------------------------- 1 | [{"id":"e431a168.1a38b","type":"exec","z":"d542cd69.c077f","command":"8relind 0 write ","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"","x":950,"y":720,"wires":[[],[],[]]}] 2 | -------------------------------------------------------------------------------- /src/comm.h: -------------------------------------------------------------------------------- 1 | #ifndef COMM_H_ 2 | #define COMM_H_ 3 | 4 | #include 5 | 6 | int i2cSetup(int addr); 7 | int i2cMem8Read(int dev, int add, uint8_t* buff, int size); 8 | int i2cMem8Write(int dev, int add, uint8_t* buff, int size); 9 | 10 | 11 | #endif //COMM_H_ -------------------------------------------------------------------------------- /src/thread.h: -------------------------------------------------------------------------------- 1 | #ifndef _THREAD_H_ 2 | #define _THREAD_H_ 3 | 4 | #define COUNT_KEY 0 5 | #define YES 1 6 | #define NO 2 7 | #define UNU __attribute__((unused)) 8 | 9 | #define PI_THREAD(X) void *X (UNU void *dummy) 10 | 11 | 12 | void busyWait(int ms); 13 | void startThread(void); 14 | int checkThreadResult(void); 15 | 16 | #endif -------------------------------------------------------------------------------- /python/Makefile: -------------------------------------------------------------------------------- 1 | PYTHON = python 2 | SETUP_PY = setup.py 3 | DIST = dist 4 | 5 | .PHONY: all dist 6 | 7 | all: dist 8 | @echo "Everything done!" 9 | @echo ' > Run "twine upload dist/*" to upload to pypi.org.' 10 | 11 | dist: 12 | @echo "Creating python distribution." 13 | @rm -rf $(DIST) 14 | @$(PYTHON) $(SETUP_PY) sdist bdist_wheel 1>/dev/null 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | # C Cli Binary 55 | 8relind 56 | 57 | #python build dirs 58 | 59 | /python/build/ 60 | /python/dist/ 61 | /python/*.egg-info/ 62 | -------------------------------------------------------------------------------- /node-red-contrib-sm-8relind/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-red-contrib-sm-8relind", 3 | "version": "1.0.3", 4 | "bundleDependencies": false, 5 | "dependencies": { 6 | "i2c-bus": "^5.2.0" 7 | }, 8 | "deprecated": false, 9 | "description": "A Node-RED node to control Sequent Microsystems 8Relay board", 10 | "main": "8relind.js", 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "author": "Sequent Microsystems", 15 | "license": "MIT", 16 | "node-red" : { 17 | "nodes": { 18 | "8relind": "8relind.js" 19 | } 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/SequentMicrosystems/8relind-rpi/tree/master/node-red-contrib-sm-8relind" 24 | }, 25 | "keywords": [ 26 | "node-red", 27 | "node-red-contrib", 28 | "i2c", 29 | "relay" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /src/relay.h: -------------------------------------------------------------------------------- 1 | #ifndef RELAY8_H_ 2 | #define RELAY8_H_ 3 | 4 | #include 5 | 6 | #define RETRY_TIMES 10 7 | #define RELAY8_INPORT_REG_ADD 0x00 8 | #define RELAY8_OUTPORT_REG_ADD 0x01 9 | #define RELAY8_POLINV_REG_ADD 0x02 10 | #define RELAY8_CFG_REG_ADD 0x03 11 | 12 | #define CHANNEL_NR_MIN 1 13 | #define RELAY_CH_NR_MAX 8 14 | 15 | #define ERROR -1 16 | #define OK 0 17 | #define FAIL -1 18 | 19 | #define RELAY8_HW_I2C_BASE_ADD 0x38 20 | #define RELAY8_HW_I2C_ALTERNATE_BASE_ADD 0x20 21 | typedef uint8_t u8; 22 | typedef uint16_t u16; 23 | 24 | typedef enum 25 | { 26 | OFF = 0, 27 | ON, 28 | STATE_COUNT 29 | } OutStateEnumType; 30 | 31 | typedef struct 32 | { 33 | const char* name; 34 | const int namePos; 35 | void(*pFunc)(int, char**); 36 | const char* help; 37 | const char* usage1; 38 | const char* usage2; 39 | const char* example; 40 | }CliCmdType; 41 | 42 | #endif //RELAY8_H_ 43 | -------------------------------------------------------------------------------- /node-red/single_relay_example.json: -------------------------------------------------------------------------------- 1 | [{"id":"bb3172f4c3522c81","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"5cfa30832f06e38d","type":"8relind","z":"bb3172f4c3522c81","name":"","stack":"0","relay":"1","payload":"payload","payloadType":"msg","x":460,"y":260,"wires":[[]]},{"id":"1042b9d0ac8ad01b","type":"ui_switch","z":"bb3172f4c3522c81","name":"relay 1","label":"relay 1","tooltip":"","group":"f2c221f8.b657a","order":1,"width":0,"height":0,"passthru":true,"decouple":"false","topic":"topic","topicType":"msg","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","animate":false,"x":310,"y":260,"wires":[["5cfa30832f06e38d"]]},{"id":"f2c221f8.b657a","type":"ui_group","name":"Relays","tab":"e45fadc8.79108","order":2,"disp":true,"width":"6","collapse":false},{"id":"e45fadc8.79108","type":"ui_tab","name":"Relays","icon":"dashboard","order":1,"disabled":false,"hidden":false}] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Sequent Microsystems 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /python/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Sequent Microsystems 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DESTDIR?=/usr 2 | PREFIX?=/local 3 | 4 | ifneq ($V,1) 5 | Q ?= @ 6 | endif 7 | 8 | CC = gcc 9 | CFLAGS = $(DEBUG) -Wall -Wextra $(INCLUDE) -Winline -pipe 10 | 11 | LDFLAGS = -L$(DESTDIR)$(PREFIX)/lib 12 | LIBS = -lpthread -lrt -lm -lcrypt 13 | 14 | SRC = src/relay.c src/comm.c src/thread.c 15 | 16 | OBJ = $(SRC:.c=.o) 17 | 18 | all: 8relind 19 | 20 | 8relind: $(OBJ) 21 | $Q echo [Link] 22 | $Q $(CC) -o $@ $(OBJ) $(LDFLAGS) $(LIBS) 23 | 24 | .c.o: 25 | $Q echo [Compile] $< 26 | $Q $(CC) -c $(CFLAGS) $< -o $@ 27 | 28 | .PHONY: clean 29 | clean: 30 | $Q echo "[Clean]" 31 | $Q rm -f $(OBJ) 8relind *~ core tags *.bak 32 | 33 | .PHONY: install 34 | install: 8relind 35 | $Q echo "[Install]" 36 | $Q cp 8relind $(DESTDIR)$(PREFIX)/bin 37 | ifneq ($(WIRINGPI_SUID),0) 38 | $Q chown root:root $(DESTDIR)$(PREFIX)/bin/8relind 39 | $Q chmod 4755 $(DESTDIR)$(PREFIX)/bin/8relind 40 | endif 41 | # $Q mkdir -p $(DESTDIR)$(PREFIX)/man/man1 42 | # $Q cp megaio.1 $(DESTDIR)$(PREFIX)/man/man1 43 | 44 | .PHONY: uninstall 45 | uninstall: 46 | $Q echo "[UnInstall]" 47 | $Q rm -f $(DESTDIR)$(PREFIX)/bin/8relind 48 | $Q rm -f $(DESTDIR)$(PREFIX)/man/man1/8relind.1 49 | -------------------------------------------------------------------------------- /node-red-contrib-sm-8relind/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2029 Sequent Microsystems 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /python/setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | setuptools.setup( 4 | name='sm8relind', 5 | packages=setuptools.find_packages(), 6 | version='1.0.4', 7 | license='MIT', 8 | description='Library to control Sequent Microsystems 8relind Card', 9 | author='Sequent Microsystems', 10 | author_email='olcitu@gmail.com', 11 | url='https://sequentmicrosystems.com', 12 | install_requires=[ 13 | "smbus2", 14 | ], 15 | #keywords=['industrial', 'raspberry', 'power', '4-20mA', '0-10V', 'optoisolated'], 16 | classifiers=[ 17 | 'Development Status :: 4 - Beta', 18 | # Chose either "3 - Alpha", "4 - Beta" or "5 - Production/Stable" as the current state of your package 19 | 'Intended Audience :: Developers', 20 | 'Topic :: Software Development :: Build Tools', 21 | 'License :: OSI Approved :: MIT License', 22 | 'Programming Language :: Python :: 2.7', 23 | 'Programming Language :: Python :: 3', 24 | 'Programming Language :: Python :: 3.4', 25 | 'Programming Language :: Python :: 3.5', 26 | 'Programming Language :: Python :: 3.6', 27 | 'Programming Language :: Python :: 3.7', 28 | ], 29 | ) 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![relay8-rpi](readmeres/sequent.jpg)](https://www.sequentmicrosystems.com) 2 | 3 | # 8relind-rpi 4 | 5 | [![relay8-rpi](readmeres/8-RELIND.jpg)](https://www.sequentmicrosystems.com) 6 | 7 | This is the command line and python functions to control the [8-RELAYS Stackable Card for Raspberry Pi](https://sequentmicrosystems.com/products/eight-relays-stackable-card-for-raspberry-pi) Ver. 4.If you use Ver 3.x download from [here](https://github.com/SequentMicrosystems/8relay-rpi). If you have Ver. 1-2 with screw-type connectors download from [here](https://github.com/SequentMicrosystems/relay8-rpi). 8 | 9 | Don't forget to enable I2C communication: 10 | ```bash 11 | ~$ sudo raspi-config 12 | ``` 13 | 14 | ## Usage 15 | 16 | ```bash 17 | ~$ git clone https://github.com/SequentMicrosystems/8relind-rpi.git 18 | ~$ cd 8relind-rpi/ 19 | ~/8relind-rpi$ sudo make install 20 | ``` 21 | 22 | Now you can access all the functions of the relays board through the command "8relind". Use -h option for help: 23 | ```bash 24 | ~$ 8relind -h 25 | ``` 26 | 27 | If you clone the repository any update can be made with the following commands: 28 | 29 | ```bash 30 | ~$ cd 8relind-rpi/ 31 | ~/8relind-rpi$ git pull 32 | ~/8relind-rpi$ sudo make install 33 | ``` 34 | 35 | ### [Python library](https://github.com/SequentMicrosystems/8relind-rpi/tree/master/python) 36 | 37 | ### [Node-Red example based on "exec" node](https://github.com/SequentMicrosystems/8relind-rpi/tree/master/node-red) 38 | 39 | ### [Node-Red "8relind" node](https://github.com/SequentMicrosystems/8relind-rpi/tree/master/node-red-contrib-sm-8relind) 40 | 41 | -------------------------------------------------------------------------------- /python/README.md: -------------------------------------------------------------------------------- 1 | 2 | [![8relind-rpi](res/sequent.jpg)](https://sequentmicrosystems.com) 3 | 4 | # lib8relind-RPI 5 | 6 | This is the python library to control the [8-RELAYS Stackable Card for Raspberry Pi](https://sequentmicrosystems.com/product/raspberry-pi-relays-stackable-card/). 7 | 8 | ## Install 9 | 10 | ```bash 11 | sudo pip install SM8relind 12 | ``` 13 | 14 | ## Usage 15 | 16 | Now you can import the lib8relind library and use its functions. To test, read relays status from the board with stack level 0: 17 | 18 | ```bash 19 | ~$ python 20 | Python 2.7.9 (default, Sep 17 2016, 20:26:04) 21 | [GCC 4.9.2] on linux2 22 | Type "help", "copyright", "credits" or "license" for more information. 23 | >>> import lib8relind 24 | >>> lib8relind.get_all(0) 25 | 0 26 | >>> 27 | ``` 28 | 29 | ## Functions 30 | 31 | ### set(stack, relay, value) 32 | Set one relay state. 33 | 34 | stack - stack level of the 8-Relay card (selectable from address jumpers [0..7]) 35 | 36 | relay - relay number (id) [1..8] 37 | 38 | value - relay state 1: turn ON, 0: turn OFF[0..1] 39 | 40 | 41 | ### set_all(stack, value) 42 | Set all relays state. 43 | 44 | stack - stack level of the 8-Relay card (selectable from address jumpers [0..7]) 45 | 46 | value - 8 bit value of all relays (ex: 255: turn on all relays, 0: turn off all relays, 1:turn on relay #1 and off the rest) 47 | 48 | ### get(stack, relay) 49 | Get one relay state. 50 | 51 | stack - stack level of the 8-Relay card (selectable from address jumpers [0..7]) 52 | 53 | relay - relay number (id) [1..8] 54 | 55 | return 0 == relay off; 1 - relay on 56 | 57 | ### get_all(stack) 58 | Return the state of all relays. 59 | 60 | stack - stack level of the 8-Relay card (selectable from address jumpers [0..7]) 61 | 62 | return - [0..255] 63 | 64 | -------------------------------------------------------------------------------- /node-red/Sprinklers/README.md: -------------------------------------------------------------------------------- 1 | # Sprinklers 2 | 3 | This is an 64 stations irrigation system created with Sequent Microsystems [Eight RELAYS 8-Layer Stackable HAT for Raspberry Pi](https://sequentmicrosystems.com/collections/industrial-automation/products/8-relays-stackable-card-for-raspberry-pi) 4 | 5 | ## Installing the software 6 | 7 | * Install NodeRed following the instructions from [HERE](https://help.ubidots.com/en/articles/1958375-how-to-install-node-red-in-raspberry-pi). 8 | 9 | * Run ```sudo raspi-config``` and enable I2C communication. 10 | 11 | * Connect up to 8 [Eight RELAYS Cards](https://sequentmicrosystems.com/collections/industrial-automation/products/8-relays-stackable-card-for-raspberry-pi) to your raspberry, each with different stack level jumper settings. 12 | 13 | * Install the node for [Eight RELAYS Card](https://sequentmicrosystems.com/collections/industrial-automation/products/8-relays-stackable-card-for-raspberry-pi) by opening the Node-RED interface in a browser: Go to Menu > Manage pallete > Install and search for node-red-contrib-sm-8relind, install the node and restart the Node-RED 14 | 15 | * Import the Sprinkler controller flow: Go to Menu > Import > select a file to import. Load the provided [flows.json](https://github.com/SequentMicrosystems/8relind-rpi/blob/main/node-red/Sprinklers/flows.json) file and click the Import button. 16 | ![import](Pic/Import1.jpg) 17 | 18 | * Click on the top rigt ```Deploy``` button. 19 | 20 | * Go to the dashboard and select the Settings menu 21 | ![Settings](Pic/Settings2.jpg) 22 | 23 | * Setup the weather and operating mode: 24 | ![Settings1](Pic/Settings1_1.jpg) 25 | 26 | * Enable each channel per day of the week from the Stations tab. The software detects how many cards are installed and creates a page for each card. 27 | ![Stations1-8](Pic/Stations1-8.jpg) 28 | 29 | 30 | -------------------------------------------------------------------------------- /node-red-contrib-sm-8relind/README.md: -------------------------------------------------------------------------------- 1 | # node-red-contrib-sm-8relind 2 | 3 | This is the node-red node to control Sequent Microsystems [8-RELAYS Stackable Card for Raspberry Pi](https://sequentmicrosystems.com/collections/all-io-cards/products/raspberry-pi-relays-stackable-card/). 4 | 5 | ## Manual Install 6 | 7 | Clone or update the repository, follow the instructions from the [first page.](https://github.com/SequentMicrosystems/8relind-rpi) 8 | 9 | In your node-red user directory, tipicaly ~/.node-red, 10 | 11 | ```bash 12 | ~$ cd ~/.node-red 13 | ``` 14 | 15 | Run the following command: 16 | 17 | ```bash 18 | ~/.node-red$ npm install ~/8relind-rpi/node-red-contrib-sm-8relind 19 | ``` 20 | 21 | In order to see the node in the palette and use-it you need to restart node-red. If you run node-red as a service: 22 | ```bash 23 | ~$ node-red-stop 24 | ~& node-red-start 25 | ``` 26 | 27 | ## Usage 28 | 29 | After install and restart the node-red you will see on the node palete, under Sequent Microsystems category the "8relind" and "8relindrd" nodes. 30 | 31 | ### 8relind node 32 | This node will turn on or off a relay or all relays as a 8 bits bitmap. The card stack level and relay number can be set in the dialog screen or dinamicaly thru ``` msg.stack``` and ``` msg.relay ```. The output of one relay or all 8 relays if you set the the relay number to 0, can be set dynamically using ``` msg.payload ```. 33 | 34 | ### 8relindrd node 35 | Thi node will read one relay state or all relays states as a 8 bits bitmap. The card stack level and relay number can be set in the dialog screen or dinamicaly thru ``` msg.stack``` and ``` msg.relay ``` and the state is output as ``` msg.payload ``` .If you set the relay number to 0 the node will output the state of all relays. 36 | 37 | ## Important note 38 | 39 | This node is using the I2C-bus package from @fivdi, you can visit his work on github [here](https://github.com/fivdi/i2c-bus). 40 | The inspiration for this node came from @nielsnl68 work with [node-red-contrib-i2c](https://github.com/nielsnl68/node-red-contrib-i2c).We thank them for the great job. 41 | -------------------------------------------------------------------------------- /node-red/cycle_relays_example.json: -------------------------------------------------------------------------------- 1 | [{"id":"bb3172f4c3522c81","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"5cfa30832f06e38d","type":"8relind","z":"bb3172f4c3522c81","name":"","stack":"0","relay":"0","payload":"payload","payloadType":"msg","x":610,"y":260,"wires":[[]]},{"id":"5538cb4053e4bfec","type":"function","z":"bb3172f4c3522c81","name":"","func":"var relays=flow.get('relays') || 0;\nvar turn = flow.get('turns') || 0;\nif(relays == 0)\n{\n turn = 1;\n \n}\nif(relays == 255)\n{\n turn = 0;\n}\nrelays = 0xff & ( relays << 1) + turn;\n\nmsg.payload = relays;\nflow.set('relays', relays);\nflow.set('turns', turn); \nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":460,"y":260,"wires":[["5cfa30832f06e38d"]]},{"id":"bf5c35a6ddfdc7af","type":"inject","z":"bb3172f4c3522c81","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"0.2","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":150,"y":240,"wires":[["c74f0a1e44a9964d"]]},{"id":"bd7072a9097ddffc","type":"ui_switch","z":"bb3172f4c3522c81","name":"","label":"switch","tooltip":"","group":"f2c221f8.b657a","order":0,"width":0,"height":0,"passthru":true,"decouple":"false","topic":"topic","topicType":"msg","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","animate":false,"x":290,"y":120,"wires":[["3855cf1ab2aa18de"]]},{"id":"c74f0a1e44a9964d","type":"switch","z":"bb3172f4c3522c81","name":"","property":"enable","propertyType":"flow","rules":[{"t":"eq","v":"1","vt":"num"}],"checkall":"true","repair":false,"outputs":1,"x":290,"y":240,"wires":[["5538cb4053e4bfec"]]},{"id":"3855cf1ab2aa18de","type":"change","z":"bb3172f4c3522c81","name":"","rules":[{"t":"set","p":"enable","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":480,"y":120,"wires":[[]]},{"id":"f2c221f8.b657a","type":"ui_group","name":"Relays","tab":"e45fadc8.79108","order":2,"disp":true,"width":"6","collapse":false},{"id":"e45fadc8.79108","type":"ui_tab","name":"Relays","icon":"dashboard","order":1,"disabled":false,"hidden":false}] -------------------------------------------------------------------------------- /src/comm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * comm.c: 3 | * Communication routines "platform specific" for Raspberry Pi 4 | * 5 | * Copyright (c) 2016-2020 Sequent Microsystem 6 | * 7 | *********************************************************************** 8 | * Author: Alexandru Burcea 9 | *********************************************************************** 10 | */ 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "comm.h" 19 | 20 | #define I2C_SLAVE 0x0703 21 | #define I2C_SMBUS 0x0720 /* SMBus-level access */ 22 | 23 | #define I2C_SMBUS_READ 1 24 | #define I2C_SMBUS_WRITE 0 25 | 26 | // SMBus transaction types 27 | 28 | #define I2C_SMBUS_QUICK 0 29 | #define I2C_SMBUS_BYTE 1 30 | #define I2C_SMBUS_BYTE_DATA 2 31 | #define I2C_SMBUS_WORD_DATA 3 32 | #define I2C_SMBUS_PROC_CALL 4 33 | #define I2C_SMBUS_BLOCK_DATA 5 34 | #define I2C_SMBUS_I2C_BLOCK_BROKEN 6 35 | #define I2C_SMBUS_BLOCK_PROC_CALL 7 /* SMBus 2.0 */ 36 | #define I2C_SMBUS_I2C_BLOCK_DATA 8 37 | 38 | // SMBus messages 39 | 40 | #define I2C_SMBUS_BLOCK_MAX 32 /* As specified in SMBus standard */ 41 | #define I2C_SMBUS_I2C_BLOCK_MAX 32 /* Not specified but we use same structure */ 42 | 43 | 44 | int i2cSetup(int addr) 45 | { 46 | int file; 47 | char filename[40]; 48 | sprintf(filename, "/dev/i2c-1"); 49 | 50 | if ( (file = open(filename, O_RDWR)) < 0) 51 | { 52 | printf("Failed to open the bus."); 53 | return -1; 54 | } 55 | if (ioctl(file, I2C_SLAVE, addr) < 0) 56 | { 57 | printf("Failed to acquire bus access and/or talk to slave.\n"); 58 | return -1; 59 | } 60 | 61 | return file; 62 | } 63 | 64 | int i2cMem8Read(int dev, int add, uint8_t* buff, int size) 65 | { 66 | uint8_t intBuff[I2C_SMBUS_BLOCK_MAX]; 67 | 68 | if (NULL == buff) 69 | { 70 | return -1; 71 | } 72 | 73 | if (size > I2C_SMBUS_BLOCK_MAX) 74 | { 75 | return -1; 76 | } 77 | 78 | intBuff[0] = 0xff & add; 79 | 80 | if (write(dev, intBuff, 1) != 1) 81 | { 82 | //printf("Fail to select mem add!\n"); 83 | return -1; 84 | } 85 | if (read(dev, buff, size) != size) 86 | { 87 | //printf("Fail to read memory!\n"); 88 | return -1; 89 | } 90 | return 0; //OK 91 | } 92 | 93 | int i2cMem8Write(int dev, int add, uint8_t* buff, int size) 94 | { 95 | uint8_t intBuff[I2C_SMBUS_BLOCK_MAX]; 96 | 97 | if (NULL == buff) 98 | { 99 | return -1; 100 | } 101 | 102 | if (size > I2C_SMBUS_BLOCK_MAX - 1) 103 | { 104 | return -1; 105 | } 106 | 107 | intBuff[0] = 0xff & add; 108 | memcpy(&intBuff[1], buff, size); 109 | 110 | if (write(dev, intBuff, size + 1) != size + 1) 111 | { 112 | //printf("Fail to write memory!\n"); 113 | return -1; 114 | } 115 | return 0; 116 | } 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /src/thread.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "thread.h" 7 | 8 | 9 | static pthread_mutex_t piMutexes [4]; 10 | 11 | int piHiPri (const int pri); 12 | int piThreadCreate (void *(*fn)(void *)); 13 | static volatile int globalResponse = 0; 14 | 15 | PI_THREAD (waitForKey) 16 | { 17 | char resp; 18 | int respI = NO; 19 | 20 | 21 | struct termios info; 22 | tcgetattr(0, &info); /* get current terminal attirbutes; 0 is the file descriptor for stdin */ 23 | info.c_lflag &= ~ICANON; /* disable canonical mode */ 24 | info.c_cc[VMIN] = 1; /* wait until at least one keystroke available */ 25 | info.c_cc[VTIME] = 0; /* no timeout */ 26 | tcsetattr(0, TCSANOW, &info); /* set i */ 27 | 28 | (void)piHiPri (10) ; // Set this thread to be high priority 29 | resp = getchar(); 30 | if((resp == 'y')||(resp == 'Y')) 31 | { 32 | respI = YES; 33 | } 34 | 35 | pthread_mutex_lock(&piMutexes[COUNT_KEY]); 36 | globalResponse = respI; 37 | pthread_mutex_unlock(&piMutexes[COUNT_KEY]); 38 | 39 | info.c_lflag |= ICANON; /* disable canonical mode */ 40 | info.c_cc[VMIN] = 0; /* wait until at least one keystroke available */ 41 | info.c_cc[VTIME] = 0; /* no timeout */ 42 | tcsetattr(0, TCSANOW, &info); /* set i */ 43 | printf("\n"); 44 | return &waitForKey; 45 | } 46 | 47 | /* 48 | * upHiPri: 49 | * Attempt to set a high priority scheduling for the running program 50 | ********************************************************************************* 51 | */ 52 | 53 | int piHiPri (const int pri) 54 | { 55 | struct sched_param sched ; 56 | 57 | memset (&sched, 0, sizeof(sched)) ; 58 | 59 | if (pri > sched_get_priority_max (SCHED_RR)) 60 | sched.sched_priority = sched_get_priority_max (SCHED_RR) ; 61 | else 62 | sched.sched_priority = pri ; 63 | 64 | return sched_setscheduler (0, SCHED_RR, &sched) ; 65 | } 66 | 67 | /* 68 | * upThreadCreate: 69 | * Create and start a thread 70 | ********************************************************************************* 71 | */ 72 | 73 | int piThreadCreate (void *(*fn)(void *)) 74 | { 75 | pthread_t myThread ; 76 | 77 | return pthread_create (&myThread, NULL, fn, NULL) ; 78 | } 79 | 80 | void startThread(void) 81 | { 82 | piThreadCreate(waitForKey); 83 | } 84 | 85 | int checkThreadResult(void) 86 | { 87 | int res; 88 | pthread_mutex_lock(&piMutexes[COUNT_KEY]); 89 | res = globalResponse; 90 | pthread_mutex_unlock(&piMutexes[COUNT_KEY]); 91 | return res; 92 | } 93 | 94 | /* 95 | * busyWait: 96 | * Wait for some number of milliseconds 97 | ********************************************************************************* 98 | */ 99 | 100 | void busyWait(int ms) 101 | { 102 | struct timespec sleeper, dummy ; 103 | 104 | sleeper.tv_sec = (time_t)(ms / 1000) ; 105 | sleeper.tv_nsec = (long)(ms % 1000) * 1000000 ; 106 | 107 | nanosleep (&sleeper, &dummy) ; 108 | } 109 | -------------------------------------------------------------------------------- /python/lib8relind/__init__.py: -------------------------------------------------------------------------------- 1 | import smbus2 2 | 3 | # bus = smbus2.SMBus(1) # 0 = /dev/i2c-0 (port I2C0), 1 = /dev/i2c-1 (port I2C1) 4 | 5 | DEVICE_ADDRESS = 0x38 # 7 bit address (will be left shifted to add the read write bit) 6 | ALTERNATE_DEVICE_ADDRESS = 0x20 # 7 bit address (will be left shifted to add the read write bit) 7 | 8 | RELAY8_INPORT_REG_ADD = 0x00 9 | RELAY8_OUTPORT_REG_ADD = 0x01 10 | RELAY8_POLINV_REG_ADD = 0x02 11 | RELAY8_CFG_REG_ADD = 0x03 12 | 13 | relayMaskRemap = [0x01, 0x04, 0x40, 0x10, 0x20, 0x80, 0x08, 0x02] 14 | relayChRemap = [0, 2, 6, 4, 5, 7, 3, 1] 15 | 16 | 17 | def __relayToIO(relay): 18 | val = 0 19 | for i in range(0, 8): 20 | if (relay & (1 << i)) != 0: 21 | val = val + relayMaskRemap[i] 22 | return val 23 | 24 | 25 | def __IOToRelay(iov): 26 | val = 0 27 | for i in range(0, 8): 28 | if (iov & relayMaskRemap[i]) != 0: 29 | val = val + (1 << i) 30 | return val 31 | 32 | 33 | def __check(bus, add): 34 | cfg = bus.read_byte_data(add, RELAY8_CFG_REG_ADD) 35 | if cfg != 0: 36 | bus.write_byte_data(add, RELAY8_OUTPORT_REG_ADD, 0) 37 | bus.write_byte_data(add, RELAY8_CFG_REG_ADD, 0) 38 | return bus.read_byte_data(add, RELAY8_INPORT_REG_ADD) 39 | 40 | 41 | def set(stack, relay, value): 42 | if stack < 0 or stack > 7: 43 | raise ValueError('Invalid stack level!') 44 | stack = 0x07 ^ stack 45 | if relay < 1: 46 | raise ValueError('Invalid relay number!') 47 | if relay > 8: 48 | raise ValueError('Invalid relay number!') 49 | bus = smbus2.SMBus(1) 50 | hwAdd = DEVICE_ADDRESS + stack 51 | try: 52 | oldVal = __check(bus, hwAdd) 53 | except Exception as e: 54 | hwAdd = ALTERNATE_DEVICE_ADDRESS + stack 55 | try: 56 | oldVal = __check(bus, hwAdd) 57 | except Exception as e: 58 | bus.close() 59 | raise ValueError('8-relay card not detected!') 60 | oldVal = __IOToRelay(oldVal) 61 | try: 62 | if value == 0: 63 | oldVal = oldVal & (~(1 << (relay - 1))) 64 | oldVal = __relayToIO(oldVal) 65 | bus.write_byte_data(hwAdd, RELAY8_OUTPORT_REG_ADD, oldVal) 66 | else: 67 | oldVal = oldVal | (1 << (relay - 1)) 68 | oldVal = __relayToIO(oldVal) 69 | bus.write_byte_data(hwAdd, RELAY8_OUTPORT_REG_ADD, oldVal) 70 | except Exception as e: 71 | bus.close() 72 | raise ValueError('Fail to write relay state value!') 73 | bus.close() 74 | 75 | 76 | def set_all(stack, value): 77 | if stack < 0 or stack > 7: 78 | raise ValueError('Invalid stack level!') 79 | stack = 0x07 ^ stack 80 | if value > 255: 81 | raise ValueError('Invalid relay value!') 82 | if value < 0: 83 | raise ValueError('Invalid relay value!') 84 | 85 | bus = smbus2.SMBus(1) 86 | hwAdd = DEVICE_ADDRESS + stack 87 | try: 88 | oldVal = __check(bus, hwAdd) 89 | except Exception as e: 90 | hwAdd = ALTERNATE_DEVICE_ADDRESS + stack 91 | try: 92 | oldVal = __check(bus, hwAdd) 93 | except Exception as e: 94 | bus.close() 95 | raise ValueError('8-relay card not detected!') 96 | value = __relayToIO(value) 97 | try: 98 | bus.write_byte_data(hwAdd, RELAY8_OUTPORT_REG_ADD, value) 99 | except Exception as e: 100 | bus.close() 101 | raise ValueError('Fail to write relay state value!') 102 | bus.close() 103 | 104 | 105 | def get(stack, relay): 106 | if stack < 0 or stack > 7: 107 | raise ValueError('Invalid stack level!') 108 | stack = 0x07 ^ stack 109 | if relay < 1: 110 | raise ValueError('Invalid relay number!') 111 | if relay > 8: 112 | raise ValueError('Invalid relay number!') 113 | bus = smbus2.SMBus(1) 114 | hwAdd = DEVICE_ADDRESS + stack 115 | try: 116 | val = __check(bus, hwAdd) 117 | except Exception as e: 118 | hwAdd = ALTERNATE_DEVICE_ADDRESS + stack 119 | try: 120 | val = __check(bus, hwAdd) 121 | except Exception as e: 122 | bus.close() 123 | raise ValueError('8-relay card not detected!') 124 | 125 | val = __IOToRelay(val) 126 | val = val & (1 << (relay - 1)) 127 | bus.close() 128 | if val == 0: 129 | return 0 130 | else: 131 | return 1 132 | 133 | 134 | def get_all(stack): 135 | if stack < 0 or stack > 7: 136 | raise ValueError('Invalid stack level!') 137 | stack = 0x07 ^ stack 138 | bus = smbus2.SMBus(1) 139 | hwAdd = DEVICE_ADDRESS + stack 140 | try: 141 | val = __check(bus, hwAdd) 142 | except Exception as e: 143 | hwAdd = ALTERNATE_DEVICE_ADDRESS + stack 144 | try: 145 | val = __check(bus, hwAdd) 146 | except Exception as e: 147 | bus.close() 148 | raise ValueError('8-relay card not detected!') 149 | 150 | val = __IOToRelay(val) 151 | bus.close() 152 | return val 153 | -------------------------------------------------------------------------------- /node-red-contrib-sm-8relind/8relind.html: -------------------------------------------------------------------------------- 1 | 21 | 22 | 29 | 30 | 92 | 93 | 113 | 114 | 120 | 121 | 183 | -------------------------------------------------------------------------------- /node-red-contrib-sm-8relind/8relind.js: -------------------------------------------------------------------------------- 1 | module.exports = function(RED) { 2 | "use strict"; 3 | var I2C = require("i2c-bus"); 4 | const DEFAULT_HW_ADD = 0x20; 5 | const ALTERNATE_HW_ADD = 0x38; 6 | const OUT_REG = 0x01; 7 | const CFG_REG = 0x03; 8 | const mask = new ArrayBuffer(8); 9 | mask[0] = 0x01; 10 | mask[1] = 0x04; 11 | mask[2] = 0x40; 12 | mask[3] = 0x10; 13 | mask[4] = 0x20; 14 | mask[5] = 0x80; 15 | mask[6] = 0x08; 16 | mask[7] = 0x02; 17 | // The relay Node 18 | function RelayNode(n) { 19 | RED.nodes.createNode(this, n); 20 | this.stack = parseInt(n.stack); 21 | this.relay = parseInt(n.relay); 22 | this.payload = n.payload; 23 | this.payloadType = n.payloadType; 24 | var node = this; 25 | 26 | node.port = I2C.openSync( 1 ); 27 | node.on("input", function(msg) { 28 | var myPayload; 29 | var stack = node.stack; 30 | if (isNaN(stack)) stack = msg.stack; 31 | stack = parseInt(stack); 32 | var relay = node.relay; 33 | if (isNaN(relay)) relay = msg.relay; 34 | relay = parseInt(relay); 35 | //var buffcount = parseInt(node.count); 36 | if (isNaN(stack + 1)) { 37 | this.status({fill:"red",shape:"ring",text:"Stack level ("+stack+") value is missing or incorrect"}); 38 | return; 39 | } else if (isNaN(relay) ) { 40 | this.status({fill:"red",shape:"ring",text:"Relay number ("+relay+") value is missing or incorrect"}); 41 | return; 42 | } else { 43 | this.status({}); 44 | } 45 | var hwAdd = DEFAULT_HW_ADD; 46 | var found = 1; 47 | if(stack < 0){ 48 | stack = 0; 49 | } 50 | if(stack > 7){ 51 | stack = 7; 52 | } 53 | //check the type of io_expander 54 | hwAdd += stack ^ 0x07; 55 | var direction = 0xaa; 56 | try{ 57 | direction = node.port.readByteSync(hwAdd, CFG_REG ); 58 | }catch(err) { 59 | hwAdd = ALTERNATE_HW_ADD; 60 | hwAdd += stack ^ 0x07; 61 | try{ 62 | direction = node.port.readByteSync(hwAdd, CFG_REG ); 63 | }catch(err) { 64 | found = 0; 65 | this.error(err,msg); 66 | } 67 | } 68 | 69 | if(1 == found){ 70 | try { 71 | if (this.payloadType == null) { 72 | myPayload = this.payload; 73 | } else if (this.payloadType == 'none') { 74 | myPayload = null; 75 | } else { 76 | myPayload = RED.util.evaluateNodeProperty(this.payload, this.payloadType, this,msg); 77 | } 78 | //node.log('Direction ' + String(direction)); 79 | if(direction != 0x00){ 80 | node.port.writeByteSync(hwAdd, OUT_REG, 0x00); 81 | //node.log('First update output'); 82 | node.port.writeByteSync(hwAdd, CFG_REG, 0x00); 83 | //node.log('First update direction'); 84 | } 85 | var relayVal = 0; 86 | 87 | //node.log('Relays ' + String(relayVal)); 88 | if(relay < 0){ 89 | relay = 0; 90 | } 91 | if(relay > 8){ 92 | relay = 8; 93 | } 94 | if(relay > 0) 95 | { 96 | relayVal = node.port.readByteSync(hwAdd, OUT_REG); 97 | relay-= 1;//zero based 98 | if (myPayload == null || myPayload == false || myPayload == 0 || myPayload == 'off') { 99 | relayVal &= ~mask[relay]; 100 | } else { 101 | relayVal |= mask[relay]; 102 | } 103 | } 104 | else if(myPayload >= 0 && myPayload < 256) 105 | { 106 | var i = 0; 107 | for(i = 0; i<8; i++){ 108 | if(( (1 << i) & myPayload) != 0){ 109 | relayVal |= mask[i]; 110 | } 111 | } 112 | } 113 | node.port.writeByte(hwAdd, OUT_REG, relayVal, function(err) { 114 | if (err) { node.error(err, msg); 115 | } else { 116 | node.send(msg); 117 | } 118 | }); 119 | } catch(err) { 120 | this.error(err,msg); 121 | } 122 | } 123 | }); 124 | 125 | node.on("close", function() { 126 | node.port.closeSync(); 127 | }); 128 | } 129 | RED.nodes.registerType("8relind", RelayNode); 130 | 131 | // The relay read Node 132 | function RelayReadNode(n) { 133 | RED.nodes.createNode(this, n); 134 | this.stack = parseInt(n.stack); 135 | this.relay = parseInt(n.relay); 136 | this.payload = n.payload; 137 | this.payloadType = n.payloadType; 138 | var node = this; 139 | 140 | node.port = I2C.openSync( 1 ); 141 | node.on("input", function(msg) { 142 | var myPayload; 143 | var stack = node.stack; 144 | if (isNaN(stack)) stack = msg.stack; 145 | stack = parseInt(stack); 146 | var relay = node.relay; 147 | if (isNaN(relay)) relay = msg.relay; 148 | relay = parseInt(relay); 149 | //var buffcount = parseInt(node.count); 150 | if (isNaN(stack + 1)) { 151 | this.status({fill:"red",shape:"ring",text:"Stack level ("+stack+") value is missing or incorrect"}); 152 | return; 153 | } else if (isNaN(relay) ) { 154 | this.status({fill:"red",shape:"ring",text:"Relay number ("+relay+") value is missing or incorrect"}); 155 | return; 156 | } else { 157 | this.status({}); 158 | } 159 | var hwAdd = DEFAULT_HW_ADD; 160 | var found = 1; 161 | if(stack < 0){ 162 | stack = 0; 163 | } 164 | if(stack > 7){ 165 | stack = 7; 166 | } 167 | //check the type of io_expander 168 | hwAdd += stack ^ 0x07; 169 | var direction = 0xaa; 170 | try{ 171 | direction = node.port.readByteSync(hwAdd, CFG_REG ); 172 | }catch(err) { 173 | hwAdd = ALTERNATE_HW_ADD; 174 | hwAdd += stack ^ 0x07; 175 | try{ 176 | direction = node.port.readByteSync(hwAdd, CFG_REG ); 177 | }catch(err) { 178 | found = 0; 179 | this.error(err,msg); 180 | } 181 | } 182 | 183 | if(1 == found){ 184 | try { 185 | if (this.payloadType == null) { 186 | myPayload = this.payload; 187 | } else if (this.payloadType == 'none') { 188 | myPayload = null; 189 | } else { 190 | myPayload = RED.util.evaluateNodeProperty(this.payload, this.payloadType, this,msg); 191 | } 192 | 193 | if(direction != 0x00){ 194 | node.port.writeByteSync(hwAdd, OUT_REG, 0x00); 195 | 196 | node.port.writeByteSync(hwAdd, CFG_REG, 0x00); 197 | } 198 | var relayVal = 0; 199 | relayVal = node.port.readByteSync(hwAdd, OUT_REG); 200 | if(relay < 0){ 201 | relay = 0; 202 | } 203 | if(relay > 8){ 204 | relay = 8; 205 | } 206 | if(relay > 0){ 207 | relay-= 1;//zero based 208 | if(relayVal & mask[relay]) 209 | { 210 | msg.payload = 1; 211 | } 212 | else 213 | { 214 | msg.payload = 0; 215 | } 216 | } 217 | else { 218 | var i; 219 | var outVal; 220 | outVal = 0; 221 | for(i = 0; i< 8; i++) 222 | { 223 | if( relayVal & mask[i]) 224 | { 225 | outVal += 1 << i; 226 | } 227 | } 228 | msg.payload = outVal; 229 | } 230 | node.send(msg); 231 | } catch(err) { 232 | this.error(err,msg); 233 | } 234 | } 235 | }); 236 | 237 | node.on("close", function() { 238 | node.port.closeSync(); 239 | }); 240 | } 241 | RED.nodes.registerType("8relindrd", RelayReadNode); 242 | 243 | } 244 | -------------------------------------------------------------------------------- /src/relay.c: -------------------------------------------------------------------------------- 1 | /* 2 | * relay.c: 3 | * Command-line interface to the Raspberry 4 | * Pi's 8-Relay Industrial board. 5 | * Copyright (c) 2016-2021 Sequent Microsystem 6 | * 7 | *********************************************************************** 8 | * Author: Alexandru Burcea 9 | *********************************************************************** 10 | */ 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "relay.h" 17 | #include "comm.h" 18 | #include "thread.h" 19 | 20 | #define VERSION_BASE (int)1 21 | #define VERSION_MAJOR (int)1 22 | #define VERSION_MINOR (int)1 23 | 24 | #define UNUSED(X) (void)X /* To avoid gcc/g++ warnings */ 25 | #define CMD_ARRAY_SIZE 7 26 | 27 | const u8 relayMaskRemap[8] = 28 | { 29 | 0x01, 30 | 0x04, 31 | 0x40, 32 | 0x10, 33 | 0x20, 34 | 0x80, 35 | 0x08, 36 | 0x02}; 37 | const int relayChRemap[8] = 38 | { 39 | 0, 40 | 2, 41 | 6, 42 | 4, 43 | 5, 44 | 7, 45 | 3, 46 | 1}; 47 | 48 | int relayChSet(int dev, u8 channel, OutStateEnumType state); 49 | int relayChGet(int dev, u8 channel, OutStateEnumType* state); 50 | u8 relayToIO(u8 relay); 51 | u8 IOToRelay(u8 io); 52 | 53 | static void doHelp(int argc, char *argv[]); 54 | const CliCmdType CMD_HELP = 55 | { 56 | "-h", 57 | 1, 58 | &doHelp, 59 | "\t-h Display the list of command options or one command option details\n", 60 | "\tUsage: 8relind -h Display command options list\n", 61 | "\tUsage: 8relind -h Display help for command option\n", 62 | "\tExample: 8relind -h write Display help for \"write\" command option\n"}; 63 | 64 | static void doVersion(int argc, char *argv[]); 65 | const CliCmdType CMD_VERSION = 66 | { 67 | "-v", 68 | 1, 69 | &doVersion, 70 | "\t-v Display the version number\n", 71 | "\tUsage: 8relind -v\n", 72 | "", 73 | "\tExample: 8relind -v Display the version number\n"}; 74 | 75 | static void doWarranty(int argc, char* argv[]); 76 | const CliCmdType CMD_WAR = 77 | { 78 | "-warranty", 79 | 1, 80 | &doWarranty, 81 | "\t-warranty Display the warranty\n", 82 | "\tUsage: 8relind -warranty\n", 83 | "", 84 | "\tExample: 8relind -warranty Display the warranty text\n"}; 85 | 86 | static void doList(int argc, char *argv[]); 87 | const CliCmdType CMD_LIST = 88 | { 89 | "-list", 90 | 1, 91 | &doList, 92 | "\t-list: List all 8relind boards connected,\n\treturn nr of boards and stack level for every board\n", 93 | "\tUsage: 8relind -list\n", 94 | "", 95 | "\tExample: 8relind -list display: 1,0 \n"}; 96 | 97 | static void doRelayWrite(int argc, char *argv[]); 98 | const CliCmdType CMD_WRITE = 99 | { 100 | "write", 101 | 2, 102 | &doRelayWrite, 103 | "\twrite: Set relays On/Off\n", 104 | "\tUsage: 8relind write \n", 105 | "\tUsage: 8relind write \n", 106 | "\tExample: 8relind 0 write 2 On; Set Relay #2 on Board #0 On\n"}; 107 | 108 | static void doRelayRead(int argc, char *argv[]); 109 | const CliCmdType CMD_READ = 110 | { 111 | "read", 112 | 2, 113 | &doRelayRead, 114 | "\tread: Read relays status\n", 115 | "\tUsage: 8relind read \n", 116 | "\tUsage: 8relind read\n", 117 | "\tExample: 8relind 0 read 2; Read Status of Relay #2 on Board #0\n"}; 118 | 119 | static void doTest(int argc, char* argv[]); 120 | const CliCmdType CMD_TEST = 121 | { 122 | "test", 123 | 2, 124 | &doTest, 125 | "\ttest: Turn ON and OFF the relays until press a key\n", 126 | "", 127 | "\tUsage: 8relind test\n", 128 | "\tExample: 8relind 0 test\n"}; 129 | 130 | CliCmdType gCmdArray[CMD_ARRAY_SIZE]; 131 | 132 | char *usage = "Usage: 8relind -h \n" 133 | " 8relind -v\n" 134 | " 8relind -warranty\n" 135 | " 8relind -list\n" 136 | " 8relind write \n" 137 | " 8relind write \n" 138 | " 8relind read \n" 139 | " 8relind read\n" 140 | " 8relind test\n" 141 | "Where: = Board level id = 0..7\n" 142 | "Type 8relind -h for more help"; // No trailing newline needed here. 143 | 144 | char *warranty = 145 | " Copyright (c) 2016-2020 Sequent Microsystems\n" 146 | " \n" 147 | " This program is free software; you can redistribute it and/or modify\n" 148 | " it under the terms of the GNU Leser General Public License as published\n" 149 | " by the Free Software Foundation, either version 3 of the License, or\n" 150 | " (at your option) any later version.\n" 151 | " \n" 152 | " This program is distributed in the hope that it will be useful,\n" 153 | " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" 154 | " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" 155 | " GNU Lesser General Public License for more details.\n" 156 | " \n" 157 | " You should have received a copy of the GNU Lesser General Public License\n" 158 | " along with this program. If not, see ."; 159 | u8 relayToIO(u8 relay) 160 | { 161 | u8 i; 162 | u8 val = 0; 163 | for (i = 0; i < 8; i++) 164 | { 165 | if ( (relay & (1 << i)) != 0) 166 | val += relayMaskRemap[i]; 167 | } 168 | return val; 169 | } 170 | 171 | u8 IOToRelay(u8 io) 172 | { 173 | u8 i; 174 | u8 val = 0; 175 | for (i = 0; i < 8; i++) 176 | { 177 | if ( (io & relayMaskRemap[i]) != 0) 178 | { 179 | val += 1 << i; 180 | } 181 | } 182 | return val; 183 | } 184 | 185 | int relayChSet(int dev, u8 channel, OutStateEnumType state) 186 | { 187 | int resp; 188 | u8 buff[2]; 189 | 190 | if ( (channel < CHANNEL_NR_MIN) || (channel > RELAY_CH_NR_MAX)) 191 | { 192 | printf("Invalid relay nr!\n"); 193 | return ERROR; 194 | } 195 | if (FAIL == i2cMem8Read(dev, RELAY8_INPORT_REG_ADD, buff, 1)) 196 | { 197 | return FAIL; 198 | } 199 | 200 | switch (state) 201 | { 202 | case OFF: 203 | buff[0] &= ~ (1 << relayChRemap[channel - 1]); 204 | resp = i2cMem8Write(dev, RELAY8_OUTPORT_REG_ADD, buff, 1); 205 | break; 206 | case ON: 207 | buff[0] |= 1 << relayChRemap[channel - 1]; 208 | resp = i2cMem8Write(dev, RELAY8_OUTPORT_REG_ADD, buff, 1); 209 | break; 210 | default: 211 | printf("Invalid relay state!\n"); 212 | return ERROR; 213 | break; 214 | } 215 | return resp; 216 | } 217 | 218 | int relayChGet(int dev, u8 channel, OutStateEnumType* state) 219 | { 220 | u8 buff[2]; 221 | 222 | if (NULL == state) 223 | { 224 | return ERROR; 225 | } 226 | 227 | if ( (channel < CHANNEL_NR_MIN) || (channel > RELAY_CH_NR_MAX)) 228 | { 229 | printf("Invalid relay nr!\n"); 230 | return ERROR; 231 | } 232 | 233 | if (FAIL == i2cMem8Read(dev, RELAY8_INPORT_REG_ADD, buff, 1)) 234 | { 235 | return ERROR; 236 | } 237 | 238 | if (buff[0] & (1 << relayChRemap[channel - 1])) 239 | { 240 | *state = ON; 241 | } 242 | else 243 | { 244 | *state = OFF; 245 | } 246 | return OK; 247 | } 248 | 249 | int relaySet(int dev, int val) 250 | { 251 | u8 buff[2]; 252 | 253 | buff[0] = relayToIO(0xff & val); 254 | 255 | return i2cMem8Write(dev, RELAY8_OUTPORT_REG_ADD, buff, 1); 256 | } 257 | 258 | int relayGet(int dev, int* val) 259 | { 260 | u8 buff[2]; 261 | 262 | if (NULL == val) 263 | { 264 | return ERROR; 265 | } 266 | if (FAIL == i2cMem8Read(dev, RELAY8_INPORT_REG_ADD, buff, 1)) 267 | { 268 | return ERROR; 269 | } 270 | *val = IOToRelay(buff[0]); 271 | return OK; 272 | } 273 | 274 | int doBoardInit(int stack) 275 | { 276 | int dev = 0; 277 | int add = 0; 278 | uint8_t buff[8]; 279 | 280 | if ( (stack < 0) || (stack > 7)) 281 | { 282 | printf("Invalid stack level [0..7]!"); 283 | return ERROR; 284 | } 285 | add = (stack + RELAY8_HW_I2C_BASE_ADD) ^ 0x07; 286 | dev = i2cSetup(add); 287 | if (dev == -1) 288 | { 289 | return ERROR; 290 | 291 | } 292 | if (ERROR == i2cMem8Read(dev, RELAY8_CFG_REG_ADD, buff, 1)) 293 | { 294 | add = (stack + RELAY8_HW_I2C_ALTERNATE_BASE_ADD) ^ 0x07; 295 | dev = i2cSetup(add); 296 | if (dev == -1) 297 | { 298 | return ERROR; 299 | } 300 | if (ERROR == i2cMem8Read(dev, RELAY8_CFG_REG_ADD, buff, 1)) 301 | { 302 | printf("8relind board id %d not detected\n", stack); 303 | return ERROR; 304 | } 305 | } 306 | if (buff[0] != 0) //non initialized I/O Expander 307 | { 308 | // put all pins in 0-logic state 309 | buff[0] = 0; 310 | if (0 > i2cMem8Write(dev, RELAY8_OUTPORT_REG_ADD, buff, 1)) 311 | { 312 | return ERROR; 313 | } 314 | // make all I/O pins output 315 | buff[0] = 0; 316 | if (0 > i2cMem8Write(dev, RELAY8_CFG_REG_ADD, buff, 1)) 317 | { 318 | return ERROR; 319 | } 320 | 321 | } 322 | 323 | return dev; 324 | } 325 | 326 | int boardCheck(int hwAdd) 327 | { 328 | int dev = 0; 329 | uint8_t buff[8]; 330 | 331 | hwAdd ^= 0x07; 332 | dev = i2cSetup(hwAdd); 333 | if (dev == -1) 334 | { 335 | return FAIL; 336 | } 337 | if (ERROR == i2cMem8Read(dev, RELAY8_CFG_REG_ADD, buff, 1)) 338 | { 339 | return ERROR; 340 | } 341 | return OK; 342 | } 343 | 344 | /* 345 | * doRelayWrite: 346 | * Write coresponding relay channel 347 | ************************************************************************************** 348 | */ 349 | static void doRelayWrite(int argc, char *argv[]) 350 | { 351 | int pin = 0; 352 | OutStateEnumType state = STATE_COUNT; 353 | int val = 0; 354 | int dev = 0; 355 | OutStateEnumType stateR = STATE_COUNT; 356 | int valR = 0; 357 | int retry = 0; 358 | 359 | if ( (argc != 5) && (argc != 4)) 360 | { 361 | printf("Usage: 8relind write \n"); 362 | printf("Usage: 8relind write \n"); 363 | exit(1); 364 | } 365 | 366 | dev = doBoardInit(atoi(argv[1])); 367 | if (dev <= 0) 368 | { 369 | exit(1); 370 | } 371 | if (argc == 5) 372 | { 373 | pin = atoi(argv[3]); 374 | if ( (pin < CHANNEL_NR_MIN) || (pin > RELAY_CH_NR_MAX)) 375 | { 376 | printf("Relay number value out of range\n"); 377 | exit(1); 378 | } 379 | 380 | /**/if ( (strcasecmp(argv[4], "up") == 0) 381 | || (strcasecmp(argv[4], "on") == 0)) 382 | state = ON; 383 | else if ( (strcasecmp(argv[4], "down") == 0) 384 | || (strcasecmp(argv[4], "off") == 0)) 385 | state = OFF; 386 | else 387 | { 388 | if ( (atoi(argv[4]) >= STATE_COUNT) || (atoi(argv[4]) < 0)) 389 | { 390 | printf("Invalid relay state!\n"); 391 | exit(1); 392 | } 393 | state = (OutStateEnumType)atoi(argv[4]); 394 | } 395 | 396 | retry = RETRY_TIMES; 397 | 398 | while ( (retry > 0) && (stateR != state)) 399 | { 400 | if (OK != relayChSet(dev, pin, state)) 401 | { 402 | printf("Fail to write relay\n"); 403 | exit(1); 404 | } 405 | if (OK != relayChGet(dev, pin, &stateR)) 406 | { 407 | printf("Fail to read relay\n"); 408 | exit(1); 409 | } 410 | retry--; 411 | } 412 | #ifdef DEBUG_I 413 | if(retry < RETRY_TIMES) 414 | { 415 | printf("retry %d times\n", 3-retry); 416 | } 417 | #endif 418 | if (retry == 0) 419 | { 420 | printf("Fail to write relay\n"); 421 | exit(1); 422 | } 423 | } 424 | else 425 | { 426 | val = atoi(argv[3]); 427 | if (val < 0 || val > 255) 428 | { 429 | printf("Invalid relay value\n"); 430 | exit(1); 431 | } 432 | 433 | retry = RETRY_TIMES; 434 | valR = -1; 435 | while ( (retry > 0) && (valR != val)) 436 | { 437 | 438 | if (OK != relaySet(dev, val)) 439 | { 440 | printf("Fail to write relay!\n"); 441 | exit(1); 442 | } 443 | if (OK != relayGet(dev, &valR)) 444 | { 445 | printf("Fail to read relay!\n"); 446 | exit(1); 447 | } 448 | } 449 | if (retry == 0) 450 | { 451 | printf("Fail to write relay!\n"); 452 | exit(1); 453 | } 454 | } 455 | } 456 | 457 | /* 458 | * doRelayRead: 459 | * Read relay state 460 | ****************************************************************************************** 461 | */ 462 | static void doRelayRead(int argc, char *argv[]) 463 | { 464 | int pin = 0; 465 | int val = 0; 466 | int dev = 0; 467 | OutStateEnumType state = STATE_COUNT; 468 | 469 | dev = doBoardInit(atoi(argv[1])); 470 | if (dev <= 0) 471 | { 472 | exit(1); 473 | } 474 | 475 | if (argc == 4) 476 | { 477 | pin = atoi(argv[3]); 478 | if ( (pin < CHANNEL_NR_MIN) || (pin > RELAY_CH_NR_MAX)) 479 | { 480 | printf("Relay number value out of range!\n"); 481 | exit(1); 482 | } 483 | 484 | if (OK != relayChGet(dev, pin, &state)) 485 | { 486 | printf("Fail to read!\n"); 487 | exit(1); 488 | } 489 | if (state != 0) 490 | { 491 | printf("1\n"); 492 | } 493 | else 494 | { 495 | printf("0\n"); 496 | } 497 | } 498 | else if (argc == 3) 499 | { 500 | if (OK != relayGet(dev, &val)) 501 | { 502 | printf("Fail to read!\n"); 503 | exit(1); 504 | } 505 | printf("%d\n", val); 506 | } 507 | else 508 | { 509 | printf("Usage: %s read relay value\n", argv[0]); 510 | exit(1); 511 | } 512 | } 513 | 514 | static void doHelp(int argc, char *argv[]) 515 | { 516 | int i = 0; 517 | if (argc == 3) 518 | { 519 | for (i = 0; i < CMD_ARRAY_SIZE; i++) 520 | { 521 | if ( (gCmdArray[i].name != NULL)) 522 | { 523 | if (strcasecmp(argv[2], gCmdArray[i].name) == 0) 524 | { 525 | printf("%s%s%s%s", gCmdArray[i].help, gCmdArray[i].usage1, 526 | gCmdArray[i].usage2, gCmdArray[i].example); 527 | break; 528 | } 529 | } 530 | } 531 | if (CMD_ARRAY_SIZE == i) 532 | { 533 | printf("Option \"%s\" not found\n", argv[2]); 534 | printf("%s: %s\n", argv[0], usage); 535 | } 536 | } 537 | else 538 | { 539 | printf("%s: %s\n", argv[0], usage); 540 | } 541 | } 542 | 543 | static void doVersion(int argc, char *argv[]) 544 | { 545 | UNUSED(argc); 546 | UNUSED(argv); 547 | printf("8relind v%d.%d.%d Copyright (c) 2016 - 2020 Sequent Microsystems\n", 548 | VERSION_BASE, VERSION_MAJOR, VERSION_MINOR); 549 | printf("\nThis is free software with ABSOLUTELY NO WARRANTY.\n"); 550 | printf("For details type: 8relind -warranty\n"); 551 | 552 | } 553 | 554 | static void doList(int argc, char *argv[]) 555 | { 556 | int ids[8]; 557 | int i; 558 | int cnt = 0; 559 | 560 | UNUSED(argc); 561 | UNUSED(argv); 562 | 563 | for (i = 0; i < 8; i++) 564 | { 565 | if (boardCheck(RELAY8_HW_I2C_BASE_ADD + i) == OK) 566 | { 567 | ids[cnt] = i; 568 | cnt++; 569 | } 570 | else 571 | { 572 | if (boardCheck(RELAY8_HW_I2C_ALTERNATE_BASE_ADD + i) == OK) 573 | { 574 | ids[cnt] = i; 575 | cnt++; 576 | } 577 | } 578 | } 579 | printf("%d board(s) detected\n", cnt); 580 | if (cnt > 0) 581 | { 582 | printf("Id:"); 583 | } 584 | while (cnt > 0) 585 | { 586 | cnt--; 587 | printf(" %d", ids[cnt]); 588 | } 589 | printf("\n"); 590 | } 591 | 592 | /* 593 | * Self test for production 594 | */ 595 | static void doTest(int argc, char* argv[]) 596 | { 597 | int dev = 0; 598 | int i = 0; 599 | int retry = 0; 600 | int relVal; 601 | int valR; 602 | int relayResult = 0; 603 | FILE* file = NULL; 604 | const u8 relayOrder[8] = 605 | { 606 | 1, 607 | 2, 608 | 3, 609 | 4, 610 | 5, 611 | 6, 612 | 7, 613 | 8}; 614 | 615 | dev = doBoardInit(atoi(argv[1])); 616 | if (dev <= 0) 617 | { 618 | exit(1); 619 | } 620 | if (argc == 4) 621 | { 622 | file = fopen(argv[3], "w"); 623 | if (!file) 624 | { 625 | printf("Fail to open result file\n"); 626 | //return -1; 627 | } 628 | } 629 | //relay test**************************** 630 | if (strcasecmp(argv[2], "test") == 0) 631 | { 632 | relVal = 0; 633 | printf( 634 | "Are all relays and LEDs turning on and off in sequence?\nPress y for Yes or any key for No...."); 635 | startThread(); 636 | while (relayResult == 0) 637 | { 638 | for (i = 0; i < 8; i++) 639 | { 640 | relayResult = checkThreadResult(); 641 | if (relayResult != 0) 642 | { 643 | break; 644 | } 645 | valR = 0; 646 | relVal = (u8)1 << (relayOrder[i] - 1); 647 | 648 | retry = RETRY_TIMES; 649 | while ( (retry > 0) && ( (valR & relVal) == 0)) 650 | { 651 | if (OK != relayChSet(dev, relayOrder[i], ON)) 652 | { 653 | retry = 0; 654 | break; 655 | } 656 | 657 | if (OK != relayGet(dev, &valR)) 658 | { 659 | retry = 0; 660 | } 661 | } 662 | if (retry == 0) 663 | { 664 | printf("Fail to write relay\n"); 665 | if (file) 666 | fclose(file); 667 | exit(1); 668 | } 669 | busyWait(150); 670 | } 671 | 672 | for (i = 0; i < 8; i++) 673 | { 674 | relayResult = checkThreadResult(); 675 | if (relayResult != 0) 676 | { 677 | break; 678 | } 679 | valR = 0xff; 680 | relVal = (u8)1 << (relayOrder[i] - 1); 681 | retry = RETRY_TIMES; 682 | while ( (retry > 0) && ( (valR & relVal) != 0)) 683 | { 684 | if (OK != relayChSet(dev, relayOrder[i], OFF)) 685 | { 686 | retry = 0; 687 | } 688 | if (OK != relayGet(dev, &valR)) 689 | { 690 | retry = 0; 691 | } 692 | } 693 | if (retry == 0) 694 | { 695 | printf("Fail to write relay!\n"); 696 | if (file) 697 | fclose(file); 698 | exit(1); 699 | } 700 | busyWait(150); 701 | } 702 | } 703 | } 704 | if (relayResult == YES) 705 | { 706 | if (file) 707 | { 708 | fprintf(file, "Relay Test ............................ PASS\n"); 709 | } 710 | else 711 | { 712 | printf("Relay Test ............................ PASS\n"); 713 | } 714 | } 715 | else 716 | { 717 | if (file) 718 | { 719 | fprintf(file, "Relay Test ............................ FAIL!\n"); 720 | } 721 | else 722 | { 723 | printf("Relay Test ............................ FAIL!\n"); 724 | } 725 | } 726 | if (file) 727 | { 728 | fclose(file); 729 | } 730 | relaySet(dev, 0); 731 | } 732 | 733 | static void doWarranty(int argc UNU, char* argv[] UNU) 734 | { 735 | printf("%s\n", warranty); 736 | } 737 | 738 | static void cliInit(void) 739 | { 740 | int i = 0; 741 | 742 | memset(gCmdArray, 0, sizeof(CliCmdType) * CMD_ARRAY_SIZE); 743 | 744 | memcpy(&gCmdArray[i], &CMD_HELP, sizeof(CliCmdType)); 745 | i++; 746 | memcpy(&gCmdArray[i], &CMD_WAR, sizeof(CliCmdType)); 747 | i++; 748 | memcpy(&gCmdArray[i], &CMD_LIST, sizeof(CliCmdType)); 749 | i++; 750 | memcpy(&gCmdArray[i], &CMD_WRITE, sizeof(CliCmdType)); 751 | i++; 752 | memcpy(&gCmdArray[i], &CMD_READ, sizeof(CliCmdType)); 753 | i++; 754 | memcpy(&gCmdArray[i], &CMD_TEST, sizeof(CliCmdType)); 755 | i++; 756 | memcpy(&gCmdArray[i], &CMD_VERSION, sizeof(CliCmdType)); 757 | 758 | } 759 | 760 | int main(int argc, char *argv[]) 761 | { 762 | int i = 0; 763 | 764 | cliInit(); 765 | 766 | if (argc == 1) 767 | { 768 | printf("%s\n", usage); 769 | return 1; 770 | } 771 | for (i = 0; i < CMD_ARRAY_SIZE; i++) 772 | { 773 | if ( (gCmdArray[i].name != NULL) && (gCmdArray[i].namePos < argc)) 774 | { 775 | if (strcasecmp(argv[gCmdArray[i].namePos], gCmdArray[i].name) == 0) 776 | { 777 | gCmdArray[i].pFunc(argc, argv); 778 | return 0; 779 | } 780 | } 781 | } 782 | printf("Invalid command option\n"); 783 | printf("%s\n", usage); 784 | 785 | return 0; 786 | } 787 | --------------------------------------------------------------------------------