├── .gitignore ├── LICENSE ├── MODBUS.md ├── Makefile ├── README.md ├── node-red-contrib-sm-ind ├── README.md ├── icons │ ├── adc.png │ ├── dac.png │ ├── mosfet.png │ └── optocoupler.png ├── ind.html ├── ind.js └── package.json ├── python ├── LICENSE ├── Makefile ├── README.md ├── megaind │ └── __init__.py ├── setup.cfg ├── setup.py └── tests │ ├── flash_leds.py │ ├── test_amp.py │ ├── test_digital.py │ └── test_volt.py ├── res ├── IND.jpg ├── industrial.jpg └── sequent.jpg ├── rtc ├── README.md └── set_time.sh ├── src ├── analog.c ├── cli.h ├── cli_def.h ├── comm.c ├── comm.h ├── dout.c ├── megaind.c ├── megaind.h ├── opto.c ├── rs485.c ├── rtc.c ├── thread.c ├── thread.h └── wdt.c └── update ├── README.md ├── ubuntu └── update └── update /.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 | megaind 55 | 56 | /python/build/ 57 | /python/dist/ 58 | /python/*.egg-info/ 59 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /MODBUS.md: -------------------------------------------------------------------------------- 1 | [![megaind-rpi](res/sequent.jpg)](https://sequentmicrosystems.com) 2 | 3 | # Modbus 4 | 5 | The [Industrial](https://sequentmicrosystems.com/product/raspberry-pi-industrial-automation/) Automation I/O Expansion cards for Raspberry Pi can be accessed thru Modbus RTU protocol over RS-485 port. 6 | You can set-up the RS-485 port with **megaind** command. 7 | 8 | Example: 9 | ```bash 10 | ~$ megaind 0 rs485wr 1 9600 1 0 1 11 | ``` 12 | Set Modbus RTU , Baudrate: 9600bps, 1 Stop Bit, parity: None, slave address offset: 1 13 | ```bash 14 | ~$ megaind -h rs485wr 15 | ``` 16 | display the full set of options 17 | 18 | ## Slave Address 19 | The slave address is add with the "stack level" jumpers. For example the jumpers configuration for stack level 1 (one jumper in position ID0) slave address offset to 1 corespond to slave address 2. 20 | 21 | ## Modbus object types 22 | All modbus RTU object type with standard addresses are implemented : Coils, Discrete Inputs, Input registers, Holding registers. 23 | 24 | ### Coils 25 | 26 | Acces level Read/Write, Size 1 bit 27 | 28 | | Device function | Register Address | Modbus Address | 29 | | --- | --- | --- | 30 | | OPEN_DRAIN_1 | 0001 | 0x00 | 31 | | OPEN_DRAIN_2 | 0002 | 0x01 | 32 | | OPEN_DRAIN_3 | 0003 | 0x02 | 33 | | OPEN_DRAIN_4 | 0004 | 0x03 | 34 | | LED_1 | 0005 | 0x04 | 35 | | LED_2 | 0006 | 0x05 | 36 | | LED_3 | 0007 | 0x06 | 37 | | LED_4 | 0008 | 0x07 | 38 | | OPTO_RISING_COUNT_1 | 0009 | 0x08 | 39 | | OPTO_RISING_COUNT_2 | 0010 | 0x09 | 40 | | OPTO_RISING_COUNT_3 | 0011 | 0x0a | 41 | | OPTO_RISING_COUNT_4 | 0012 | 0x0b | 42 | | OPTO_FALLING_COUNT_1 | 0013 | 0x0c | 43 | | OPTO_FALLING_COUNT_2 | 0014 | 0x0d | 44 | | OPTO_FALLING_COUNT_3 | 0015 | 0x0e | 45 | | OPTO_FALLING_COUNT_4 | 0016 | 0x0f | 46 | 47 | OPTO_RISING_COUNT coil enable/disable counting on rising edge of coresponding opto channel 48 | OPTO_FALLING_COUNT coil enable/disable counting on falling edge of coresponding opto channel 49 | 50 | ### Discrete Inputs 51 | 52 | Access level Read Only, Size 1 bit 53 | 54 | | Device function | Register Address | Modbus Address | 55 | | --- | --- | --- | 56 | | OPTO_1 | 10001 | 0x00 | 57 | | OPTO_2 | 10002 | 0x01 | 58 | | OPTO_3 | 10003 | 0x02 | 59 | | OPTO_4 | 10004 | 0x03 | 60 | | OWB_PRESENT | 10005 | 0x04 | 61 | 62 | 63 | ### Input registers 64 | 65 | Access level Read Only, Size 16 bits 66 | 67 | | Device function | Register Address | Description | Measurement Unit | 68 | | --- | --- | --- | --- | 69 | | 0-10V_IN_1 | 30001 | 0-10V Input 1 | mV | 70 | | 0-10V_IN_2 | 30002 | 0-10V Input 2 | mV | 71 | | 0-10V_IN_3 | 30003 |0-10V Input 3 | mV | 72 | | 0-10V_IN_4 | 30004 | 0-10V Input 4 | mV | 73 | | +/-10V_IN_1 | 30005 | +/-10V Input 1 | mV | 74 | | +/-10V_IN_2 | 30006 | +/-10V Input 2 | mV | 75 | | +/-10V_IN_3 | 30007 | +/-10V Input 3 | mV | 76 | | +/-10V_IN_4 | 30008 | +/-10V Input 4 | mV | 77 | | 4-20mA_IN_1 | 30009 | 4-20mA Input 1 | uA | 78 | | 4-20mA_IN_2 | 30010 | 4-20mA Input 2 | uA | 79 | | 4-20mA_IN_3 | 30011 | 4-20mA Input 3 | uA | 80 | | 4-20mA_IN_4 | 30012 | 4-20mA Input 4 | uA | 81 | | OPTO_COUNT_1 | 30013 | Opto Count 1 | | 82 | | OPTO_COUNT_2 | 30014 | Opto Count 2 | | 83 | | OPTO_COUNT_3 | 30015 | Opto Count 3 | | 84 | | OPTO_COUNT_4 | 30016 | Opto Count 4 | | 85 | | OWB_TEMP1| 30017 | Temperature 1 | 0.01C| 86 | | OWB_TEMP2| 30018 | Temperature 2 | 0.01C| 87 | | OWB_TEMP3| 30019 | Temperature 3 | 0.01C| 88 | | OWB_TEMP4| 30020 | Temperature 4 | 0.01C| 89 | | OWB_TEMP5| 30021 | Temperature 5 | 0.01C| 90 | | OWB_TEMP6| 30022 | Temperature 6 | 0.01C| 91 | | OWB_TEMP7| 30023 | Temperature 7 | 0.01C| 92 | | OWB_TEMP8| 30024 | Temperature 8 | 0.01C| 93 | | OWB_TEMP9| 30025 | Temperature 9 | 0.01C| 94 | | OWB_TEMP10| 30026 | Temperature 10 | 0.01C| 95 | | OWB_TEMP11| 30027 | Temperature 11 | 0.01C| 96 | | OWB_TEMP12| 30028 | Temperature 12 | 0.01C| 97 | | OWB_TEMP13| 30028 | Temperature 13 | 0.01C| 98 | | OWB_TEMP14| 30030 | Temperature 14 | 0.01C| 99 | | OWB_TEMP15| 30031 | Temperature 15 | 0.01C| 100 | | OWB_TEMP16| 30032 | Temperature 16 | 0.01C| 101 | | OWB_ID_A_TEMP1| 30033 | ID A Temperature 1 | 64 bit ID | 102 | | OWB_ID_B_TEMP1| 30034 | ID B Temperature 1 | | 103 | | OWB_ID_C_TEMP1| 30035 | ID C Temperature 1 | | 104 | | OWB_ID_D_TEMP1| 30036 | ID D Temperature 1 | | 105 | | OWB_ID_A_TEMP2| 30037 | ID A Temperature 2 | 64 bit ID | 106 | | OWB_ID_B_TEMP2| 30038 | ID B Temperature 2 | | 107 | | OWB_ID_C_TEMP2| 30039 | ID C Temperature 2 | | 108 | | OWB_ID_D_TEMP2| 30040 | ID D Temperature 2 | | 109 | | OWB_ID_A_TEMP3| 30041 | ID A Temperature 3 | 64 bit ID| 110 | | OWB_ID_B_TEMP3| 30042 | ID B Temperature 3 | | 111 | | OWB_ID_C_TEMP3| 30043 | ID C Temperature 3 | | 112 | | OWB_ID_D_TEMP3| 30044 | ID D Temperature 3 | | 113 | | OWB_ID_A_TEMP4| 30045 | ID A Temperature 4 | 64 bit ID| 114 | | OWB_ID_B_TEMP4| 30046 | ID B Temperature 4 | | 115 | | OWB_ID_C_TEMP4| 30047 | ID C Temperature 4 | | 116 | | OWB_ID_D_TEMP4| 30048 | ID D Temperature 4 | | 117 | | OWB_ID_A_TEMP5| 30049 | ID A Temperature 5 | 64 bit ID| 118 | | OWB_ID_B_TEMP5| 30050 | ID B Temperature 5 | | 119 | | OWB_ID_C_TEMP5| 30051 | ID C Temperature 5 | | 120 | | OWB_ID_D_TEMP5| 30052 | ID D Temperature 5 | | 121 | | OWB_ID_A_TEMP6| 30053 | ID A Temperature 6 | 64 bit ID| 122 | | OWB_ID_B_TEMP6| 30054 | ID B Temperature 6 | | 123 | | OWB_ID_C_TEMP6| 30055 | ID C Temperature 6 | | 124 | | OWB_ID_D_TEMP6| 30056 | ID D Temperature 6 | | 125 | | OWB_ID_A_TEMP7| 30057 | ID A Temperature 7 | 64 bit ID| 126 | | OWB_ID_B_TEMP7| 30058 | ID B Temperature 7 | | 127 | | OWB_ID_C_TEMP7| 30059 | ID C Temperature 7 | | 128 | | OWB_ID_D_TEMP7| 30060 | ID D Temperature 7 | | 129 | | OWB_ID_A_TEMP8| 30061 | ID A Temperature 8 | 64 bit ID| 130 | | OWB_ID_B_TEMP8| 30062 | ID B Temperature 8 | | 131 | | OWB_ID_C_TEMP8| 30063 | ID C Temperature 8 | | 132 | | OWB_ID_D_TEMP8| 30064 | ID D Temperature 8 | | 133 | | OWB_ID_A_TEMP9| 30065 | ID A Temperature 9 | 64 bit ID| 134 | | OWB_ID_B_TEMP9| 30066 | ID B Temperature 9 | | 135 | | OWB_ID_C_TEMP9| 30067 | ID C Temperature 9 | | 136 | | OWB_ID_D_TEMP9| 30068 | ID D Temperature 9 | | 137 | | OWB_ID_A_TEMP10| 30069 | ID A Temperature 10 | 64 bit ID| 138 | | OWB_ID_B_TEMP10| 30070 | ID B Temperature 10 | | 139 | | OWB_ID_C_TEMP10| 30071 | ID C Temperature 10 | | 140 | | OWB_ID_D_TEMP10| 30072 | ID D Temperature 10 | | 141 | | OWB_ID_A_TEMP11| 30073 | ID A Temperature 11 | 64 bit ID| 142 | | OWB_ID_B_TEMP11| 30074 | ID B Temperature 11 | | 143 | | OWB_ID_C_TEMP11| 30075 | ID C Temperature 11 | | 144 | | OWB_ID_D_TEMP11| 30076 | ID D Temperature 11 | | 145 | | OWB_ID_A_TEMP12| 30077 | ID A Temperature 12 | 64 bit ID| 146 | | OWB_ID_B_TEMP12| 30078 | ID B Temperature 12 | | 147 | | OWB_ID_C_TEMP12| 30079 | ID C Temperature 12 | | 148 | | OWB_ID_D_TEMP12| 30080 | ID D Temperature 12 | | 149 | | OWB_ID_A_TEMP13| 30081 | ID A Temperature 13 | 64 bit ID| 150 | | OWB_ID_B_TEMP13| 30082 | ID B Temperature 13 | | 151 | | OWB_ID_C_TEMP13| 30083 | ID C Temperature 13 | | 152 | | OWB_ID_D_TEMP13| 30084 | ID D Temperature 13 | | 153 | | OWB_ID_A_TEMP14| 30085 | ID A Temperature 14 | 64 bit ID| 154 | | OWB_ID_B_TEMP14| 30086 | ID B Temperature 14 | | 155 | | OWB_ID_C_TEMP14| 30087 | ID C Temperature 14 | | 156 | | OWB_ID_D_TEMP14| 30088 | ID D Temperature 14 | | 157 | | OWB_ID_A_TEMP15| 30089 | ID A Temperature 15 | 64 bit ID| 158 | | OWB_ID_B_TEMP15| 30090 | ID B Temperature 15 | | 159 | | OWB_ID_C_TEMP15| 30091 | ID C Temperature 15 | | 160 | | OWB_ID_D_TEMP15| 30092 | ID D Temperature 15 | | 161 | | OWB_ID_A_TEMP16| 30093 | ID A Temperature 16 | 64 bit ID| 162 | | OWB_ID_B_TEMP16| 30094 | ID B Temperature 16 | | 163 | | OWB_ID_C_TEMP16| 30095 | ID C Temperature 16 | | 164 | | OWB_ID_D_TEMP16| 30096 | ID D Temperature 16 | | 165 | 166 | 167 | ### Holding registers 168 | 169 | Access level Read/Write, Size 16 bits 170 | 171 | | Device function | Register Address | Modbus Address | Measurement Unit | Range | 172 | | --- | --- | --- | --- | --- | 173 | | 0-10V_OUT_1 | 40001 | 0x00 | mV | 0..10000 | 174 | | 0-10V_OUT_2 | 40002 | 0x01 | mV | 0..10000 | 175 | | 0-10V_OUT_3 | 40003 | 0x02 | mV | 0..10000 | 176 | | 0-10V_OUT_4 | 40004 | 0x03 | mV | 0..10000 | 177 | | 4-20mA_OUT_1 | 40005 | 0x04 | uA | 4000..20000 | 178 | | 4-20mA_OUT_2 | 40006 | 0x05 | uA | 4000..20000 | 179 | | 4-20mA_OUT_3 | 40007 | 0x06 | uA | 4000..20000 | 180 | | 4-20mA_OUT_4 | 40008 | 0x07 | uA | 4000..20000 | 181 | | OPEN_DRAIN_PWM_1 | 40009 | 0x08 | | 0..10000 | 182 | | OPEN_DRAIN_PWM_2 | 40010 | 0x09 | | 0..10000 | 183 | | OPEN_DRAIN_PWM_3 | 40011 | 0x0a | | 0..10000 | 184 | | OPEN_DRAIN_PWM_4 | 40012 | 0x0b | | 0..10000 | 185 | 186 | 187 | ## Function codes implemented 188 | 189 | * Read Coils (0x01) 190 | * Read Discrete Inputs (0x02) 191 | * Read Holding Registers (0x03) 192 | * Read Input Registers (0x04) 193 | * Write Single Coil (0x05) 194 | * Write Single Register (0x06) 195 | * Write Multiple Coils (0x0f) 196 | * Write Multiple registers (0x10) 197 | -------------------------------------------------------------------------------- /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/megaind.c src/comm.c src/thread.c src/opto.c src/wdt.c src/rtc.c src/rs485.c src/analog.c src/dout.c 15 | 16 | OBJ = $(SRC:.c=.o) 17 | 18 | all: megaind 19 | 20 | megaind: $(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) megaind *~ core tags *.bak 32 | 33 | .PHONY: install 34 | install: megaind 35 | $Q echo "[Install]" 36 | $Q cp megaind $(DESTDIR)$(PREFIX)/bin 37 | ifneq ($(WIRINGPI_SUID),0) 38 | $Q chown root:root $(DESTDIR)$(PREFIX)/bin/megaind 39 | $Q chmod 4755 $(DESTDIR)$(PREFIX)/bin/megaind 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/megaind 48 | $Q rm -f $(DESTDIR)$(PREFIX)/man/man1/megaind.1 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![megaind-rpi](res/sequent.jpg)](https://www.sequentmicrosystems.com) 2 | 3 | # megaind-rpi 4 | 5 | This is the command to control [Industrial Automation Stackable Card for Raspberry Pi](https://sequentmicrosystems.com/products/industrial-automation-for-raspberry-pi). 6 | 7 | ![MEGA-IND](res/IND.jpg) 8 | 9 | ## Setup 10 | 11 | Enable I2C communication first: 12 | ```bash 13 | ~$ sudo raspi-config 14 | ``` 15 | A good article about I2C on Raspberry can be found [here.](https://www.raspberrypi-spy.co.uk/2014/11/enabling-the-i2c-interface-on-the-raspberry-pi/) 16 | 17 | If you use Ubuntu you need to install ```raspi-config``` first: 18 | 19 | ```bash 20 | ~$ sudo apt update 21 | ~$ sudo apt install raspi-config 22 | ``` 23 | 24 | Make sure you have all tools you need: 25 | ```bash 26 | ~$ sudo apt update 27 | ~$ sudo apt-get install git 28 | ~$ sudo apt-get install build-essential 29 | ``` 30 | ## Usage 31 | 32 | Install the command: 33 | ```bash 34 | ~$ git clone https://github.com/SequentMicrosystems/megaind-rpi.git 35 | ~$ cd megaind-rpi/ 36 | ~/megaind-rpi$ sudo make install 37 | ``` 38 | 39 | Now you can access all the functions of the [Industrial Automation Stackable Card for Raspberry Pi](https://sequentmicrosystems.com/collections/all-io-cards/products/industrial-raspberry-pi) through the command "megaind". Use -h option for help: 40 | ```bash 41 | ~$ megaind -h 42 | ``` 43 | 44 | If you clone the repository any update can be made with the following commands: 45 | 46 | ```bash 47 | ~$ cd megaind-rpi/ 48 | ~/megaind-rpi$ git pull 49 | ~/megaind-rpi$ sudo make install 50 | ``` 51 | [Python Library](https://github.com/SequentMicrosystems/megaind-rpi/tree/master/python) 52 | 53 | [NodeRed nodes](https://github.com/SequentMicrosystems/megaind-rpi/tree/master/node-red-contrib-sm-ind) 54 | 55 | [firmware update instructions](https://github.com/SequentMicrosystems/megaind-rpi/tree/master/update). 56 | 57 | The board can act as Modbus RTU slave device, checkout [modbus instructions](https://github.com/SequentMicrosystems/megaind-rpi/blob/master/MODBUS.md). 58 | 59 | [RTC Usage](rtc/README.md) 60 | -------------------------------------------------------------------------------- /node-red-contrib-sm-ind/README.md: -------------------------------------------------------------------------------- 1 | # node-red-contrib-sm-ind 2 | 3 | This is the node-red node to control Sequent Microsystems [Industrial Automation](https://sequentmicrosystems.com/products/raspberry-pi-industrial-automation) card. 4 | 5 | ## Manual Install 6 | 7 | Clone or update the repository, follow the instrutions from the [first page.](https://github.com/SequentMicrosystems/megaind-rpi) 8 | 9 | In your node-red user directory, typicaly ~/.node-red, 10 | 11 | ```bash 12 | ~$ cd ~/.node-red 13 | ``` 14 | 15 | Run the following command: 16 | 17 | ```bash 18 | ~/.node-red$ npm install ~/megaind-rpi/node-red-contrib-sm-ind 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 13 new nodes: 30 | 31 | ### IND 0 10V in 32 | 33 | This node reads one 0-10V input channel (Make sure the corresponding jumper is removed in order for this read to be correct). 34 | The card stack level and channel number can be set in the node dialog box or dynamically through ```msg.stack``` and ```msg.channel```. 35 | The read is triggered by the message input and output can be found in the output message payload as a number representing the voltage readings in volts. 36 | 37 | ### IND 0 10V out 38 | 39 | This node controls one 0-10V output channel. 40 | The card stack level and channel number can be set in the node dialog box or dynamically through ```msg.stack``` and ```msg.channel```. 41 | The value in volts is set dynamically as a number between 0..10 thru ```msg.payload```. 42 | 43 | ### IND 4 20mA in 44 | 45 | This node reads one 4-20mA input channel. 46 | The card stack level and channel number can be set in the node dialog box or dynamically through ```msg.stack``` and ```msg.channel```. 47 | The read is triggered by the message input and output can be found in the output message payload as a number representing the current readings in milliamps. 48 | 49 | ### IND 4 20mA out 50 | 51 | This node controls one 4-20mA output channel. 52 | The card stack level and channel number can be set in the node dialog box or dynamically through ```msg.stack``` and ```msg.channel```. 53 | The value in milliamps is set dynamically as a number between 4..20 thru ```msg.payload```. 54 | 55 | ### IND +/-10V in 56 | 57 | This node reads one +/-10V input channel (Make sure the corresponding jumper is placed in order for this read to be correct). 58 | The card stack level and channel number can be set in the node dialog box or dynamically through ```msg.stack``` and ```msg.channel```. 59 | The read is triggered by the message input and output can be found in the output message payload as a number representing the voltage readings in volts. 60 | 61 | ### IND OPT cnt 62 | 63 | This node reads the optically coupled input counter for one particular channel and sets the counting edges for that channel. 64 | Card stack level and optically coupled input counter channel number can be set in the node dialog box or dynamically through ```msg.stack``` and ```msg.channel```. 65 | Rising edge counting and/or falling edge counting can be enabled/diabled with corresponding check box in the node dialog. 66 | Edge settings are sent to the card every time you deploy this node or the flow starts or you select a different channel for reading through ```msg.channel```. 67 | The read is triggered by the message input and output can be found in the output message payload. 68 | The counter can be reset by sending to the node a message with the attribute ```reset``` equal to 1, ```msg.reset = 1```. 69 | 70 | ### IND OPT in 71 | 72 | This node reads the optically coupled input state for one particular channel. 73 | Card stack level and optically coupled input channel number can be set in the node dialog box or dynamically through ```msg.stack``` and ```msg.channel```. 74 | The read is triggered by the message input and output can be found in the output message payload as boolean. 75 | 76 | ### IND OD out 77 | 78 | This node controls one Open-Drain output channel. 79 | Card stack level and channel number can be set in the node dialog box or dynamically through ```msg.stack``` and ```msg.channel```. 80 | The value in percentage is set dynamically as a number between 0..100 thru ```msg.payload```. 81 | 82 | ### IND CPU Temp 83 | This node reads the temperature from the CPU on the Sequent board. 84 | 85 | Card stack level can be set in the node dialog box or dynamically 86 | through ```msg.stack```. 87 | 88 | The integer value in units of degrees Celcius is set dynamically as a 89 | number via ```msg.payload```. 90 | 91 | *NB - not sure if the byte read from the Sequent board is signed or 92 | unsigned, so the range might be -128C..+127C, or it might be 93 | 0C..255C.* 94 | 95 | ### IND PS Voltage 96 | This node reads the power supply voltage (nominally 24V) from the CPU 97 | on the Sequent board. 98 | 99 | Card stack level can be set in the node dialog box or dynamically 100 | through ```msg.stack```. 101 | 102 | The floating point value in volts is set dynamically as a number via 103 | ```msg.payload```. 104 | 105 | ### IND RasPi Voltage 106 | This node reads the voltage supplied to the Raspberry Pi (nominally 107 | 5V) from the CPU on the Sequent board. 108 | 109 | Card stack level can be set in the node dialog box or dynamically 110 | through ```msg.stack```. 111 | 112 | The floating point value in volts is set dynamically as a number via 113 | ```msg.payload```. 114 | 115 | ### IND LED Out 116 | This node controls one LED channel. 117 | 118 | The card stack level and LED channel number can be set in the node 119 | dialog box or dynamically through ```msg.stack``` and 120 | ```msg.channel```. 121 | 122 | The value in volts is set dynamically as a number. 0 is off; anything 123 | else is on via ```msg.payload```. 124 | 125 | ### IND Read RTC 126 | This node reads the Real Time Clock (RTC) from the Sequent board. 127 | 128 | *NB Since there is no indication of timezone, Node interprets the time 129 | from the RTC to be in the system local time.* 130 | 131 | Card stack level can be set in the node dialog box or dynamically 132 | through ```msg.stack```. 133 | 134 | The date and time are set dynamically as a Date via ```msg.payload```. 135 | 136 | ### IND OWB Temp 137 | This node reads the temperature from 18B20 sensors connected on the one wire bus interface of the cards 138 | 139 | Card stack level and sensor channel number can be set in the node dialog box or dynamically through ```msg.stack``` and ```msg.channel```. 140 | The read is triggered by the message input and output can be found in the output message payload as boolean. 141 | 142 | ### IND OWB Scan 143 | This node start the connected sensors scaning process 144 | 145 | Card stack level can be set in the node dialog box or dynamically through ```msg.stack``` 146 | 147 | ## Important note 148 | 149 | This node is using the I2C-bus package from @fivdi, you can visit his work on github [here](https://github.com/fivdi/i2c-bus).  150 | The inspiration for this node came from @nielsnl68 work with [node-red-contrib-i2c](https://github.com/nielsnl68/node-red-contrib-i2c). 151 | Thank both for the great job. 152 | -------------------------------------------------------------------------------- /node-red-contrib-sm-ind/icons/adc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/megaind-rpi/652448333b5c9d04be1ffd88889b6e10751076aa/node-red-contrib-sm-ind/icons/adc.png -------------------------------------------------------------------------------- /node-red-contrib-sm-ind/icons/dac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/megaind-rpi/652448333b5c9d04be1ffd88889b6e10751076aa/node-red-contrib-sm-ind/icons/dac.png -------------------------------------------------------------------------------- /node-red-contrib-sm-ind/icons/mosfet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/megaind-rpi/652448333b5c9d04be1ffd88889b6e10751076aa/node-red-contrib-sm-ind/icons/mosfet.png -------------------------------------------------------------------------------- /node-red-contrib-sm-ind/icons/optocoupler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/megaind-rpi/652448333b5c9d04be1ffd88889b6e10751076aa/node-red-contrib-sm-ind/icons/optocoupler.png -------------------------------------------------------------------------------- /node-red-contrib-sm-ind/ind.js: -------------------------------------------------------------------------------- 1 | module.exports = function (RED) { 2 | "use strict"; 3 | var I2C = require("i2c-bus"); 4 | const DEFAULT_HW_ADD = 0x50; 5 | 6 | const I2C_MEM_DIAG_TEMPERATURE = 114; // Byte/Degrees C 7 | const I2C_MEM_DIAG_24V = 115; // Word/millivolts 8 | const I2C_MEM_DIAG_5V = 117; // Word/millivolts 9 | 10 | const I2C_MEM_OPTO_IN_VAL = 3; 11 | const I2C_MEM_U0_10_OUT_VAL1 = 4; 12 | const I2C_MEM_I4_20_OUT_VAL1 = 12; 13 | const I2C_MEM_OD_PWM1 = 20; 14 | 15 | const I2C_MEM_U0_10_IN_VAL1 = 28; 16 | const I2C_MEM_U_PM_10_IN_VAL1 = 36; 17 | const I2C_MEM_I4_20_IN_VAL1 = 44; 18 | const I2C_MEM_OPTO_RISING_ENABLE = 103; 19 | const I2C_MEM_OPTO_FALLING_ENABLE = 104; 20 | const I2C_MEM_OPTO_CH_CONT_RESET = 105; 21 | const I2C_MEM_OPTO_COUNT1 = 106; //2 bytes integers 22 | 23 | const I2C_MEM_RELAY_VAL = 0; // Byte/upper four bits mapped to LEDs 24 | const I2C_MEM_RELAY_SET = 1; // Byte/values 4-7 map to LEDs 0-3 25 | const I2C_MEM_RELAY_CLR = 2; // Byte/values 4-7 map to LEDs 0-3 26 | 27 | // Real Time Clock 28 | // 29 | // Apparantly no provision for specifying Timezone, whether local 30 | // or UTC, or DST status 31 | const I2C_MEM_RTC_YEAR = 70; // Byte/(Year - 2000) 32 | const I2C_MEM_RTC_MONTH = 71; // Byte/1-12 (note: Node uses 0-11) 33 | const I2C_MEM_RTC_DAY = 72; // Byte/1-31 34 | const I2C_MEM_RTC_HOUR = 73; // Byte/0-23 35 | const I2C_MEM_RTC_MINUTE = 74; // Byte/0-59 36 | const I2C_MEM_RTC_SECOND = 75; // Byte/0-59 37 | const I2C_MEM_RTC_SET_YEAR = 76; // Byte/(Year - 2000) 38 | const I2C_MEM_RTC_SET_MONTH = 77; // Byte/1-12 39 | const I2C_MEM_RTC_SET_DAY = 78; // Byte/1-31 40 | const I2C_MEM_RTC_SET_HOUR = 79; // Byte/0-23 41 | const I2C_MEM_RTC_SET_MINUTE = 80; // Byte/0-59 42 | const I2C_MEM_RTC_SET_SECOND = 81; // Byte/0-59 43 | const I2C_MEM_RTC_CMD = 82; // Defined in megaind.h, but 44 | // not used in C or Python code 45 | const I2C_MEM_1WB_DEV = 147; 46 | const I2C_MEM_1WB_T1 = 174; 47 | const I2C_MEM_1WB_ROM_CODE_IDX = 150; 48 | const I2C_MEM_1WB_ROM_CODE = 151;//rom code 64 bits 49 | const I2C_MEM_1WB_START_SEARCH = 173; 50 | 51 | function VInNode(n) { 52 | RED.nodes.createNode(this, n); 53 | this.stack = parseInt(n.stack); 54 | this.channel = parseInt(n.channel); 55 | this.payload = n.payload; 56 | this.payloadType = n.payloadType; 57 | var node = this; 58 | var buffer = Buffer.alloc(2); 59 | 60 | node.port = I2C.openSync(1); 61 | node.on("input", function (msg) { 62 | var myPayload; 63 | var stack = node.stack; 64 | if (isNaN(stack)) stack = msg.stack; 65 | var channel = node.channel; 66 | if (isNaN(channel)) channel = msg.channel; 67 | stack = parseInt(stack); 68 | channel = parseInt(channel); 69 | //var buffcount = parseInt(node.count); 70 | if (isNaN(stack)) { 71 | this.status({ fill: "red", shape: "ring", text: "Stack level (" + stack + ") value is missing or incorrect" }); 72 | return; 73 | } else if (isNaN(channel)) { 74 | this.status({ fill: "red", shape: "ring", text: "Sensor number (" + channel + ") value is missing or incorrect" }); 75 | return; 76 | } else { 77 | this.status({}); 78 | } 79 | try { 80 | var hwAdd = DEFAULT_HW_ADD; 81 | if (stack < 0) { 82 | stack = 0; 83 | } 84 | if (stack > 7) { 85 | stack = 7; 86 | } 87 | hwAdd += stack; 88 | 89 | if (channel < 1) { 90 | channel = 1; 91 | } 92 | if (channel > 4) { 93 | channel = 4; 94 | } 95 | 96 | if (this.payloadType == null) { 97 | myPayload = this.payload; 98 | } else if (this.payloadType == 'none') { 99 | myPayload = null; 100 | } else { 101 | myPayload = RED.util.evaluateNodeProperty(this.payload, this.payloadType, this, msg); 102 | } 103 | node.port.readI2cBlock(hwAdd, I2C_MEM_U0_10_IN_VAL1 + (channel - 1) * 2, 2, buffer, function (err, size, res) { 104 | if (err) { 105 | node.error(err, msg); 106 | } 107 | else { 108 | msg.payload = res.readIntLE(0, 2) / 1000.0; 109 | node.send(msg); 110 | } 111 | }); 112 | 113 | } catch (err) { 114 | this.error(err, msg); 115 | } 116 | 117 | }); 118 | 119 | node.on("close", function () { 120 | node.port.closeSync(); 121 | }); 122 | } 123 | RED.nodes.registerType("IND 0-10V in", VInNode); 124 | 125 | function DVInNode(n) { 126 | RED.nodes.createNode(this, n); 127 | this.stack = parseInt(n.stack); 128 | this.channel = parseInt(n.channel); 129 | this.payload = n.payload; 130 | this.payloadType = n.payloadType; 131 | var node = this; 132 | var buffer = Buffer.alloc(2); 133 | 134 | node.port = I2C.openSync(1); 135 | node.on("input", function (msg) { 136 | var myPayload; 137 | var stack = node.stack; 138 | if (isNaN(stack)) stack = msg.stack; 139 | var channel = node.channel; 140 | if (isNaN(channel)) channel = msg.channel; 141 | stack = parseInt(stack); 142 | channel = parseInt(channel); 143 | //var buffcount = parseInt(node.count); 144 | if (isNaN(stack)) { 145 | this.status({ fill: "red", shape: "ring", text: "Stack level (" + stack + ") value is missing or incorrect" }); 146 | return; 147 | } else if (isNaN(channel)) { 148 | this.status({ fill: "red", shape: "ring", text: "Sensor number (" + channel + ") value is missing or incorrect" }); 149 | return; 150 | } else { 151 | this.status({}); 152 | } 153 | try { 154 | var hwAdd = DEFAULT_HW_ADD; 155 | if (stack < 0) { 156 | stack = 0; 157 | } 158 | if (stack > 7) { 159 | stack = 7; 160 | } 161 | hwAdd += stack; 162 | 163 | if (channel < 1) { 164 | channel = 1; 165 | } 166 | if (channel > 4) { 167 | channel = 4; 168 | } 169 | 170 | if (this.payloadType == null) { 171 | myPayload = this.payload; 172 | } else if (this.payloadType == 'none') { 173 | myPayload = null; 174 | } else { 175 | myPayload = RED.util.evaluateNodeProperty(this.payload, this.payloadType, this, msg); 176 | } 177 | node.port.readI2cBlock(hwAdd, I2C_MEM_U_PM_10_IN_VAL1 + (channel - 1) * 2, 2, buffer, function (err, size, res) { 178 | if (err) { 179 | node.error(err, msg); 180 | } 181 | else { 182 | msg.payload = (res.readIntLE(0, 2) - 10000) / 1000.0; 183 | node.send(msg); 184 | } 185 | }); 186 | 187 | } catch (err) { 188 | this.error(err, msg); 189 | } 190 | 191 | }); 192 | 193 | node.on("close", function () { 194 | node.port.closeSync(); 195 | }); 196 | } 197 | RED.nodes.registerType("IND D10V in", DVInNode); 198 | 199 | function VOutNode(n) { 200 | RED.nodes.createNode(this, n); 201 | this.stack = parseInt(n.stack); 202 | this.channel = parseInt(n.channel); 203 | this.payload = n.payload; 204 | this.payloadType = n.payloadType; 205 | var node = this; 206 | var buffer = Buffer.alloc(2); 207 | 208 | node.port = I2C.openSync(1); 209 | node.on("input", function (msg) { 210 | var myPayload; 211 | var stack = node.stack; 212 | if (isNaN(stack)) stack = msg.stack; 213 | var channel = node.channel; 214 | if (isNaN(channel)) channel = msg.channel; 215 | stack = parseInt(stack); 216 | channel = parseInt(channel); 217 | //var buffcount = parseInt(node.count); 218 | if (this.payloadType == null) { 219 | myPayload = this.payload; 220 | } else if (this.payloadType == 'none') { 221 | myPayload = null; 222 | } else { 223 | myPayload = RED.util.evaluateNodeProperty(this.payload, this.payloadType, this, msg); 224 | } 225 | if (isNaN(stack)) { 226 | this.status({ fill: "red", shape: "ring", text: "Stack level (" + stack + ") value is missing or incorrect" }); 227 | return; 228 | } else if (isNaN(channel)) { 229 | this.status({ fill: "red", shape: "ring", text: "Sensor number (" + channel + ") value is missing or incorrect" }); 230 | return; 231 | } else if (isNaN(myPayload)) { 232 | this.status({ fill: "red", shape: "ring", text: "Payload type must be a number (" + this.payload + ") value is missing or incorrect myPayload: (" + myPayload + ")" }); 233 | return; 234 | } 235 | else { 236 | this.status({}); 237 | } 238 | try { 239 | var hwAdd = DEFAULT_HW_ADD; 240 | if (stack < 0) { 241 | stack = 0; 242 | } 243 | if (stack > 7) { 244 | stack = 7; 245 | } 246 | hwAdd += stack; 247 | 248 | if (channel < 1) { 249 | channel = 1; 250 | } 251 | if (channel > 4) { 252 | channel = 4; 253 | } 254 | 255 | if (myPayload < 0) { 256 | myPayload = 0; 257 | } 258 | if (myPayload > 10) { 259 | myPayload = 10; 260 | } 261 | var intVal = Math.round(myPayload * 1000); 262 | 263 | node.port.writeWord(hwAdd, I2C_MEM_U0_10_OUT_VAL1 + (channel - 1) * 2, intVal, function (err, size, res) { 264 | if (err) { 265 | node.error(err, msg); 266 | } 267 | else { 268 | //rmsg.payload = res.readIntLE(0, 2) / 1000.0; 269 | node.send(msg); 270 | } 271 | }); 272 | 273 | } catch (err) { 274 | this.error(err, msg); 275 | } 276 | 277 | }); 278 | 279 | node.on("close", function () { 280 | node.port.closeSync(); 281 | }); 282 | } 283 | RED.nodes.registerType("IND 0-10V out", VOutNode); 284 | 285 | // current nodes 286 | 287 | function IInNode(n) { 288 | RED.nodes.createNode(this, n); 289 | this.stack = parseInt(n.stack); 290 | this.channel = parseInt(n.channel); 291 | this.payload = n.payload; 292 | this.payloadType = n.payloadType; 293 | var node = this; 294 | var buffer = Buffer.alloc(2); 295 | 296 | node.port = I2C.openSync(1); 297 | node.on("input", function (msg) { 298 | var myPayload; 299 | var stack = node.stack; 300 | if (isNaN(stack)) stack = msg.stack; 301 | var channel = node.channel; 302 | if (isNaN(channel)) channel = msg.channel; 303 | stack = parseInt(stack); 304 | channel = parseInt(channel); 305 | //var buffcount = parseInt(node.count); 306 | if (isNaN(stack)) { 307 | this.status({ fill: "red", shape: "ring", text: "Stack level (" + stack + ") value is missing or incorrect" }); 308 | return; 309 | } else if (isNaN(channel)) { 310 | this.status({ fill: "red", shape: "ring", text: "Sensor number (" + channel + ") value is missing or incorrect" }); 311 | return; 312 | } else { 313 | this.status({}); 314 | } 315 | try { 316 | var hwAdd = DEFAULT_HW_ADD; 317 | if (stack < 0) { 318 | stack = 0; 319 | } 320 | if (stack > 7) { 321 | stack = 7; 322 | } 323 | hwAdd += stack; 324 | 325 | if (channel < 1) { 326 | channel = 1; 327 | } 328 | if (channel > 4) { 329 | channel = 4; 330 | } 331 | 332 | if (this.payloadType == null) { 333 | myPayload = this.payload; 334 | } else if (this.payloadType == 'none') { 335 | myPayload = null; 336 | } else { 337 | myPayload = RED.util.evaluateNodeProperty(this.payload, this.payloadType, this, msg); 338 | } 339 | node.port.readI2cBlock(hwAdd, I2C_MEM_I4_20_IN_VAL1 + (channel - 1) * 2, 2, buffer, function (err, size, res) { 340 | if (err) { 341 | node.error(err, msg); 342 | } 343 | else { 344 | msg.payload = res.readIntLE(0, 2) / 1000.0; 345 | node.send(msg); 346 | } 347 | }); 348 | 349 | } catch (err) { 350 | this.error(err, msg); 351 | } 352 | 353 | }); 354 | 355 | node.on("close", function () { 356 | node.port.closeSync(); 357 | }); 358 | } 359 | RED.nodes.registerType("IND 4-20mA in", IInNode); 360 | 361 | 362 | function IOutNode(n) { 363 | RED.nodes.createNode(this, n); 364 | this.stack = parseInt(n.stack); 365 | this.channel = parseInt(n.channel); 366 | this.payload = n.payload; 367 | this.payloadType = n.payloadType; 368 | var node = this; 369 | var buffer = Buffer.alloc(2); 370 | 371 | node.port = I2C.openSync(1); 372 | node.on("input", function (msg) { 373 | var myPayload; 374 | var stack = node.stack; 375 | if (isNaN(stack)) stack = msg.stack; 376 | var channel = node.channel; 377 | if (isNaN(channel)) channel = msg.channel; 378 | stack = parseInt(stack); 379 | channel = parseInt(channel); 380 | //var buffcount = parseInt(node.count); 381 | if (this.payloadType == null) { 382 | myPayload = this.payload; 383 | } else if (this.payloadType == 'none') { 384 | myPayload = null; 385 | } else { 386 | myPayload = RED.util.evaluateNodeProperty(this.payload, this.payloadType, this, msg); 387 | } 388 | if (isNaN(stack)) { 389 | this.status({ fill: "red", shape: "ring", text: "Stack level (" + stack + ") value is missing or incorrect" }); 390 | return; 391 | } else if (isNaN(channel)) { 392 | this.status({ fill: "red", shape: "ring", text: "Sensor number (" + channel + ") value is missing or incorrect" }); 393 | return; 394 | } else if (isNaN(myPayload)) { 395 | this.status({ fill: "red", shape: "ring", text: "Payload type must be a number (" + this.payload + ") value is missing or incorrect myPayload: (" + myPayload + ")" }); 396 | return; 397 | } 398 | else { 399 | this.status({}); 400 | } 401 | try { 402 | var hwAdd = DEFAULT_HW_ADD; 403 | if (stack < 0) { 404 | stack = 0; 405 | } 406 | if (stack > 7) { 407 | stack = 7; 408 | } 409 | hwAdd += stack; 410 | 411 | if (channel < 1) { 412 | channel = 1; 413 | } 414 | if (channel > 4) { 415 | channel = 4; 416 | } 417 | 418 | if (myPayload < 4) { 419 | myPayload = 4; 420 | } 421 | if (myPayload > 20) { 422 | myPayload = 20; 423 | } 424 | var intVal = Math.round(myPayload * 1000); 425 | 426 | node.port.writeWord(hwAdd, I2C_MEM_I4_20_OUT_VAL1 + (channel - 1) * 2, intVal, function (err, size, res) { 427 | if (err) { 428 | node.error(err, msg); 429 | } 430 | else { 431 | //rmsg.payload = res.readIntLE(0, 2) / 1000.0; 432 | node.send(msg); 433 | } 434 | }); 435 | 436 | } catch (err) { 437 | this.error(err, msg); 438 | } 439 | 440 | }); 441 | 442 | node.on("close", function () { 443 | node.port.closeSync(); 444 | }); 445 | } 446 | RED.nodes.registerType("IND 4-20mA out", IOutNode); 447 | 448 | function OptoCounterNode(n) { 449 | RED.nodes.createNode(this, n); 450 | this.stack = parseInt(n.stack); 451 | this.channel = parseInt(n.channel); 452 | this.falling = n.falling; 453 | this.rising = n.rising; 454 | this.payload = n.payload; 455 | this.payloadType = n.payloadType; 456 | var node = this; 457 | var buffer = Buffer.alloc(4); 458 | var lastCfgCh = 0; 459 | var cfgByte = 0; 460 | 461 | node.port = I2C.openSync(1); 462 | node.on("input", function (msg) { 463 | var myPayload; 464 | var stack = node.stack; 465 | if (isNaN(stack)) stack = msg.stack; 466 | var channel = node.channel; 467 | if (isNaN(channel)) channel = msg.channel; 468 | stack = parseInt(stack); 469 | channel = parseInt(channel); 470 | var rising = true; 471 | var falling = true; 472 | if (node.rising == false || node.rising == "false" || node.rising == 0) { 473 | rising = false; 474 | } 475 | if (node.falling == false || node.falling == "false" || node.falling == 0) { 476 | falling = false; 477 | } 478 | var resetIn; 479 | if (isNaN(msg.reset)) resetIn = 0; 480 | else resetIn = msg.reset; 481 | //var buffcount = parseInt(node.count); 482 | if (isNaN(stack)) { 483 | this.status({ fill: "red", shape: "ring", text: "Stack level (" + stack + ") value is missing or incorrect" }); 484 | return; 485 | } else if (isNaN(channel)) { 486 | this.status({ fill: "red", shape: "ring", text: "Sensor number (" + channel + ") value is missing or incorrect" }); 487 | return; 488 | } else { 489 | this.status({}); 490 | } 491 | var hwAdd = DEFAULT_HW_ADD; 492 | if (stack < 0) { 493 | stack = 0; 494 | } 495 | if (stack > 7) { 496 | stack = 7; 497 | } 498 | hwAdd += stack; 499 | 500 | if (channel < 1) { 501 | channel = 1; 502 | } 503 | if (channel > 4) { 504 | channel = 4; 505 | } 506 | if (lastCfgCh != channel) { 507 | //node.log("Check configuration"); 508 | node.port.readByte(hwAdd, I2C_MEM_OPTO_RISING_ENABLE, function (err, rbyte) { 509 | if (err) { 510 | node.error(err, msg); 511 | } 512 | else { 513 | if (((rising == true) && ((rbyte & (1 << (channel - 1))) == 0)) || ((rising == false) && ((rbyte & (1 << (channel - 1))) != 0))) { 514 | cfgByte = rbyte; 515 | if (rising) { 516 | cfgByte |= 1 << (channel - 1); 517 | //node.log("Enable rising edge counting on channel " + channel ); 518 | } 519 | else { 520 | cfgByte &= 0xff ^ (1 << (channel - 1)); 521 | //node.log("Disable rising edge counting on channel " + channel ); 522 | } 523 | 524 | node.port.writeByte(hwAdd, I2C_MEM_OPTO_RISING_ENABLE, cfgByte, function (err) { 525 | if (err) { 526 | node.error(err, msg); 527 | } 528 | }); 529 | } 530 | } 531 | }); 532 | 533 | node.port.readByte(hwAdd, I2C_MEM_OPTO_FALLING_ENABLE, function (err, rbyte) { 534 | if (err) { 535 | node.error(err, msg); 536 | } 537 | else { 538 | if (((falling == true) && ((rbyte & (1 << (channel - 1))) == 0)) || ((falling == false) && ((rbyte & (1 << (channel - 1))) != 0))) { 539 | cfgByte = rbyte; 540 | if (falling) { 541 | cfgByte |= 1 << (channel - 1); 542 | //node.log("Enable falling edge counting on channel " + channel ); 543 | } 544 | else { 545 | cfgByte &= 0xff ^ (1 << (channel - 1)); 546 | //node.log("Disable falling edge counting on channel " + channel ); 547 | } 548 | 549 | node.port.writeByte(hwAdd, I2C_MEM_OPTO_FALLING_ENABLE, cfgByte, function (err) { 550 | if (err) { 551 | node.error(err, msg); 552 | } 553 | }); 554 | } 555 | } 556 | }); 557 | lastCfgCh = channel; 558 | } 559 | if (resetIn != 0) { 560 | node.port.writeByte(hwAdd, I2C_MEM_OPTO_CH_CONT_RESET, channel, function (err) { 561 | if (err) { 562 | node.error(err, msg); 563 | } 564 | }); 565 | } 566 | else { 567 | try { 568 | 569 | if (this.payloadType == null) { 570 | myPayload = this.payload; 571 | } else if (this.payloadType == 'none') { 572 | myPayload = null; 573 | } else { 574 | myPayload = RED.util.evaluateNodeProperty(this.payload, this.payloadType, this, msg); 575 | } 576 | node.port.readI2cBlock(hwAdd, I2C_MEM_OPTO_COUNT1 + (channel - 1) * 2, 2, buffer, function (err, size, res) { 577 | if (err) { 578 | node.error(err, msg); 579 | } 580 | else { 581 | msg.payload = res.readIntLE(0, 2); 582 | node.send(msg); 583 | } 584 | }); 585 | 586 | } catch (err) { 587 | this.error(err, msg); 588 | } 589 | } 590 | }); 591 | 592 | node.on("close", function () { 593 | node.port.closeSync(); 594 | }); 595 | } 596 | RED.nodes.registerType("IND OPT cnt", OptoCounterNode); 597 | 598 | 599 | function OptoInNode(n) { 600 | RED.nodes.createNode(this, n); 601 | this.stack = parseInt(n.stack); 602 | this.channel = parseInt(n.channel); 603 | this.falling = n.falling; 604 | this.rising = n.rising; 605 | this.payload = n.payload; 606 | this.payloadType = n.payloadType; 607 | var node = this; 608 | var buffer = Buffer.alloc(4); 609 | 610 | 611 | node.port = I2C.openSync(1); 612 | node.on("input", function (msg) { 613 | var myPayload; 614 | var stack = node.stack; 615 | if (isNaN(stack)) stack = msg.stack; 616 | var channel = node.channel; 617 | if (isNaN(channel)) channel = msg.channel; 618 | stack = parseInt(stack); 619 | channel = parseInt(channel); 620 | 621 | //var buffcount = parseInt(node.count); 622 | if (isNaN(stack)) { 623 | this.status({ fill: "red", shape: "ring", text: "Stack level (" + stack + ") value is missing or incorrect" }); 624 | return; 625 | } else if (isNaN(channel)) { 626 | this.status({ fill: "red", shape: "ring", text: "Sensor number (" + channel + ") value is missing or incorrect" }); 627 | return; 628 | } else { 629 | this.status({}); 630 | } 631 | var hwAdd = DEFAULT_HW_ADD; 632 | if (stack < 0) { 633 | stack = 0; 634 | } 635 | if (stack > 7) { 636 | stack = 7; 637 | } 638 | hwAdd += stack; 639 | 640 | if (channel < 1) { 641 | channel = 1; 642 | } 643 | if (channel > 4) { 644 | channel = 4 645 | } 646 | try { 647 | 648 | if (this.payloadType == null) { 649 | myPayload = this.payload; 650 | } else if (this.payloadType == 'none') { 651 | myPayload = null; 652 | } else { 653 | myPayload = RED.util.evaluateNodeProperty(this.payload, this.payloadType, this, msg); 654 | } 655 | node.port.readByte(hwAdd, I2C_MEM_OPTO_IN_VAL, function (err, res) { 656 | if (err) { 657 | node.error(err, msg); 658 | } 659 | else { 660 | //node.log("Opto val read " + res ); 661 | if ((res & (1 << (channel - 1))) != 0) { 662 | msg.payload = true; 663 | } 664 | else { 665 | msg.payload = false; 666 | } 667 | node.send(msg); 668 | } 669 | }); 670 | 671 | } catch (err) { 672 | this.error(err, msg); 673 | } 674 | 675 | }); 676 | 677 | node.on("close", function () { 678 | node.port.closeSync(); 679 | }); 680 | } 681 | RED.nodes.registerType("IND OPT in", OptoInNode); 682 | 683 | function PWMOutNode(n) { 684 | RED.nodes.createNode(this, n); 685 | this.stack = parseInt(n.stack); 686 | this.channel = parseInt(n.channel); 687 | this.payload = n.payload; 688 | this.payloadType = n.payloadType; 689 | var node = this; 690 | var buffer = Buffer.alloc(2); 691 | 692 | node.port = I2C.openSync(1); 693 | node.on("input", function (msg) { 694 | var myPayload; 695 | var stack = node.stack; 696 | if (isNaN(stack)) stack = msg.stack; 697 | var channel = node.channel; 698 | if (isNaN(channel)) channel = msg.channel; 699 | stack = parseInt(stack); 700 | channel = parseInt(channel); 701 | //var buffcount = parseInt(node.count); 702 | if (this.payloadType == null) { 703 | myPayload = this.payload; 704 | } else if (this.payloadType == 'none') { 705 | myPayload = null; 706 | } else { 707 | myPayload = RED.util.evaluateNodeProperty(this.payload, this.payloadType, this, msg); 708 | } 709 | if (isNaN(stack)) { 710 | this.status({ fill: "red", shape: "ring", text: "Stack level (" + stack + ") value is missing or incorrect" }); 711 | return; 712 | } else if (isNaN(channel)) { 713 | this.status({ fill: "red", shape: "ring", text: "Sensor number (" + channel + ") value is missing or incorrect" }); 714 | return; 715 | } else if (isNaN(myPayload)) { 716 | this.status({ fill: "red", shape: "ring", text: "Payload type must be a number (" + this.payload + ") value is missing or incorrect myPayload: (" + myPayload + ")" }); 717 | return; 718 | } 719 | else { 720 | this.status({}); 721 | } 722 | try { 723 | var hwAdd = DEFAULT_HW_ADD; 724 | if (stack < 0) { 725 | stack = 0; 726 | } 727 | if (stack > 7) { 728 | stack = 7; 729 | } 730 | hwAdd += stack; 731 | 732 | if (channel < 1) { 733 | channel = 1; 734 | } 735 | if (channel > 4) { 736 | channel = 4; 737 | } 738 | 739 | if (myPayload < 0) { 740 | myPayload = 0; 741 | } 742 | if (myPayload > 100) { 743 | myPayload = 100; 744 | } 745 | var intVal = Math.round(myPayload * 100); 746 | 747 | node.port.writeWord(hwAdd, I2C_MEM_OD_PWM1 + (channel - 1) * 2, intVal, function (err, size, res) { 748 | if (err) { 749 | node.error(err, msg); 750 | } 751 | else { 752 | //rmsg.payload = res.readIntLE(0, 2) / 1000.0; 753 | node.send(msg); 754 | } 755 | }); 756 | 757 | } catch (err) { 758 | this.error(err, msg); 759 | } 760 | 761 | }); 762 | 763 | node.on("close", function () { 764 | node.port.closeSync(); 765 | }); 766 | } 767 | RED.nodes.registerType("IND OD out", PWMOutNode); 768 | 769 | function CpuTempNode(n) { 770 | RED.nodes.createNode(this, n); 771 | this.stack = parseInt(n.stack); 772 | this.channel = parseInt(n.channel); 773 | this.payload = n.payload; 774 | this.payloadType = n.payloadType; 775 | var node = this; 776 | var buffer = Buffer.alloc(2); 777 | 778 | node.port = I2C.openSync(1); 779 | node.on("input", function (msg) { 780 | var myPayload; 781 | var stack = node.stack; 782 | if (isNaN(stack)) stack = msg.stack; 783 | stack = parseInt(stack); 784 | 785 | if (isNaN(stack)) { 786 | this.status({ fill: "red", shape: "ring", text: "Stack level (" + stack + ") value is missing or incorrect" }); 787 | return; 788 | } else { 789 | this.status({}); 790 | } 791 | try { 792 | var hwAdd = DEFAULT_HW_ADD; 793 | if (stack < 0) { 794 | stack = 0; 795 | } 796 | if (stack > 7) { 797 | stack = 7; 798 | } 799 | hwAdd += stack; 800 | 801 | if (this.payloadType == null) { 802 | myPayload = this.payload; 803 | } else if (this.payloadType == 'none') { 804 | myPayload = null; 805 | } else { 806 | myPayload = RED.util.evaluateNodeProperty(this.payload, this.payloadType, this, msg); 807 | } 808 | node.port.readByte(hwAdd, I2C_MEM_DIAG_TEMPERATURE, function (err, res) { 809 | if (err) { 810 | node.error(err, msg); 811 | } 812 | else { 813 | msg.payload = res; 814 | node.send(msg); 815 | } 816 | }); 817 | 818 | } catch (err) { 819 | this.error(err, msg); 820 | } 821 | 822 | }); 823 | 824 | node.on("close", function () { 825 | node.port.closeSync(); 826 | }); 827 | } 828 | RED.nodes.registerType("IND CPU Temp", CpuTempNode); 829 | 830 | function PSVoltageNode(n) { 831 | RED.nodes.createNode(this, n); 832 | this.stack = parseInt(n.stack); 833 | this.channel = parseInt(n.channel); 834 | this.payload = n.payload; 835 | this.payloadType = n.payloadType; 836 | var node = this; 837 | var buffer = Buffer.alloc(2); 838 | 839 | node.port = I2C.openSync(1); 840 | node.on("input", function (msg) { 841 | var myPayload; 842 | var stack = node.stack; 843 | if (isNaN(stack)) stack = msg.stack; 844 | stack = parseInt(stack); 845 | 846 | if (isNaN(stack)) { 847 | this.status({ fill: "red", shape: "ring", text: "Stack level (" + stack + ") value is missing or incorrect" }); 848 | return; 849 | } else { 850 | this.status({}); 851 | } 852 | try { 853 | var hwAdd = DEFAULT_HW_ADD; 854 | if (stack < 0) { 855 | stack = 0; 856 | } 857 | if (stack > 7) { 858 | stack = 7; 859 | } 860 | hwAdd += stack; 861 | 862 | if (this.payloadType == null) { 863 | myPayload = this.payload; 864 | } else if (this.payloadType == 'none') { 865 | myPayload = null; 866 | } else { 867 | myPayload = RED.util.evaluateNodeProperty(this.payload, this.payloadType, this, msg); 868 | } 869 | node.port.readWord(hwAdd, I2C_MEM_DIAG_24V, function (err, res) { 870 | if (err) { 871 | node.error(err, msg); 872 | } 873 | else { 874 | msg.payload = res / 1000.0; 875 | node.send(msg); 876 | } 877 | }); 878 | 879 | } catch (err) { 880 | this.error(err, msg); 881 | } 882 | 883 | }); 884 | 885 | node.on("close", function () { 886 | node.port.closeSync(); 887 | }); 888 | } 889 | RED.nodes.registerType("IND PS Voltage", PSVoltageNode); 890 | 891 | 892 | function RasPiVoltageNode(n) { 893 | RED.nodes.createNode(this, n); 894 | this.stack = parseInt(n.stack); 895 | this.channel = parseInt(n.channel); 896 | this.payload = n.payload; 897 | this.payloadType = n.payloadType; 898 | var node = this; 899 | var buffer = Buffer.alloc(2); 900 | 901 | node.port = I2C.openSync(1); 902 | node.on("input", function (msg) { 903 | var myPayload; 904 | var stack = node.stack; 905 | if (isNaN(stack)) stack = msg.stack; 906 | stack = parseInt(stack); 907 | 908 | if (isNaN(stack)) { 909 | this.status({ fill: "red", shape: "ring", text: "Stack level (" + stack + ") value is missing or incorrect" }); 910 | return; 911 | } else { 912 | this.status({}); 913 | } 914 | try { 915 | var hwAdd = DEFAULT_HW_ADD; 916 | if (stack < 0) { 917 | stack = 0; 918 | } 919 | if (stack > 7) { 920 | stack = 7; 921 | } 922 | hwAdd += stack; 923 | 924 | if (this.payloadType == null) { 925 | myPayload = this.payload; 926 | } else if (this.payloadType == 'none') { 927 | myPayload = null; 928 | } else { 929 | myPayload = RED.util.evaluateNodeProperty(this.payload, this.payloadType, this, msg); 930 | } 931 | node.port.readWord(hwAdd, I2C_MEM_DIAG_5V, function (err, res) { 932 | if (err) { 933 | node.error(err, msg); 934 | } 935 | else { 936 | msg.payload = res / 1000.0; 937 | node.send(msg); 938 | } 939 | }); 940 | 941 | } catch (err) { 942 | this.error(err, msg); 943 | } 944 | 945 | }); 946 | 947 | node.on("close", function () { 948 | node.port.closeSync(); 949 | }); 950 | } 951 | RED.nodes.registerType("IND RasPi Voltage", RasPiVoltageNode); 952 | 953 | // LEDs 954 | function LedOutNode(n) { 955 | RED.nodes.createNode(this, n); 956 | this.stack = parseInt(n.stack); 957 | this.channel = parseInt(n.channel); 958 | this.payload = n.payload; 959 | this.payloadType = n.payloadType; 960 | var node = this; 961 | 962 | node.port = I2C.openSync(1); 963 | node.on("input", function (msg) { 964 | var myPayload; 965 | var stack = node.stack; 966 | if (isNaN(stack)) stack = msg.stack; 967 | var channel = node.channel; 968 | if (isNaN(channel)) channel = msg.channel; 969 | stack = parseInt(stack); 970 | channel = parseInt(channel); 971 | //var buffcount = parseInt(node.count); 972 | if (this.payloadType == null) { 973 | myPayload = this.payload; 974 | } else if (this.payloadType == 'none') { 975 | myPayload = null; 976 | } else { 977 | myPayload = RED.util.evaluateNodeProperty(this.payload, this.payloadType, this, msg); 978 | } 979 | if (isNaN(stack)) { 980 | this.status({ fill: "red", shape: "ring", text: "Stack level (" + stack + ") value is missing or incorrect" }); 981 | return; 982 | } else if (isNaN(channel)) { 983 | this.status({ fill: "red", shape: "ring", text: "Sensor number (" + channel + ") value is missing or incorrect" }); 984 | return; 985 | } else if (isNaN(myPayload)) { 986 | this.status({ fill: "red", shape: "ring", text: "Payload type must be a number (" + this.payload + ") value is missing or incorrect myPayload: (" + myPayload + ")" }); 987 | return; 988 | } 989 | else { 990 | this.status({}); 991 | } 992 | try { 993 | var hwAdd = DEFAULT_HW_ADD; 994 | if (stack < 0) { 995 | stack = 0; 996 | } 997 | if (stack > 7) { 998 | stack = 7; 999 | } 1000 | hwAdd += stack; 1001 | 1002 | if (channel < 1) { 1003 | channel = 1; 1004 | } 1005 | if (channel > 4) { 1006 | channel = 4; 1007 | } 1008 | var regAdd = 0; 1009 | if (myPayload != 0) { // turn on LED 1010 | regAdd = I2C_MEM_RELAY_SET; 1011 | 1012 | } else { // turn off LED 1013 | regAdd = I2C_MEM_RELAY_CLR; 1014 | } 1015 | 1016 | 1017 | var intVal = channel + 4 1018 | 1019 | node.port.writeByte(hwAdd, regAdd, intVal, function (err, size, res) { 1020 | if (err) { 1021 | node.error(err, msg); 1022 | } 1023 | else { 1024 | //rmsg.payload = res.readIntLE(0, 2) / 1000.0; 1025 | node.send(msg); 1026 | } 1027 | }); 1028 | 1029 | } catch (err) { 1030 | this.error(err, msg); 1031 | } 1032 | 1033 | }); 1034 | 1035 | node.on("close", function () { 1036 | node.port.closeSync(); 1037 | }); 1038 | } 1039 | RED.nodes.registerType("IND LED out", LedOutNode); 1040 | 1041 | // Real Time Clock 1042 | function RtcReadNode(n) { 1043 | RED.nodes.createNode(this, n); 1044 | this.stack = parseInt(n.stack); 1045 | this.channel = parseInt(n.channel); 1046 | this.payload = n.payload; 1047 | this.payloadType = n.payloadType; 1048 | var node = this; 1049 | var buffer = Buffer.alloc(6); // Y, M, D, H, M, S 1050 | node.port = I2C.openSync(1); 1051 | node.on("input", function (msg) { 1052 | var myPayload; 1053 | var stack = node.stack; 1054 | if (isNaN(stack)) stack = msg.stack; 1055 | stack = parseInt(stack); 1056 | 1057 | if (isNaN(stack)) { 1058 | this.status({ fill: "red", shape: "ring", text: "Stack level (" + stack + ") value is missing or incorrect" }); 1059 | return; 1060 | } else { 1061 | this.status({}); 1062 | } 1063 | try { 1064 | var hwAdd = DEFAULT_HW_ADD; 1065 | if (stack < 0) { 1066 | stack = 0; 1067 | } 1068 | if (stack > 7) { 1069 | stack = 7; 1070 | } 1071 | hwAdd += stack; 1072 | 1073 | if (this.payloadType == null) { 1074 | myPayload = this.payload; 1075 | } else if (this.payloadType == 'none') { 1076 | myPayload = null; 1077 | } else { 1078 | myPayload = RED.util.evaluateNodeProperty(this.payload, 1079 | this.payloadType, 1080 | this, msg); 1081 | } 1082 | node.port.readI2cBlock(hwAdd, I2C_MEM_RTC_YEAR, 1, buffer, 1083 | function (err, size, res) { 1084 | if (err) { 1085 | node.error(err, msg); 1086 | } 1087 | else { 1088 | // Following assumes 1089 | // RTC is in local time 1090 | msg.payload = Date(buffer[0] + 2000, 1091 | buffer[1] - 1, 1092 | buffer[2], 1093 | buffer[3], 1094 | buffer[4], 1095 | buffer[5], 1096 | 0); 1097 | node.send(msg); 1098 | } 1099 | }); 1100 | } catch (err) { 1101 | this.error(err, msg); 1102 | } 1103 | 1104 | }); 1105 | 1106 | node.on("close", function () { 1107 | node.port.closeSync(); 1108 | }); 1109 | } 1110 | RED.nodes.registerType("IND Read RTC", RtcReadNode); 1111 | 1112 | 1113 | function OwbTempNode(n) { 1114 | RED.nodes.createNode(this, n); 1115 | this.stack = parseInt(n.stack); 1116 | this.channel = parseInt(n.channel); 1117 | this.payload = n.payload; 1118 | this.payloadType = n.payloadType; 1119 | var node = this; 1120 | var buffer = Buffer.alloc(2); 1121 | 1122 | node.port = I2C.openSync(1); 1123 | node.on("input", function (msg) { 1124 | var myPayload; 1125 | var stack = node.stack; 1126 | if (isNaN(stack)) stack = msg.stack; 1127 | var channel = node.channel; 1128 | if (isNaN(channel)) channel = msg.channel; 1129 | stack = parseInt(stack); 1130 | channel = parseInt(channel); 1131 | //var buffcount = parseInt(node.count); 1132 | if (isNaN(stack)) { 1133 | this.status({ fill: "red", shape: "ring", text: "Stack level (" + stack + ") value is missing or incorrect" }); 1134 | return; 1135 | } else if (isNaN(channel)) { 1136 | this.status({ fill: "red", shape: "ring", text: "Sensor number (" + channel + ") value is missing or incorrect" }); 1137 | return; 1138 | } else { 1139 | this.status({}); 1140 | } 1141 | try { 1142 | var hwAdd = DEFAULT_HW_ADD; 1143 | if (stack < 0) { 1144 | stack = 0; 1145 | } 1146 | if (stack > 7) { 1147 | stack = 7; 1148 | } 1149 | hwAdd += stack; 1150 | 1151 | if (channel < 1) { 1152 | channel = 1; 1153 | } 1154 | if (channel > 4) { 1155 | channel = 4; 1156 | } 1157 | 1158 | if (this.payloadType == null) { 1159 | myPayload = this.payload; 1160 | } else if (this.payloadType == 'none') { 1161 | myPayload = null; 1162 | } else { 1163 | myPayload = RED.util.evaluateNodeProperty(this.payload, this.payloadType, this, msg); 1164 | } 1165 | node.port.readI2cBlock(hwAdd, I2C_MEM_1WB_T1 + (channel - 1) * 2, 2, buffer, function (err, size, res) { 1166 | if (err) { 1167 | node.error(err, msg); 1168 | } 1169 | else { 1170 | msg.payload = res.readIntLE(0, 2) / 100.0; 1171 | node.send(msg); 1172 | } 1173 | }); 1174 | 1175 | } catch (err) { 1176 | this.error(err, msg); 1177 | } 1178 | 1179 | }); 1180 | 1181 | node.on("close", function () { 1182 | node.port.closeSync(); 1183 | }); 1184 | } 1185 | RED.nodes.registerType("IND OWB Temp", OwbTempNode); 1186 | 1187 | 1188 | function OwbScanNode(n) { 1189 | RED.nodes.createNode(this, n); 1190 | this.stack = parseInt(n.stack); 1191 | 1192 | this.payload = n.payload; 1193 | this.payloadType = n.payloadType; 1194 | var node = this; 1195 | var buffer = Buffer.alloc(8); 1196 | var sensors = 0; 1197 | var code = new BigUint64Array(1); 1198 | //var codes = []; 1199 | //const codes = new BigUint64Array(16); 1200 | var k = 0; 1201 | node.port = I2C.openSync(1); 1202 | node.on("input", function (msg) { 1203 | var myPayload; 1204 | var stack = node.stack; 1205 | if (isNaN(stack)) stack = msg.stack; 1206 | 1207 | stack = parseInt(stack); 1208 | 1209 | //var buffcount = parseInt(node.count); 1210 | if (isNaN(stack)) { 1211 | this.status({ fill: "red", shape: "ring", text: "Stack level (" + stack + ") value is missing or incorrect" }); 1212 | return; 1213 | } else { 1214 | this.status({}); 1215 | } 1216 | try { 1217 | var hwAdd = DEFAULT_HW_ADD; 1218 | if (stack < 0) { 1219 | stack = 0; 1220 | } 1221 | if (stack > 7) { 1222 | stack = 7; 1223 | } 1224 | hwAdd += stack; 1225 | 1226 | if (this.payloadType == null) { 1227 | myPayload = this.payload; 1228 | } else if (this.payloadType == 'none') { 1229 | myPayload = null; 1230 | } else { 1231 | myPayload = RED.util.evaluateNodeProperty(this.payload, this.payloadType, this, msg); 1232 | } 1233 | buffer[0] = 0xaa; 1234 | node.port.writeByteSync(hwAdd, I2C_MEM_1WB_START_SEARCH, buffer[0], function (err) { 1235 | if (err) { 1236 | node.error(err, msg); 1237 | return; 1238 | } 1239 | else { 1240 | //rmsg.payload = res.readIntLE(0, 2) / 1000.0; 1241 | //node.send(msg); 1242 | } 1243 | }); 1244 | 1245 | setTimeout(function () { 1246 | 1247 | sensors = node.port.readByteSync(hwAdd, I2C_MEM_1WB_DEV);// 1248 | 1249 | console.log("Discovered " + sensors +" sensors"); 1250 | const codes = new BigUint64Array(sensors); 1251 | k = 0; 1252 | while (k < sensors) { 1253 | 1254 | buffer[0] = k; 1255 | node.port.writeByteSync(hwAdd, I2C_MEM_1WB_ROM_CODE_IDX, buffer[0]);// 1256 | //console.log("Command sent to read sensor ID " + buffer[0]); 1257 | 1258 | var t = node.port.readI2cBlockSync(hwAdd, I2C_MEM_1WB_ROM_CODE, 8, buffer);// 1259 | 1260 | code = 0; 1261 | 1262 | //console.log(buffer); 1263 | 1264 | for (var j = 0; j < 8; j++) { 1265 | codes[k] = BigInt(codes[k] * BigInt(256)) + BigInt(buffer[j]); 1266 | } 1267 | 1268 | //console.log("Read ROM code ret " + codes[k]); 1269 | k++; 1270 | 1271 | } 1272 | msg.payload = codes; 1273 | node.send(msg, sensors); 1274 | }, 500); 1275 | 1276 | } catch (err) { 1277 | this.error(err, msg); 1278 | } 1279 | 1280 | }); 1281 | 1282 | node.on("close", function () { 1283 | node.port.closeSync(); 1284 | }); 1285 | } 1286 | RED.nodes.registerType("IND OWB Scan", OwbScanNode); 1287 | } 1288 | -------------------------------------------------------------------------------- /node-red-contrib-sm-ind/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-red-contrib-sm-ind", 3 | "version": "1.0.5", 4 | "bundleDependencies": false, 5 | "dependencies": { 6 | "i2c-bus": ">1.0.0" 7 | }, 8 | "engines": { 9 | "node": ">=10.0.0" 10 | }, 11 | "deprecated": false, 12 | "description": "A Node-RED collection of nodes to control Sequent Microsystems Industrial Automation Card", 13 | "main": "ind.js", 14 | "scripts": { 15 | "test": "echo \"Error: no test specified\" && exit 1" 16 | }, 17 | "author": "Sequent Microsystems", 18 | "license": "MIT", 19 | "node-red" : { 20 | "version": ">=2.0.0", 21 | "nodes": { 22 | "ind": "ind.js" 23 | } 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "https://github.com/SequentMicrosystems/megaind-rpi/tree/master/node-red-contrib-sm-ind" 28 | }, 29 | "keywords": [ 30 | "node-red", 31 | "node-red-contrib", 32 | "i2c", 33 | "industrial", 34 | "automation", 35 | "0-10V", 36 | "4-20mA", 37 | "open-drain", 38 | "optoisolated", 39 | "MODBUS" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /python/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Alexandru Burcea 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/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 | -------------------------------------------------------------------------------- /python/README.md: -------------------------------------------------------------------------------- 1 | 2 | # megaind 3 | 4 | This is the python library to control the [Industrial Automation Stackable Card for Raspberry Pi](https://sequentmicrosystems.com/collections/all-io-cards/products/industrial-raspberry-pi). 5 | 6 | ## Install 7 | 8 | ```bash 9 | sudo pip install SMmegaind 10 | ``` 11 | 12 | ## Update 13 | 14 | ```bash 15 | sudo pip install --upgrade SMmegaind 16 | ``` 17 | 18 | ## Usage 19 | 20 | Now you can import the megaind library and use its functions. To test, read 0 - 10V input channel 1 from the MEGA-IND board with stack level 0: 21 | 22 | ```bash 23 | ~$ python 24 | Python 2.7.9 (default, Sep 17 2016, 20:26:04) 25 | [GCC 4.9.2] on linux2 26 | Type "help", "copyright", "credits" or "license" for more information. 27 | >>> import megaind 28 | >>> megaind.get0_10In(0, 1) 29 | 0.003 30 | >>> 31 | ``` 32 | Checkout the [/tests](https://github.com/SequentMicrosystems/megaind-rpi/tree/master/python/tests) subfolder for more usage examples. 33 | All tests assume that one card with no address jumpers (stack level = 0) is present. 34 | 35 | 36 | ## Diagnose Functions 37 | 38 | ### getFwVer(stack) 39 | Return firmware version 40 | 41 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 42 | 43 | 44 | ### getRaspVolt(stack) 45 | Read raspberry voltage 46 | 47 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 48 | 49 | return Raspberry PI voltage in volts 50 | 51 | 52 | ## Analog input/output Functions 53 | 54 | ### getPowerVolt(stack) 55 | Read power source voltage 56 | 57 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 58 | 59 | return voltage in volts 60 | 61 | 62 | ### getCpuTemp(stack) 63 | Get the cpu temperature 64 | 65 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 66 | 67 | return - temperature in deg Celsius 68 | 69 | 70 | ### get0_10In(stack, ch) 71 | Return the selected input 0-10V channel value in volts. 72 | Make sure the selection jumper is not connectd in order for this measurement to be correct. 73 | 74 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 75 | 76 | ch - selected channel number [1..4] 77 | 78 | return - value in volts 79 | 80 | 81 | ### getpm10In(stack, ch) 82 | Return the selected input +/-10V channel value in volts. 83 | Make sure the selection jumper is connectd in order for this measurement to be correct. 84 | 85 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 86 | 87 | ch - selected channel number [1..4] 88 | 89 | return - value in volts 90 | 91 | 92 | ### get0_10Out(stack, ch) 93 | Get the selected output 0-10V channel value in volts 94 | 95 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 96 | 97 | ch - selected channel number [1..4] 98 | 99 | return - value in volts [0..10] 100 | 101 | 102 | ### set0_10Out(stack, ch, value) 103 | Set the selected output 0-10V channel value in volts 104 | 105 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 106 | 107 | ch - selected channel number [1..4] 108 | 109 | value - voltage output value in V [0..10] 110 | 111 | return - none 112 | 113 | 114 | ### get4_20In(stack, ch) 115 | Return the selected input 4 - 20mA channel value. 116 | 117 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 118 | 119 | ch - selected channel number [1..4] 120 | 121 | return - value in milliamps 122 | 123 | 124 | ### get4_20Out(stack, ch) 125 | Get the selected output 4 - 20mA channel value. 126 | 127 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 128 | 129 | ch - selected channel number [1..4] 130 | 131 | return - value in milliamps [4..20] 132 | 133 | 134 | ### set4_20Out(stack, ch, value) 135 | Set the selected output 4 - 20mA channel value. 136 | 137 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 138 | 139 | ch - selected channel number [1..4] 140 | 141 | value - current output value in milliamps [4..20] 142 | 143 | return - none 144 | 145 | 146 | ## Digital input/output Functions 147 | 148 | ### getOptoCh(stack, ch) 149 | Get the state of the optically isolated digital input channel 150 | 151 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 152 | 153 | ch - selected channel number [1..4] 154 | 155 | return - value of the inputs [0/1] 156 | 157 | 158 | ### getOpto(stack) 159 | Get the state of the all optically isolated digital inputs 160 | 161 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 162 | 163 | return - value of the inputs [0..15] 164 | 165 | 166 | ### getOptoCount(stack, ch) 167 | Return the counter value for corresponding optically isolated input. 168 | 169 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 170 | 171 | ch - selected channel number [1..4] 172 | 173 | return - counter value [0..65535] 174 | 175 | 176 | ### rstOptoCount(stack, ch) 177 | Reset the counter value for corresponding optically isolated input. 178 | 179 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 180 | 181 | ch - selected channel number [1..4] 182 | 183 | return none 184 | 185 | 186 | ### getOptoRisingCountEnable(stack, ch) 187 | Get the rising edge enable counting for corresponding optically isolated input. 188 | 189 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 190 | 191 | ch - selected channel number [1..4] 192 | 193 | return 1 - rising edges will be counted; 0 - not 194 | 195 | 196 | ## setOptoRisingCountEnable(stack, ch, state) 197 | Set the rising edge enable counting for corresponding optically isolated input. 198 | 199 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 200 | 201 | ch - selected channel number [1..4] 202 | 203 | state - 1 = enable; 0 - disable 204 | 205 | return none 206 | 207 | 208 | ### getOptoFallingCountEnable(stack, ch) 209 | Get the falling edge enable counting for corresponding optically isolated input. 210 | 211 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 212 | 213 | ch - selected channel number [1..4] 214 | 215 | return 1 - falling edges will be counted; 0 - not 216 | 217 | 218 | ### setOptoFallingCountEnable(stack, ch, state) 219 | Set the falling edge enable counting for corresponding optically isolated input. 220 | 221 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 222 | 223 | ch - selected channel number [1..4] 224 | 225 | state - 1 = enable; 0 - disable 226 | 227 | return none 228 | 229 | 230 | ### setOdPWM(stack, ch, value) 231 | Set the filling factor for Open-Drain outputs 232 | 233 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 234 | 235 | ch - selected channel number [1..4] 236 | 237 | value - filling factor in precent [0..100] 238 | 239 | return none 240 | 241 | 242 | ### getOdPWM(stack, ch) 243 | Read the filling factor for Open-Drain outputs 244 | 245 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 246 | 247 | ch - selected channel number [1..4] 248 | 249 | return - filling factor in precent [0..100] 250 | 251 | 252 | ### setLed(stack, channel, val) 253 | Set one of the 4 LED's on the card. 254 | 255 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 256 | 257 | channel - selected LED number [1..4] 258 | 259 | val - 0 = OFF, 1 = ON 260 | 261 | 262 | ### setLedAll(stack, val) 263 | Set all 4 LED's on the card. 264 | 265 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 266 | 267 | val - bitmap of the 4 LED's [0..15] 268 | 269 | 270 | ### getLed(stack, channel) 271 | Get the state of one of the 4 LED's om the card. 272 | 273 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 274 | 275 | channel - selected LED number [1..4] 276 | 277 | return 0 if the LED is OFF, 1 if is ON 278 | 279 | ### getOptoFrequency(stack, channel) 280 | Get the frequency of the signal connected on one of the 4 optically isolated inputs. 281 | 282 | channel - selected LED number [1..4] 283 | 284 | return frequency expressed in Hz 285 | 286 | ## Watcdog Timer Functions 287 | 288 | ### wdtGetPeriod(stack) 289 | Return the current period of the watchdog timer in seconds 290 | 291 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 292 | 293 | 294 | ### wdtSetPeriod(stack, val) 295 | Set the period of the watchdog in seconds, val = 65000 disable the watchdog 296 | 297 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 298 | 299 | val - [10..65000] 300 | 301 | 302 | ### wdtReload(stack) 303 | Reload the watchdog timer with the current period. 304 | The next reload command must occur in no more the "period" time in order to prevent watchdog to re-power the Raspberry. 305 | This command also enables the watchdog if is disabled (power-up disabled). 306 | 307 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 308 | 309 | 310 | ### wdtSetDefaultPeriod(stack, val) 311 | This function updates the period that will be loaded after Raspberry power is turned off and back on. You must set this period long enough to let Raspberry boot-up and your "watchdog maintaining" script to start. 312 | 313 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 314 | 315 | value - [10...64999] seconds 316 | 317 | 318 | ### wdtGetDefaultPeriod(stack) 319 | Return the default period 320 | 321 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 322 | 323 | value - [10...64999] seconds 324 | 325 | 326 | ### wdtSetOffInterval(stack, val) 327 | Set the time interval in seconds for keeping Raspberry power off in case of watchdog timer expire. 328 | 329 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 330 | 331 | val - [10...4147200] seconds 332 | 333 | 334 | ### wdtGetOffInterval(stack) 335 | Return the Off time interval in seconds 336 | 337 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 338 | 339 | return - [10...4147200] seconds 340 | 341 | 342 | ### wdtGetResetCount(stack) 343 | Return the numbers of Raspberry re-powers performed by the watchdog 344 | 345 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 346 | 347 | return - [0..65535] 348 | 349 | ## RTC Functions 350 | 351 | ### rtcGet(stack) 352 | Return the RTC date and time as a list 353 | 354 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 355 | 356 | return (year, month, day, hour, minute, seconds) 357 | 358 | ### rtcSet(stack, y, mo, d, h, m, s) 359 | Set the RTC date and time 360 | 361 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 362 | 363 | y - year between 2000..2255 or between 0..255 364 | 365 | mo - month 1..12 366 | 367 | d - day 368 | 369 | h - hour 370 | 371 | m - minutes 372 | 373 | s - seconds 374 | 375 | 376 | ## Owire Bus Functions 377 | 378 | ### owbScan(stack) 379 | Start scanning for connected sensors 380 | 381 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 382 | 383 | ### owbGetSensorNo(stack) 384 | Get the numbers of 18B20 sensors connected on the bus 385 | 386 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 387 | 388 | return number of connected sensors 389 | 390 | ### owbGetTemp(stack, sensor) 391 | Read the temperature aquired by one sensor 392 | 393 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 394 | 395 | sensor - sensor number [1..16] 396 | 397 | return temperature in degree Celsius 398 | 399 | ### owbGetRomCode(stack, sensor) 400 | Read the unic ROM code of one sensor 401 | 402 | stack - stack level of the megaind card (selectable from address jumpers [0..7]) 403 | 404 | sensor - sensor number [1..16] 405 | 406 | return ROM code as 8 bytes array 407 | -------------------------------------------------------------------------------- /python/megaind/__init__.py: -------------------------------------------------------------------------------- 1 | import smbus2 2 | import struct 3 | import time 4 | 5 | __HW_ADD_BASE = 0x50 6 | VOLT_TO_MILIVOLT = 1000.0 7 | BUS_NO = 1 # change this in case of using different SBC, for example use 7 for ROCK 4 SE 8 | 9 | def checkStack(stack): 10 | if stack < 0 or stack > 7: 11 | raise ValueError('Invalid stack level!') 12 | return __HW_ADD_BASE + stack 13 | 14 | 15 | def checkChannel(ch, limit=4): 16 | if ch < 1 or ch > limit: 17 | raise ValueError('Invalid channel number!') 18 | 19 | def checkOwbSns(ch): 20 | if ch < 1 or ch > 16: 21 | raise ValueError('One Wire Bus sensor number out of range [1..16]') 22 | 23 | def getWord(bus, hwAdd, add): 24 | retry = 0 25 | try: 26 | buff = bus.read_i2c_block_data(hwAdd, add, 2) 27 | val = bytearray(buff)[0] + 256 * bytearray(buff)[1] 28 | except Exception as e: 29 | bus.close() 30 | raise Exception("Fail to read with exception " + str(e)) 31 | return val 32 | 33 | 34 | # Diagnose functions 35 | I2C_MEM_DIAG_TEMPERATURE = 114 36 | I2C_MEM_DIAG_24V = 115 37 | I2C_MEM_DIAG_5V = 117 38 | I2C_MEM_REVISION_MAJOR = 120 39 | I2C_MEM_REVISION_MINOR = 121 40 | 41 | 42 | def getFwVer(stack): 43 | hwAdd = checkStack(stack) 44 | bus = smbus2.SMBus(BUS_NO) 45 | try: 46 | major = bus.read_byte_data(hwAdd, I2C_MEM_REVISION_MAJOR) 47 | minor = bus.read_byte_data(hwAdd, I2C_MEM_REVISION_MINOR) 48 | except Exception as e: 49 | bus.close() 50 | raise Exception("Fail to read with exception " + str(e)) 51 | bus.close() 52 | return major + minor / 100.0 53 | 54 | 55 | def getRaspVolt(stack): 56 | hwAdd = checkStack(stack) 57 | bus = smbus2.SMBus(BUS_NO) 58 | try: 59 | val = bus.read_word_data(hwAdd, I2C_MEM_DIAG_5V) 60 | except Exception as e: 61 | bus.close() 62 | raise Exception("Fail to read with exception " + str(e)) 63 | bus.close() 64 | return val / VOLT_TO_MILIVOLT 65 | 66 | 67 | def getPowerVolt(stack): 68 | hwAdd = checkStack(stack) 69 | bus = smbus2.SMBus(BUS_NO) 70 | val = getWord(bus, hwAdd, I2C_MEM_DIAG_24V) 71 | bus.close() 72 | return val / VOLT_TO_MILIVOLT 73 | 74 | 75 | def getCpuTemp(stack): 76 | hwAdd = checkStack(stack) 77 | bus = smbus2.SMBus(BUS_NO) 78 | try: 79 | val = bus.read_byte_data(hwAdd, I2C_MEM_DIAG_TEMPERATURE) 80 | except Exception as e: 81 | bus.close() 82 | raise Exception("Fail to read with exception " + str(e)) 83 | bus.close() 84 | return val 85 | 86 | 87 | # 0 to 10 volts input and output functions 88 | U0_10_IN_VAL1_ADD = 28 89 | U_PM_10_IN_VAL1_ADD = 36 90 | U_0_10_OUT_VAL1_ADD = 4 91 | 92 | 93 | def get0_10In(stack, channel): 94 | checkChannel(channel) 95 | hwAdd = checkStack(stack) 96 | bus = smbus2.SMBus(BUS_NO) 97 | val = getWord(bus, hwAdd, U0_10_IN_VAL1_ADD + (2 * (channel - 1))) 98 | bus.close() 99 | return val / VOLT_TO_MILIVOLT 100 | 101 | 102 | def getpm10In(stack, channel): 103 | checkChannel(channel) 104 | hwAdd = checkStack(stack) 105 | bus = smbus2.SMBus(BUS_NO) 106 | val = getWord(bus, hwAdd, U_PM_10_IN_VAL1_ADD + (2 * (channel - 1))) 107 | bus.close() 108 | return val / VOLT_TO_MILIVOLT - 10 109 | 110 | 111 | def get0_10Out(stack, channel): 112 | checkChannel(channel) 113 | hwAdd = checkStack(stack) 114 | bus = smbus2.SMBus(BUS_NO) 115 | val = getWord(bus, hwAdd, U_0_10_OUT_VAL1_ADD + (2 * (channel - 1))) 116 | bus.close() 117 | return val / VOLT_TO_MILIVOLT 118 | 119 | 120 | def set0_10Out(stack, channel, value): 121 | checkChannel(channel) 122 | hwAdd = checkStack(stack) 123 | bus = smbus2.SMBus(BUS_NO) 124 | if value < 0 or value > 10: 125 | raise ValueError("Invalid value!") 126 | try: 127 | bus.write_word_data(hwAdd, U_0_10_OUT_VAL1_ADD + (2 * (channel - 1)), int(value * 1000)) 128 | except Exception as e: 129 | bus.close() 130 | raise Exception("Fail to Write 0-10V output with exception " + str(e)) 131 | bus.close() 132 | 133 | 134 | # 4 - 20 mA in/out functions 135 | I4_20_IN_VAL1_ADD = 44 136 | I4_20_OUT_VAL1_ADD = 12 137 | MILLIAMP_TO_MICROAMP = 1000.0 138 | 139 | 140 | def get4_20In(stack, channel): 141 | checkChannel(channel) 142 | hwAdd = checkStack(stack) 143 | bus = smbus2.SMBus(BUS_NO) 144 | val = getWord(bus, hwAdd, I4_20_IN_VAL1_ADD + (2 * (channel - 1))) 145 | bus.close() 146 | return val / 1000.0 147 | 148 | 149 | def get4_20Out(stack, channel): 150 | checkChannel(channel) 151 | hwAdd = checkStack(stack) 152 | bus = smbus2.SMBus(BUS_NO) 153 | val = getWord(bus, hwAdd, I4_20_OUT_VAL1_ADD + (2 * (channel - 1))) 154 | bus.close() 155 | return val / 1000.0 156 | 157 | 158 | def set4_20Out(stack, channel, value): 159 | checkChannel(channel) 160 | hwAdd = checkStack(stack) 161 | bus = smbus2.SMBus(BUS_NO) 162 | if value < 0 or value > 20: 163 | raise ValueError("Invalid value!") 164 | try: 165 | bus.write_word_data(hwAdd, I4_20_OUT_VAL1_ADD + (2 * (channel - 1)), int(value * 1000)) 166 | except Exception as e: 167 | bus.close() 168 | raise Exception("Fail to Write 4-20mA output with exception " + str(e)) 169 | bus.close() 170 | 171 | 172 | # digital in/out functions 173 | I2C_MEM_RELAY_VAL = 0 174 | I2C_MEM_RELAY_SET = 1 175 | I2C_MEM_RELAY_CLR = 2 176 | I2C_MEM_OPTO_IN_VAL = 3 177 | I2C_MEM_OD_PWM1 = 20 178 | I2C_MEM_OPTO_RISING_ENABLE = 103 179 | I2C_MEM_OPTO_FALLING_ENABLE = 104 180 | I2C_MEM_OPTO_CH_CONT_RESET = 105 181 | I2C_MEM_OPTO_COUNT1 = 106 182 | I2C_MEM_OPTO_FREQ1 = 159 183 | 184 | 185 | def getOptoCh(stack, channel): 186 | checkChannel(channel) 187 | hwAdd = checkStack(stack) 188 | bus = smbus2.SMBus(BUS_NO) 189 | try: 190 | val = bus.read_byte_data(hwAdd, I2C_MEM_OPTO_IN_VAL) 191 | except Exception as e: 192 | bus.close() 193 | raise Exception("Fail to read with exception " + str(e)) 194 | bus.close() 195 | if (1 << (channel - 1)) & val: 196 | return 1 197 | return 0 198 | 199 | 200 | def getOpto(stack): 201 | hwAdd = checkStack(stack) 202 | bus = smbus2.SMBus(BUS_NO) 203 | try: 204 | val = bus.read_byte_data(hwAdd, I2C_MEM_OPTO_IN_VAL) 205 | except Exception as e: 206 | bus.close() 207 | raise Exception("Fail to read with exception " + str(e)) 208 | bus.close() 209 | return val 210 | 211 | 212 | def getOptoCount(stack, channel): 213 | checkChannel(channel) 214 | hwAdd = checkStack(stack) 215 | bus = smbus2.SMBus(BUS_NO) 216 | try: 217 | val = bus.read_word_data(hwAdd, I2C_MEM_OPTO_COUNT1 + (2 * (channel - 1))) 218 | except Exception as e: 219 | bus.close() 220 | raise Exception("Fail to read with exception " + str(e)) 221 | bus.close() 222 | return val 223 | 224 | 225 | def rstOptoCount(stack, channel): 226 | checkChannel(channel) 227 | hwAdd = checkStack(stack) 228 | bus = smbus2.SMBus(BUS_NO) 229 | try: 230 | bus.write_byte_data(hwAdd, I2C_MEM_OPTO_CH_CONT_RESET, int(channel)) 231 | except Exception as e: 232 | bus.close() 233 | raise Exception("Fail to write with exception " + str(e)) 234 | bus.close() 235 | 236 | 237 | def getOptoRisingCountEnable(stack, channel): 238 | checkChannel(channel) 239 | hwAdd = checkStack(stack) 240 | bus = smbus2.SMBus(BUS_NO) 241 | try: 242 | val = bus.read_byte_data(hwAdd, I2C_MEM_OPTO_RISING_ENABLE) 243 | except Exception as e: 244 | bus.close() 245 | raise Exception("Fail to read with exception " + str(e)) 246 | bus.close() 247 | if (1 << (channel - 1)) & val != 0: 248 | return 1 249 | return 0 250 | 251 | 252 | def setOptoRisingCountEnable(stack, channel, state): 253 | checkChannel(channel) 254 | hwAdd = checkStack(stack) 255 | bus = smbus2.SMBus(BUS_NO) 256 | try: 257 | val = bus.read_byte_data(hwAdd, I2C_MEM_OPTO_RISING_ENABLE) 258 | except Exception as e: 259 | bus.close() 260 | raise Exception("Fail to read with exception " + str(e)) 261 | if state == 0: 262 | val &= ~(1 << (channel - 1)) 263 | else: 264 | val |= 1 << (channel - 1) 265 | try: 266 | bus.write_byte_data(hwAdd, I2C_MEM_OPTO_RISING_ENABLE, val) 267 | except Exception as e: 268 | bus.close() 269 | raise Exception("Fail to write with exception " + str(e)) 270 | bus.close() 271 | 272 | 273 | def getOptoFallingCountEnable(stack, channel): 274 | checkChannel(channel) 275 | hwAdd = checkStack(stack) 276 | bus = smbus2.SMBus(BUS_NO) 277 | try: 278 | val = bus.read_byte_data(hwAdd, I2C_MEM_OPTO_FALLING_ENABLE) 279 | except Exception as e: 280 | bus.close() 281 | raise Exception("Fail to read with exception " + str(e)) 282 | bus.close() 283 | if (1 << (channel - 1)) & val != 0: 284 | return 1 285 | return 0 286 | 287 | 288 | def setOptoFallingCountEnable(stack, channel, state): 289 | checkChannel(channel) 290 | hwAdd = checkStack(stack) 291 | bus = smbus2.SMBus(BUS_NO) 292 | try: 293 | val = bus.read_byte_data(hwAdd, I2C_MEM_OPTO_FALLING_ENABLE) 294 | except Exception as e: 295 | bus.close() 296 | raise Exception("Fail to read with exception " + str(e)) 297 | if state == 0: 298 | val &= ~(1 << (channel - 1)) 299 | else: 300 | val |= 1 << (channel - 1) 301 | try: 302 | bus.write_byte_data(hwAdd, I2C_MEM_OPTO_FALLING_ENABLE, val) 303 | except Exception as e: 304 | bus.close() 305 | raise Exception("Fail to write with exception " + str(e)) 306 | bus.close() 307 | 308 | 309 | def setOdPWM(stack, channel, value): 310 | checkChannel(channel) 311 | hwAdd = checkStack(stack) 312 | bus = smbus2.SMBus(BUS_NO) 313 | if value < 0 or value > 100: # prcent 314 | raise ValueError("Invalid value!") 315 | try: 316 | bus.write_word_data(hwAdd, I2C_MEM_OD_PWM1 + (2 * (channel - 1)), int(value * 100)) 317 | except Exception as e: 318 | bus.close() 319 | raise Exception("Fail to Write Open-Drain output PWM with exception " + str(e)) 320 | bus.close() 321 | 322 | 323 | def getOdPWM(stack, channel): 324 | checkChannel(channel) 325 | hwAdd = checkStack(stack) 326 | bus = smbus2.SMBus(BUS_NO) 327 | val = getWord(bus, hwAdd, I2C_MEM_OD_PWM1 + (2 * (channel - 1))) 328 | bus.close() 329 | return val / 100.0 330 | 331 | 332 | def setOd(stack, channel, val): 333 | checkChannel(channel) 334 | hwAdd = checkStack(stack) 335 | bus = smbus2.SMBus(BUS_NO) 336 | try: 337 | if val != 0: 338 | bus.write_byte_data(hwAdd, I2C_MEM_RELAY_SET, channel) 339 | else: 340 | bus.write_byte_data(hwAdd, I2C_MEM_RELAY_CLR, channel) 341 | except Exception as e: 342 | bus.close() 343 | raise Exception("Fail to Write OD with exception " + str(e)) 344 | bus.close() 345 | 346 | 347 | def getOd(stack, channel): 348 | checkChannel(channel) 349 | hwAdd = checkStack(stack) 350 | bus = smbus2.SMBus(BUS_NO) 351 | mask = 1 << (channel - 1) 352 | try: 353 | val = bus.read_byte_data(hwAdd, I2C_MEM_RELAY_VAL) 354 | except Exception as e: 355 | bus.close() 356 | raise Exception("Fail to Get OD's with exception " + str(e)) 357 | bus.close() 358 | if val & mask: 359 | return 1 360 | return 0 361 | 362 | 363 | def setLed(stack, channel, val): 364 | checkChannel(channel) 365 | hwAdd = checkStack(stack) 366 | bus = smbus2.SMBus(BUS_NO) 367 | out = channel + 4 368 | try: 369 | if val != 0: 370 | bus.write_byte_data(hwAdd, I2C_MEM_RELAY_SET, out) 371 | else: 372 | bus.write_byte_data(hwAdd, I2C_MEM_RELAY_CLR, out) 373 | except Exception as e: 374 | bus.close() 375 | raise Exception("Fail to Write LED's with exception " + str(e)) 376 | bus.close() 377 | 378 | 379 | def setLedAll(stack, val): 380 | if val < 0 or val > 15: 381 | raise ValueError("Invalid value!") 382 | val = val << 4 383 | hwAdd = checkStack(stack) 384 | bus = smbus2.SMBus(BUS_NO) 385 | try: 386 | bus.write_byte_data(hwAdd, I2C_MEM_RELAY_VAL, val) 387 | except Exception as e: 388 | bus.close() 389 | raise Exception("Fail to Write LED's with exception " + str(e)) 390 | bus.close() 391 | 392 | 393 | def getLed(stack, channel): 394 | checkChannel(channel) 395 | hwAdd = checkStack(stack) 396 | bus = smbus2.SMBus(BUS_NO) 397 | mask = 1 << (channel + 3) 398 | try: 399 | val = bus.read_byte_data(hwAdd, I2C_MEM_RELAY_VAL) 400 | except Exception as e: 401 | bus.close() 402 | raise Exception("Fail to Write LED's with exception " + str(e)) 403 | bus.close() 404 | if val & mask: 405 | return 1 406 | return 0 407 | 408 | def getOptoFrequency(stack, channel): 409 | checkChannel(channel) 410 | hwAdd = checkStack(stack) 411 | bus = smbus2.SMBus(BUS_NO) 412 | val = getWord(bus, hwAdd, I2C_MEM_OPTO_FREQ1 + (2 * (channel - 1))) 413 | bus.close() 414 | return val 415 | 416 | 417 | # watchdog functions 418 | I2C_MEM_WDT_RESET_ADD = 83 419 | I2C_MEM_WDT_INTERVAL_SET_ADD = 84 420 | I2C_MEM_WDT_INTERVAL_GET_ADD = I2C_MEM_WDT_INTERVAL_SET_ADD + 2 421 | I2C_MEM_WDT_INIT_INTERVAL_SET_ADD = I2C_MEM_WDT_INTERVAL_GET_ADD + 2 422 | I2C_MEM_WDT_INIT_INTERVAL_GET_ADD = I2C_MEM_WDT_INIT_INTERVAL_SET_ADD + 2 423 | I2C_MEM_WDT_RESET_COUNT_ADD = I2C_MEM_WDT_INIT_INTERVAL_GET_ADD + 2 424 | I2C_MEM_WDT_CLEAR_RESET_COUNT_ADD = I2C_MEM_WDT_RESET_COUNT_ADD + 2 425 | I2C_MEM_WDT_POWER_OFF_INTERVAL_SET_ADD = I2C_MEM_WDT_CLEAR_RESET_COUNT_ADD + 1 426 | I2C_MEM_WDT_POWER_OFF_INTERVAL_GET_ADD = I2C_MEM_WDT_POWER_OFF_INTERVAL_SET_ADD + 4 427 | WDT_MAX_POWER_OFF_INTERVAL = 4147200 428 | RELOAD_KEY = 202 429 | 430 | 431 | def wdtGetPeriod(stack): 432 | hwAdd = checkStack(stack) 433 | bus = smbus2.SMBus(BUS_NO) 434 | try: 435 | val = bus.read_word_data(hwAdd, I2C_MEM_WDT_INTERVAL_GET_ADD) 436 | except Exception as e: 437 | bus.close() 438 | raise ValueError(e) 439 | bus.close() 440 | return val 441 | 442 | 443 | def wdtSetPeriod(stack, val): 444 | ret = 1 445 | hwAdd = checkStack(stack) 446 | if val < 10 or val > 65000: 447 | raise ValueError('Invalid interval value [10..65000]') 448 | bus = smbus2.SMBus(BUS_NO) 449 | try: 450 | bus.write_word_data(hwAdd, I2C_MEM_WDT_INTERVAL_SET_ADD, val) 451 | except Exception as e: 452 | bus.close() 453 | raise ValueError(e) 454 | bus.close() 455 | return ret 456 | 457 | 458 | def wdtReload(stack): 459 | ret = 1 460 | hwAdd = checkStack(stack) 461 | bus = smbus2.SMBus(BUS_NO) 462 | try: 463 | bus.write_byte_data(hwAdd, I2C_MEM_WDT_RESET_ADD, RELOAD_KEY) 464 | except Exception as e: 465 | bus.close() 466 | raise ValueError(e) 467 | bus.close() 468 | return ret 469 | 470 | 471 | def wdtSetDefaultPeriod(stack, val): 472 | ret = 1 473 | hwAdd = checkStack(stack) 474 | if val < 10 or val > 64999: 475 | raise ValueError('Invalid interval value [10..64999]') 476 | bus = smbus2.SMBus(BUS_NO) 477 | try: 478 | bus.write_word_data(hwAdd, I2C_MEM_WDT_INIT_INTERVAL_SET_ADD, val) 479 | except Exception as e: 480 | bus.close() 481 | raise ValueError(e) 482 | bus.close() 483 | return ret 484 | 485 | 486 | def wdtGetDefaultPeriod(stack): 487 | hwAdd = checkStack(stack) 488 | bus = smbus2.SMBus(BUS_NO) 489 | try: 490 | val = bus.read_word_data(hwAdd, I2C_MEM_WDT_INIT_INTERVAL_GET_ADD) 491 | except Exception as e: 492 | bus.close() 493 | raise ValueError(e) 494 | bus.close() 495 | return val 496 | 497 | 498 | def wdtSetOffInterval(stack, val): 499 | ret = 1 500 | hwAdd = checkStack(stack) 501 | if 10 > val or val > WDT_MAX_POWER_OFF_INTERVAL: 502 | raise ValueError('Invalid interval value [2..4147200]') 503 | bus = smbus2.SMBus(BUS_NO) 504 | buff = [0, 0, 0, 0] 505 | buff[0] = 0xff & val 506 | buff[1] = 0xff & (val >> 8) 507 | buff[2] = 0xff & (val >> 16) 508 | buff[3] = 0xff & (val >> 24) 509 | try: 510 | bus.write_i2c_block_data(hwAdd, I2C_MEM_WDT_POWER_OFF_INTERVAL_SET_ADD, buff) 511 | except Exception as e: 512 | bus.close() 513 | raise ValueError(e) 514 | bus.close() 515 | return ret 516 | 517 | 518 | def wdtGetOffInterval(stack): 519 | hwAdd = checkStack(stack) 520 | bus = smbus2.SMBus(BUS_NO) 521 | try: 522 | buff = bus.read_i2c_block_data(hwAdd, I2C_MEM_WDT_POWER_OFF_INTERVAL_GET_ADD, 4) 523 | val = buff[0] + (buff[1] << 8) + (buff[2] << 16) + (buff[3] << 24) 524 | except Exception as e: 525 | bus.close() 526 | raise ValueError(e) 527 | bus.close() 528 | return val 529 | 530 | 531 | def wdtGetResetCount(stack): 532 | hwAdd = checkStack(stack) 533 | bus = smbus2.SMBus(BUS_NO) 534 | try: 535 | val = bus.read_word_data(hwAdd, I2C_MEM_WDT_RESET_COUNT_ADD) 536 | except Exception as e: 537 | bus.close() 538 | raise ValueError(e) 539 | bus.close() 540 | return val 541 | 542 | 543 | I2C_RTC_YEAR_ADD = 70 544 | I2C_RTC_MONTH_ADD = 71 545 | I2C_RTC_DAY_ADD = 72 546 | I2C_RTC_HOUR_ADD = 73 547 | I2C_RTC_MINUTE_ADD = 74 548 | I2C_RTC_SECOND_ADD = 75 549 | I2C_RTC_SET_YEAR_ADD = 76 550 | I2C_RTC_SET_MONTH_ADD = 77 551 | I2C_RTC_SET_DAY_ADD = 78 552 | I2C_RTC_SET_HOUR_ADD = 79 553 | I2C_RTC_SET_MINUTE_ADD = 80 554 | I2C_RTC_SET_SECOND_ADD = 81 555 | I2C_RTC_CMD_ADD = 82 556 | 557 | 558 | def rtcGet(stack): 559 | hwAdd = checkStack(stack) 560 | bus = smbus2.SMBus(BUS_NO) 561 | try: 562 | buff = bus.read_i2c_block_data(hwAdd, I2C_RTC_YEAR_ADD, 6) 563 | except Exception as e: 564 | bus.close() 565 | raise ValueError(e) 566 | bus.close() 567 | t = (2000 + buff[0], buff[1], buff[2], buff[3], buff[4], buff[5]) 568 | return t 569 | 570 | 571 | def rtcSet(stack, y, mo, d, h, m, s): 572 | if y > 2000: 573 | y -= 2000 574 | if y < 0 or y > 255: 575 | raise ValueError("Invalid year!") 576 | if mo > 12 or mo < 1: 577 | raise ValueError("Invalid month!") 578 | if d < 1 or d > 31: 579 | raise ValueError("Invalid day!") 580 | if h < 0 or h > 23: 581 | raise ValueError("Invalid hour!") 582 | if m < 0 or m > 59: 583 | raise ValueError("Invalid minute!") 584 | if s < 0 or s > 59: 585 | raise ValueError("Invalid seconds!") 586 | hwAdd = checkStack(stack) 587 | bus = smbus2.SMBus(BUS_NO) 588 | buff = [int(y), int(mo), int(d), int(h), int(m), int(s), 0xaa] 589 | # buff[0] = int(y) 590 | # buff[1] = int(mo) 591 | # buff[2] = int(d) 592 | # buff[3] = int(h) 593 | # buff[4] = int(m) 594 | # buff[5] = int(s) 595 | # buff[6] = 0xaa 596 | try: 597 | bus.write_i2c_block_data(hwAdd, I2C_RTC_SET_YEAR_ADD, buff) 598 | except Exception as e: 599 | bus.close() 600 | raise ValueError(e) 601 | bus.close() 602 | 603 | 604 | I2C_MEM_1WB_DEV = 147 605 | I2C_MEM_1WB_TEMP_ALL = 148 606 | I2C_MEM_1WB_START_SEARCH = 173 607 | I2C_MEM_1WB_T1 = 174 608 | I2C_MEM_1WB_ROM_CODE_IDX = 150 609 | I2C_MEM_1WB_ROM_CODE = 151 610 | OWB_TEMP_SIZE_B = 2 611 | OWM_ROM_CODE_SIZE_B = 8 612 | 613 | def owbScan(stack): 614 | hwAdd = checkStack(stack) 615 | bus = smbus2.SMBus(BUS_NO) 616 | buf = 0xaa 617 | try: 618 | bus.write_byte_data(hwAdd, I2C_MEM_1WB_START_SEARCH, buf) 619 | except Exception as e: 620 | bus.close() 621 | raise Exception("Fail to write with exception " + str(e)) 622 | bus.close() 623 | 624 | 625 | def owbGetSensorNo(stack): 626 | hwAdd = checkStack(stack) 627 | bus = smbus2.SMBus(BUS_NO) 628 | try: 629 | no = bus.read_byte_data(hwAdd, I2C_MEM_1WB_DEV) 630 | except Exception as e: 631 | bus.close() 632 | raise Exception("Fail to read with exception " + str(e)) 633 | bus.close() 634 | return no 635 | 636 | 637 | def owbGetTemp(stack, sensor): 638 | checkOwbSns(sensor) 639 | hwAdd = checkStack(stack) 640 | bus = smbus2.SMBus(BUS_NO) 641 | try: 642 | temp = bus.read_word_data(hwAdd, I2C_MEM_1WB_T1 + OWB_TEMP_SIZE_B * (sensor - 1)) 643 | except Exception as e: 644 | bus.close() 645 | raise Exception("Fail to read with exception " + str(e)) 646 | bus.close() 647 | return temp / 100.0 648 | 649 | 650 | def owbGetRomCode(stack, sensor): 651 | checkOwbSns(sensor) 652 | hwAdd = checkStack(stack) 653 | bus = smbus2.SMBus(BUS_NO) 654 | try: 655 | bus.write_byte_data(hwAdd, I2C_MEM_1WB_ROM_CODE_IDX, sensor - 1) # Select the sensor 656 | buff = bus.read_i2c_block_data(hwAdd, I2C_MEM_1WB_ROM_CODE, 8) 657 | except Exception as e: 658 | bus.close() 659 | raise Exception("Fail to read with exception " + str(e)) 660 | bus.close() 661 | return buff 662 | -------------------------------------------------------------------------------- /python/setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal=1 3 | -------------------------------------------------------------------------------- /python/setup.py: -------------------------------------------------------------------------------- 1 | #python2 setup.py sdist bdist_wheel 2 | 3 | # For testing 4 | #twine upload --repostitory testpypi dist/* 5 | #pip install --index-url https://test.pypi.org/simple/ --no-deps multiio 6 | 7 | # For release 8 | #twine upload dist/* 9 | #pip install multiio 10 | 11 | 12 | with open("README.md", 'r') as f: 13 | long_description = f.read() 14 | 15 | from setuptools import setup, find_packages 16 | setup( 17 | name='smmegaind', 18 | packages=find_packages(), 19 | version='1.0.4', 20 | license='MIT', 21 | description='Library to control PLACEHOLDER Automation Card', 22 | long_description=long_description, 23 | long_description_content_type="text/markdown", 24 | author='Sequent Microsystems', 25 | author_email='olcitu@gmail.com', 26 | url='https://sequentmicrosystems.com', 27 | #keywords=['industrial', 'raspberry', 'power', '4-20mA', '0-10V', 'optoisolated'], 28 | install_requires=[ 29 | "smbus2", 30 | ], 31 | classifiers=[ 32 | 'Development Status :: 4 - Beta', 33 | # Chose either "3 - Alpha", "4 - Beta" or "5 - Production/Stable" as the current state of your package 34 | 'Intended Audience :: Developers', 35 | 'Topic :: Software Development :: Build Tools', 36 | 'License :: OSI Approved :: MIT License', 37 | 'Programming Language :: Python :: 2.7', 38 | 'Programming Language :: Python :: 3', 39 | 'Programming Language :: Python :: 3.4', 40 | 'Programming Language :: Python :: 3.5', 41 | 'Programming Language :: Python :: 3.6', 42 | 'Programming Language :: Python :: 3.7', 43 | ], 44 | ) 45 | -------------------------------------------------------------------------------- /python/tests/flash_leds.py: -------------------------------------------------------------------------------- 1 | import megaind as ind 2 | import time 3 | 4 | 5 | def flash_seq(n, delay=0.1, stack=0): 6 | for i in range(n): 7 | for j in range(4): 8 | ind.setLed(stack, j + 1, 1) 9 | time.sleep(delay) 10 | for j in range(4): 11 | ind.setLed(stack, j + 1, 0) 12 | time.sleep(delay) 13 | 14 | 15 | def flash(n, delay=0.2, stack=0): 16 | for i in range(n): 17 | ind.setLedAll(stack, 15) 18 | time.sleep(delay) 19 | ind.setLedAll(stack, 0) 20 | time.sleep(delay) 21 | 22 | 23 | if __name__ == "__main__": 24 | flash_seq(10) 25 | flash(10) 26 | -------------------------------------------------------------------------------- /python/tests/test_amp.py: -------------------------------------------------------------------------------- 1 | import megaind as ind 2 | import sys 3 | 4 | 5 | def test_get4_20In(v = False): 6 | for i in range(1, 5): 7 | val = ind.get4_20In(0, i) 8 | if v: 9 | print('4 - 20mA input channel ' + str(i) + ' read ' + str(val)) 10 | assert val >= 0, "Should be greater than or equal to 0" #disconnected channel 11 | assert val <= 20, "Should be less than or equal to 20" 12 | 13 | 14 | def test_set4_20Out(v = False): 15 | for i in range(1, 5): 16 | ind.set4_20Out(0, i, 4) 17 | if v: 18 | print('4 - 20mA output channel '+ str(i) + ' set to 4mA') 19 | 20 | 21 | def test_get4_20Out(v = False): 22 | for i in range(1, 5): 23 | ind.set4_20Out(0, i, 4) 24 | val = ind.get4_20Out(0, i) 25 | if v: 26 | print('4 - 20mA output channel ' + str(i) + ' read ' + str(val)) 27 | assert val == 4, "Should be 4" 28 | 29 | 30 | if __name__ == "__main__": 31 | verb = False 32 | if len(sys.argv) > 1: 33 | if sys.argv[1] == '-v': 34 | verb = True 35 | test_get4_20In(verb) 36 | print("get4_20In() passed") 37 | test_set4_20Out(verb) 38 | print("set4_20Out() passed") 39 | test_get4_20Out(verb) 40 | print("get4_20Out() passed") 41 | -------------------------------------------------------------------------------- /python/tests/test_digital.py: -------------------------------------------------------------------------------- 1 | import megaind as ind 2 | import sys 3 | 4 | 5 | def test_getOptoCh(v): 6 | for i in range (1, 5): 7 | val = ind.getOptoCh(0, i) 8 | if v : 9 | print ('Opto cupled input channel ' + str(i) + " read value " + str(val)) 10 | assert val >= 0, "Should be greater than or equal to 0" 11 | assert val <= 1, "Should be less than or equal to 1" 12 | 13 | 14 | def test_getOpto(v): 15 | val = ind.getOpto(0) 16 | if v : 17 | print("Opto input read " + str(val)) 18 | assert val >= 0, "Should be greater than or equal to 0" 19 | assert val <= 15, "Should be less than or equal to 15" 20 | 21 | 22 | def test_getOptoCount(v): 23 | for i in range (1, 5): 24 | val = ind.getOptoCount(0, i) 25 | if v : 26 | print ('Opto cupled input contor channel ' + str(i) + " read value " + str(val)) 27 | assert val >= 0, "Should be greater than or equal to 0" 28 | 29 | 30 | def test_rstOptoCount(v): 31 | for i in range (1, 5): 32 | ind.rstOptoCount(0, i); 33 | val = ind.getOptoCount(0, i) 34 | if v : 35 | print ('Opto cupled input contor channel ' + str(i) + " read value " + str(val)) 36 | assert val == 0, "Should be equal to 0" 37 | 38 | 39 | def test_getOptoRisingCountEnable(v): 40 | for i in range (1, 5): 41 | val = ind.getOptoRisingCountEnable(0, i) 42 | if v : 43 | print ('Opto cupled input rising edge enable channel ' + str(i) + " read value " + str(val)) 44 | assert val >= 0, "Should be greater than or equal to 0" 45 | 46 | 47 | if __name__ == "__main__": 48 | verb = False 49 | if len(sys.argv) > 1: 50 | if sys.argv[1] == '-v': 51 | verb = True 52 | test_getOptoCh(verb) 53 | print("getOptoCh() passed") 54 | test_getOpto(verb) 55 | print("getOpto() passed") 56 | test_getOptoCount(verb) 57 | print ("getOptoCount() passed") 58 | test_rstOptoCount(verb) 59 | print ("rstOptoCount() passed") -------------------------------------------------------------------------------- /python/tests/test_volt.py: -------------------------------------------------------------------------------- 1 | import megaind as ind 2 | import sys 3 | 4 | 5 | def test_get0_10In(verb = False): 6 | for i in range(1, 5): 7 | val = ind.get0_10In(0, i) 8 | if verb : 9 | print('0 - 10V input channel '+ str(i) + ' read '+ str(val)) 10 | assert val >= 0, "Should be greater than or equal to 0" 11 | assert val <= 10, "Should be less than or equal to 10" 12 | 13 | 14 | def test_getpm10In(verb = False): 15 | for i in range(1, 5): 16 | val = ind.getpm10In(0, i) 17 | if verb : 18 | print('+/-10V input channel '+ str(i) + ' read '+ str(val)) 19 | assert val >= -10 20 | 21 | 22 | def test_set0_10Out(verb = False): 23 | for i in range(1, 5): 24 | ind.set0_10Out(0, i, 0) 25 | if verb : 26 | print('+/-10V output channel '+ str(i) + ' write 0.0') 27 | 28 | 29 | def test_get0_10Out(verb = False): 30 | for i in range(1, 5): 31 | ind.set0_10Out(0, i, 0) 32 | val = ind.get0_10Out(0, i) 33 | if verb : 34 | print('0 - 10V output channel '+ str(i) + ' read '+ str(val)) 35 | assert val == 0, "Should be 0" 36 | 37 | 38 | if __name__ == "__main__": 39 | verb = False 40 | if len(sys.argv) > 1: 41 | if sys.argv[1] == '-v': 42 | verb = True 43 | test_get0_10In(verb) 44 | print("get0_10In() passed") 45 | test_getpm10In(verb) 46 | print("getpm10In() passed") 47 | test_set0_10Out(verb) 48 | print("set0_10Out() passed") 49 | test_get0_10Out(verb) 50 | print("get0_10Out() passed") -------------------------------------------------------------------------------- /res/IND.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/megaind-rpi/652448333b5c9d04be1ffd88889b6e10751076aa/res/IND.jpg -------------------------------------------------------------------------------- /res/industrial.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/megaind-rpi/652448333b5c9d04be1ffd88889b6e10751076aa/res/industrial.jpg -------------------------------------------------------------------------------- /res/sequent.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/megaind-rpi/652448333b5c9d04be1ffd88889b6e10751076aa/res/sequent.jpg -------------------------------------------------------------------------------- /rtc/README.md: -------------------------------------------------------------------------------- 1 | # set_time.sh 2 | 3 | This is a script created for the Industrial Automation Card RTC usage. 4 | The script checks if the raspberry clock is synchronized with the network time, if yes it set the card RTC, if not reads the RTC and set the system clock with it. 5 | 6 | #### Usage: 7 | ```bash 8 | $ sudo /home/pi/megaind-rpi/rtc/set_time.sh 9 | ``` 10 | 11 | The actual PATH might be different depending on your megaind-rpi PATH 12 | 13 | The script can be run manually to check and fix the system time or can be run from cron at a few minutes intervals. 14 | -------------------------------------------------------------------------------- /rtc/set_time.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | STR=$(timedatectl status) 3 | SUB="synchronized: yes" 4 | case $STR in 5 | 6 | *"$SUB"*) 7 | echo "Network time present set the RTC" 8 | string=$(date +%y%m%d%T) 9 | year=${string:0:2} 10 | month=${string:2:2} 11 | day=${string:4:2} 12 | hour=${string:6:2} 13 | min=${string:9:2} 14 | sec=${string:12:2} 15 | echo $(/usr/local/bin/megaind 0 rtcwr $month $day $year $hour $min $sec) 16 | exit 17 | ;; 18 | esac 19 | echo "Network time not present use the RTC" 20 | t=$(/usr/local/bin/megaind 0 rtcrd) 21 | sudo date --set "$t" 22 | -------------------------------------------------------------------------------- /src/analog.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "megaind.h" 8 | #include "comm.h" 9 | #include "thread.h" 10 | 11 | int val16Get(int dev, int baseAdd, int ch, float scale, float *val) 12 | { 13 | u8 buff[2] = {0, 0}; 14 | s16 raw = 0; 15 | u8 add = 0; 16 | 17 | if (ch < CHANNEL_NR_MIN) 18 | { 19 | return ERROR; 20 | } 21 | 22 | if ( (baseAdd + 2 * (ch - 1)) >= I2C_MEM_SIZE) 23 | { 24 | return ERROR; 25 | } 26 | add = baseAdd + 2 * (ch - 1); 27 | if (OK != i2cMem8Read(dev, add, buff, 2)) 28 | { 29 | printf("Fail to read!\n"); 30 | return ERROR; 31 | } 32 | memcpy(&raw, buff, 2); 33 | *val = (float)raw / scale; 34 | return OK; 35 | } 36 | 37 | int val16Set(int dev, int baseAdd, int ch, float scale, float val) 38 | { 39 | u8 buff[2] = {0, 0}; 40 | //u16 raw = 0; 41 | s16 raw = 0; 42 | u8 add = 0; 43 | 44 | if (ch < CHANNEL_NR_MIN) 45 | { 46 | return ERROR; 47 | } 48 | 49 | if ( (baseAdd + 2 * (ch - 1)) >= I2C_MEM_SIZE) 50 | { 51 | return ERROR; 52 | } 53 | add = baseAdd + 2 * (ch - 1); 54 | raw = (s16)ceil(val * scale); //transform to milivolts 55 | memcpy(buff, &raw, 2); 56 | if (OK != i2cMem8Write(dev, add, buff, 2)) 57 | { 58 | printf("Fail to write!\n"); 59 | return ERROR; 60 | } 61 | return OK; 62 | } 63 | 64 | int setUinType(int dev, u8 ch, u8 val) 65 | { 66 | u8 buf = 0; 67 | u8 needWrite = 0; 68 | 69 | if (OK != i2cMem8Read(dev, I2C_MEM_PM_IN_SW, &buf, 1)) 70 | { 71 | printf("Fail to check input type\n"); 72 | return ERROR; 73 | } 74 | if (val == 0) 75 | { 76 | if ((buf & (1 << (ch - 1))) != 0) 77 | { 78 | buf &= ~ (1 << (ch - 1)); 79 | needWrite = 1; 80 | } 81 | } 82 | else 83 | { 84 | if ((buf & (1 << (ch - 1))) == 0) 85 | { 86 | buf |= 1 << (ch - 1); 87 | needWrite = 1; 88 | } 89 | } 90 | if (needWrite) 91 | { 92 | if (OK != i2cMem8Write(dev, I2C_MEM_PM_IN_SW, &buf, 1)) 93 | { 94 | printf("Fail to set input type!\n"); 95 | return ERROR; 96 | } 97 | } 98 | return OK; 99 | 100 | } 101 | 102 | int doUOutRead(int argc, char *argv[]); 103 | const CliCmdType CMD_UOUT_READ = 104 | {"uoutrd", 2, &doUOutRead, 105 | "\tuoutrd: Read 0-10V Output voltage value(V) \n", 106 | "\tUsage: megaind uoutrd \n", "", 107 | "\tExample: megaind 0 uoutrd 2; Read the voltage on 0-10V out channel #2 on Board #0\n"}; 108 | 109 | int doUOutRead(int argc, char *argv[]) 110 | { 111 | int ch = 0; 112 | float val = 0; 113 | int dev = 0; 114 | 115 | dev = doBoardInit(atoi(argv[1])); 116 | if (dev <= 0) 117 | { 118 | return ERROR; 119 | } 120 | 121 | if (argc == 4) 122 | { 123 | ch = atoi(argv[3]); 124 | if ( (ch < CHANNEL_NR_MIN) || (ch > U_OUT_CH_NR_MAX)) 125 | { 126 | printf("0-10V Output channel out of range!\n"); 127 | return ERROR; 128 | } 129 | if (OK 130 | != val16Get(dev, I2C_MEM_U0_10_OUT_VAL1, ch, VOLT_TO_MILIVOLT, &val)) 131 | { 132 | return ERROR; 133 | } 134 | printf("%0.3f\n", val); 135 | } 136 | else 137 | { 138 | return ARG_CNT_ERR; 139 | } 140 | return OK; 141 | } 142 | 143 | int doIOutRead(int argc, char *argv[]); 144 | const CliCmdType CMD_IOUT_READ = 145 | {"ioutrd", 2, &doIOutRead, 146 | "\tioutrd: Read 4-20mA Output current value (mA) \n", 147 | "\tUsage: megaind ioutrd \n", "", 148 | "\tExample: megaind 0 ioutrd 2; Read the current on 4-20mA out channel #2 on Board #0\n"}; 149 | 150 | int doIOutRead(int argc, char *argv[]) 151 | { 152 | int ch = 0; 153 | float val = 0; 154 | int dev = 0; 155 | 156 | dev = doBoardInit(atoi(argv[1])); 157 | if (dev <= 0) 158 | { 159 | return ERROR; 160 | } 161 | 162 | if (argc == 4) 163 | { 164 | ch = atoi(argv[3]); 165 | if ( (ch < CHANNEL_NR_MIN) || (ch > I_OUT_CH_NR_MAX)) 166 | { 167 | printf("4-20mA Output channel out of range!\n"); 168 | return ERROR; 169 | } 170 | if (OK 171 | != val16Get(dev, I2C_MEM_I4_20_OUT_VAL1, ch, VOLT_TO_MILIVOLT, &val)) 172 | { 173 | return ERROR; 174 | } 175 | printf("%0.3f\n", val); 176 | } 177 | else 178 | { 179 | return ARG_CNT_ERR; 180 | } 181 | return OK; 182 | } 183 | 184 | int doODRead(int argc, char *argv[]); 185 | const CliCmdType CMD_OD_READ = 186 | {"odrd", 2, &doODRead, 187 | "\todrd: Read open-drain Output PWM value(0..100%) \n", 188 | "\tUsage: megaind odrd \n", "", 189 | "\tExample: megaind 0 odrd 2; Read the PWM value on open-drain output channel #2 on Board #0\n"}; 190 | 191 | int doODRead(int argc, char *argv[]) 192 | { 193 | int ch = 0; 194 | float val = 0; 195 | int dev = 0; 196 | 197 | dev = doBoardInit(atoi(argv[1])); 198 | if (dev <= 0) 199 | { 200 | return ERROR; 201 | } 202 | 203 | if (argc == 4) 204 | { 205 | ch = atoi(argv[3]); 206 | if ( (ch < CHANNEL_NR_MIN) || (ch > OD_CH_NR_MAX)) 207 | { 208 | printf("Open-drain Output channel out of range!\n"); 209 | return ERROR; 210 | } 211 | if (OK != val16Get(dev, I2C_MEM_OD_PWM1, ch, 100, &val)) 212 | { 213 | return ERROR; 214 | } 215 | printf("%0.3f\n", val); 216 | } 217 | else 218 | { 219 | return ARG_CNT_ERR; 220 | } 221 | return OK; 222 | } 223 | 224 | int doODFreqRead(int argc, char *argv[]); 225 | const CliCmdType CMD_OD_FREQ_READ = 226 | {"odfrd", 2, &doODFreqRead, 227 | "\todfrd: Read open-drain Output PWM frequency(10..6400Hz) \n", 228 | "\tUsage: megaind odfrd \n", "", 229 | "\tExample: megaind 0 odfrd 2; Read the PWM frequency on open-drain output channel #2 on Board #0\n"}; 230 | 231 | int doODFreqRead(int argc, char *argv[]) 232 | { 233 | int ch = 0; 234 | float val = 0; 235 | int dev = 0; 236 | 237 | dev = doBoardInit(atoi(argv[1])); 238 | if (dev <= 0) 239 | { 240 | return ERROR; 241 | } 242 | 243 | if (argc == 4) 244 | { 245 | ch = atoi(argv[3]); 246 | if ( (ch < CHANNEL_NR_MIN) || (ch > OD_CH_NR_MAX)) 247 | { 248 | printf("Open-drain Output channel out of range!\n"); 249 | return ERROR; 250 | } 251 | if(ch == OD_CH_NR_MAX) 252 | { 253 | val = 6400; 254 | } 255 | else 256 | if (OK != val16Get(dev, I2C_MEM_OD1_FREQ, ch, 1, &val)) 257 | { 258 | return ERROR; 259 | } 260 | printf("%d\n", (int)val); 261 | } 262 | else 263 | { 264 | return ARG_CNT_ERR; 265 | } 266 | return OK; 267 | } 268 | 269 | 270 | int doOptoFreqRead(int argc, char *argv[]); 271 | const CliCmdType CMD_IF_READ = 272 | {"ifrd", 2, &doOptoFreqRead, 273 | "\tifrd: Read frequency applied to opto inputs \n", 274 | "\tUsage: megaind ifrd \n", "", 275 | "\tExample: megaind 0 ifrd 2; Read the signal frequency applied to opto input channel #2 on Board #0\n"}; 276 | 277 | int doOptoFreqRead(int argc, char *argv[]) 278 | { 279 | int ch = 0; 280 | float val = 0; 281 | int dev = 0; 282 | 283 | dev = doBoardInit(atoi(argv[1])); 284 | if (dev <= 0) 285 | { 286 | return ERROR; 287 | } 288 | 289 | if (argc == 4) 290 | { 291 | ch = atoi(argv[3]); 292 | if ( (ch < CHANNEL_NR_MIN) || (ch > OD_CH_NR_MAX)) 293 | { 294 | printf("Opto input channel out of range!\n"); 295 | return ERROR; 296 | } 297 | if (OK != val16Get(dev, I2C_MEM_OPTO_FREQ1, ch, 1, &val)) 298 | { 299 | return ERROR; 300 | } 301 | printf("%d\n", (int)val); 302 | } 303 | else 304 | { 305 | return ARG_CNT_ERR; 306 | } 307 | return OK; 308 | } 309 | 310 | int doUOutWrite(int argc, char *argv[]); 311 | const CliCmdType CMD_UOUT_WRITE = 312 | {"uoutwr", 2, &doUOutWrite, 313 | "\tuoutwr: Write 0-10V output voltage value (V)\n", 314 | "\tUsage: megaind uoutwr \n", "", 315 | "\tExample: megaind 0 uoutwr 2 2.5; Write 2.5V to 0-10V output channel #2 on Board #0\n"}; 316 | 317 | int doUOutWrite(int argc, char *argv[]) 318 | { 319 | int ch = 0; 320 | int dev = 0; 321 | float volt = 0; 322 | 323 | dev = doBoardInit(atoi(argv[1])); 324 | if (dev <= 0) 325 | { 326 | return ERROR; 327 | } 328 | 329 | if (argc == 5) 330 | { 331 | ch = atoi(argv[3]); 332 | 333 | volt = atof(argv[4]); 334 | if ( (ch < CHANNEL_NR_MIN) || (ch > U_OUT_CH_NR_MAX)) 335 | { 336 | printf("0-10V Output channel out of range!\n"); 337 | return ERROR; 338 | } 339 | if (volt < -10 || volt > 10) 340 | { 341 | printf("Invalid voltage value, must be -10..10 \n"); 342 | return ERROR; 343 | } 344 | 345 | if (OK 346 | != val16Set(dev, I2C_MEM_U0_10_OUT_VAL1, ch, VOLT_TO_MILIVOLT, volt)) 347 | { 348 | return ERROR; 349 | } 350 | printf("done\n"); 351 | } 352 | else 353 | { 354 | return ARG_CNT_ERR; 355 | } 356 | return OK; 357 | } 358 | 359 | int doIOutWrite(int argc, char *argv[]); 360 | const CliCmdType CMD_IOUT_WRITE = 361 | {"ioutwr", 2, &doIOutWrite, "\tioutwr: Write 4-20mA output value (mA)\n", 362 | "\tUsage: megaind ioutwr \n", "", 363 | "\tExample: megaind 0 ioutwr 2 10.5; Set 10.5mA to 4-20mA output channel #2 on Board #0\n"}; 364 | 365 | int doIOutWrite(int argc, char *argv[]) 366 | { 367 | int ch = 0; 368 | int dev = 0; 369 | float volt = 0; 370 | 371 | dev = doBoardInit(atoi(argv[1])); 372 | if (dev <= 0) 373 | { 374 | return ERROR; 375 | } 376 | 377 | if (argc == 5) 378 | { 379 | ch = atoi(argv[3]); 380 | if ( (ch < CHANNEL_NR_MIN) || (ch > I_OUT_CH_NR_MAX)) 381 | { 382 | printf("4-20mA Output channel out of range!\n"); 383 | return ERROR; 384 | } 385 | volt = atof(argv[4]); 386 | if (volt < 0 || volt > 20) 387 | { 388 | printf("Invalid current value, must be 0..20 \n"); 389 | return ERROR; 390 | } 391 | 392 | if (OK 393 | != val16Set(dev, I2C_MEM_I4_20_OUT_VAL1, ch, VOLT_TO_MILIVOLT, volt)) 394 | { 395 | return ERROR; 396 | } 397 | printf("done\n"); 398 | } 399 | else 400 | { 401 | return ARG_CNT_ERR; 402 | } 403 | return OK; 404 | } 405 | 406 | int doODWrite(int argc, char *argv[]); 407 | const CliCmdType CMD_OD_WRITE = 408 | {"odwr", 2, &doODWrite, 409 | "\todwr: Write open-drain output PWM value (0..100%)\n", 410 | "\tUsage: megaind odwr \n", "", 411 | "\tExample: megaind 0 odwr 2 10.5; Set PWM 10.5% to open-drain output channel #2 on Board #0\n"}; 412 | 413 | int doODWrite(int argc, char *argv[]) 414 | { 415 | int ch = 0; 416 | int dev = 0; 417 | float volt = 0; 418 | 419 | dev = doBoardInit(atoi(argv[1])); 420 | if (dev <= 0) 421 | { 422 | return ERROR; 423 | } 424 | 425 | if (argc == 5) 426 | { 427 | ch = atoi(argv[3]); 428 | if ( (ch < CHANNEL_NR_MIN) || (ch > OD_CH_NR_MAX)) 429 | { 430 | printf("Open-drain channel out of range!\n"); 431 | return ERROR; 432 | } 433 | volt = atof(argv[4]); 434 | if (volt < 0 || volt > 100) 435 | { 436 | printf("Invalid PWM value, must be 0..100 \n"); 437 | return ERROR; 438 | } 439 | 440 | if (OK != val16Set(dev, I2C_MEM_OD_PWM1, ch, 100, volt)) 441 | { 442 | return ERROR; 443 | } 444 | printf("done\n"); 445 | } 446 | else 447 | { 448 | return ARG_CNT_ERR; 449 | } 450 | return OK; 451 | } 452 | int doODFreqWrite(int argc, char *argv[]); 453 | const CliCmdType CMD_OD_FREQ_WRITE = 454 | {"odfwr", 2, &doODFreqWrite, 455 | "\todfwr: Write open-drain output PWM frequency value (10..6400Hz)\n", 456 | "\tUsage: megaind odfwr \n", "", 457 | "\tExample: megaind 0 odfwr 2 250; Set PWM frequency of 250Hz for open-drain output channel #2 on Board #0\n"}; 458 | 459 | int doODFreqWrite(int argc, char *argv[]) 460 | { 461 | int ch = 0; 462 | int dev = 0; 463 | int frequency = 0; 464 | 465 | dev = doBoardInit(atoi(argv[1])); 466 | if (dev <= 0) 467 | { 468 | return ERROR; 469 | } 470 | 471 | if (argc == 5) 472 | { 473 | ch = atoi(argv[3]); 474 | if ( (ch < CHANNEL_NR_MIN) || (ch > 3)) 475 | { 476 | printf("Invalid Open-drain channel for frequency change, must be 1..3!\n"); 477 | return ERROR; 478 | } 479 | frequency = atoi(argv[4]); 480 | if (frequency < 10 || frequency > 6400) 481 | { 482 | printf("Invalid PWM frequency value, must be 10..6400 \n"); 483 | return ERROR; 484 | } 485 | 486 | if (OK != val16Set(dev, I2C_MEM_OD1_FREQ, ch, 1, frequency)) 487 | { 488 | return ERROR; 489 | } 490 | printf("done\n"); 491 | } 492 | else 493 | { 494 | return ARG_CNT_ERR; 495 | } 496 | return OK; 497 | } 498 | int doUInRead(int argc, char *argv[]); 499 | const CliCmdType CMD_UIN_READ = 500 | {"uinrd", 2, &doUInRead, "\tuinrd: Read 0-10V input value (V) \n", 501 | "\tUsage: megaind uinrd \n", "", 502 | "\tExample: megaind 0 uinrd 2; Read the voltage input on 0-10V in channel #2 on Board #0\n"}; 503 | 504 | int doUInRead(int argc, char *argv[]) 505 | { 506 | int ch = 0; 507 | float val = 0; 508 | int dev = 0; 509 | 510 | dev = doBoardInit(atoi(argv[1])); 511 | if (dev <= 0) 512 | { 513 | return ERROR; 514 | } 515 | 516 | if (argc == 4) 517 | { 518 | ch = atoi(argv[3]); 519 | if ( (ch < CHANNEL_NR_MIN) || (ch > U_IN_CH_NR_MAX)) 520 | { 521 | printf("0-10V input channel out of range!\n"); 522 | return ERROR; 523 | } 524 | setUinType(dev, ch, 0);// skip the error 525 | if (OK 526 | != val16Get(dev, I2C_MEM_U0_10_IN_VAL1, ch, VOLT_TO_MILIVOLT, &val)) 527 | { 528 | return ERROR; 529 | } 530 | 531 | printf("%0.3f\n", val); 532 | } 533 | else 534 | { 535 | return ARG_CNT_ERR; 536 | } 537 | return OK; 538 | } 539 | 540 | int doPmUInRead(int argc, char *argv[]); 541 | const CliCmdType CMD_PMUIN_READ = 542 | {"pmuinrd", 2, &doPmUInRead, 543 | "\tpmuinrd: Read +/-10V input value (V). Warning: This value is valid only if the corespondung jumper is connected\n", 544 | "\tUsage: megaind pmuinrd \n", "", 545 | "\tExample: megaind 0 pmuinrd 2; Read the voltage input on +/-10V in channel #2 on Board #0\n"}; 546 | 547 | int doPmUInRead(int argc, char *argv[]) 548 | { 549 | int ch = 0; 550 | float val = 0; 551 | int dev = 0; 552 | 553 | dev = doBoardInit(atoi(argv[1])); 554 | if (dev <= 0) 555 | { 556 | return ERROR; 557 | } 558 | 559 | if (argc == 4) 560 | { 561 | ch = atoi(argv[3]); 562 | if ( (ch < CHANNEL_NR_MIN) || (ch > U_IN_CH_NR_MAX)) 563 | { 564 | printf("+/-10V input channel out of range!\n"); 565 | return ERROR; 566 | } 567 | setUinType(dev, ch, 1);// skip the error 568 | 569 | if (OK 570 | != val16Get(dev, I2C_MEM_U_PM_10_IN_VAL1, ch, VOLT_TO_MILIVOLT, &val)) 571 | { 572 | return ERROR; 573 | } 574 | val -= 10; 575 | printf("%0.3f\n", val); 576 | } 577 | else 578 | { 579 | return ARG_CNT_ERR; 580 | } 581 | return OK; 582 | } 583 | 584 | int doIInRead(int argc, char *argv[]); 585 | const CliCmdType CMD_IIN_READ = 586 | {"iinrd", 2, &doIInRead, "\tiinrd: Read 4-20mA input value (mA) \n", 587 | "\tUsage: megaind iinrd \n", "", 588 | "\tExample: megaind 0 iinrd 2; Read the voltage input on 4-20mA in channel #2 on Board #0\n"}; 589 | 590 | int doIInRead(int argc, char *argv[]) 591 | { 592 | int ch = 0; 593 | float val = 0; 594 | int dev = 0; 595 | 596 | dev = doBoardInit(atoi(argv[1])); 597 | if (dev <= 0) 598 | { 599 | return ERROR; 600 | } 601 | 602 | if (argc == 4) 603 | { 604 | ch = atoi(argv[3]); 605 | if ( (ch < CHANNEL_NR_MIN) || (ch > I_IN_CH_NR_MAX)) 606 | { 607 | printf("4-20mA input channel out of range!\n"); 608 | return ERROR; 609 | } 610 | 611 | if (OK 612 | != val16Get(dev, I2C_MEM_I4_20_IN_VAL1, ch, VOLT_TO_MILIVOLT, &val)) 613 | { 614 | return ERROR; 615 | } 616 | 617 | printf("%0.3f\n", val); 618 | } 619 | else 620 | { 621 | return ARG_CNT_ERR; 622 | } 623 | return OK; 624 | } 625 | 626 | void getCalStat(int dev) 627 | { 628 | u8 buff[2]; 629 | 630 | busyWait(100); 631 | if (OK != i2cMem8Read(dev, I2C_MEM_CALIB_STATUS, buff, 1)) 632 | { 633 | printf("Fail to read calibration status!\n"); 634 | return; 635 | } 636 | switch (buff[0]) 637 | { 638 | case 0: 639 | printf("Calibration in progress\n"); 640 | break; 641 | case 1: 642 | printf("Calibration done\n"); 643 | break; 644 | case 2: 645 | printf("Calibration error!\n"); 646 | break; 647 | default: 648 | printf("Unknown calibration status\n"); 649 | break; 650 | } 651 | } 652 | 653 | int doUInCal(int argc, char *argv[]); 654 | const CliCmdType CMD_UIN_CAL = 655 | {"uincal", 2, &doUInCal, 656 | "\tuincal: Calibrate one 0-10V input channel, the calibration must be done in 2 points at min 5V apart\n", 657 | "\tUsage: megaind uincal \n", "", 658 | "\tExample: megaind 0 uincal 2 0.5; Calibrate the 0-10V input channel #2 on Board #0 at 0.5V\n"}; 659 | 660 | int doUInCal(int argc, char *argv[]) 661 | { 662 | int ch = 0; 663 | float val = 0; 664 | int dev = 0; 665 | u8 buff[4] = {0, 0}; 666 | u16 raw = 0; 667 | 668 | dev = doBoardInit(atoi(argv[1])); 669 | if (dev <= 0) 670 | { 671 | return ERROR; 672 | } 673 | 674 | if (argc == 5) 675 | { 676 | ch = atoi(argv[3]); 677 | if ( (ch < CHANNEL_NR_MIN) || (ch > U_IN_CH_NR_MAX)) 678 | { 679 | printf("0-10V input channel out of range!\n"); 680 | return ERROR; 681 | } 682 | 683 | val = atof(argv[4]); 684 | if ( (val < 0) || (val > 10)) 685 | { 686 | printf("0-10V input calibration value out of range!\n"); 687 | return ERROR; 688 | } 689 | raw = (u16)ceil(val * VOLT_TO_MILIVOLT); 690 | memcpy(buff, &raw, 2); 691 | buff[2] = ch + CAL_0_10V_IN_START_ID - 1; 692 | buff[3] = CALIBRATION_KEY; 693 | 694 | if (OK != i2cMem8Write(dev, I2C_MEM_CALIB_VALUE, buff, 4)) 695 | { 696 | printf("Fail to write calibration data!\n"); 697 | return ERROR; 698 | } 699 | 700 | getCalStat(dev); 701 | } 702 | else 703 | { 704 | return ARG_CNT_ERR; 705 | } 706 | return OK; 707 | } 708 | 709 | int doIInCal(int argc, char *argv[]); 710 | const CliCmdType CMD_IIN_CAL = 711 | {"iincal", 2, &doIInCal, 712 | "\tiincal: Calibrate one 4-20mA input channel, the calibration must be done in 2 points at min 10mA apart\n", 713 | "\tUsage: megaind iincal \n", "", 714 | "\tExample: megaind 0 iincal 2 5.5; Calibrate the 4-20mA input channel #2 on Board #0 at 5.5mA\n"}; 715 | 716 | int doIInCal(int argc, char *argv[]) 717 | { 718 | int ch = 0; 719 | float val = 0; 720 | int dev = 0; 721 | u8 buff[4] = {0, 0}; 722 | u16 raw = 0; 723 | 724 | dev = doBoardInit(atoi(argv[1])); 725 | if (dev <= 0) 726 | { 727 | return ERROR; 728 | } 729 | 730 | if (argc == 5) 731 | { 732 | ch = atoi(argv[3]); 733 | if ( (ch < CHANNEL_NR_MIN) || (ch > I_IN_CH_NR_MAX)) 734 | { 735 | printf("4-20mA input channel out of range!\n"); 736 | return ERROR; 737 | } 738 | 739 | val = atof(argv[4]); 740 | if ( (val < 4) || (val > 20)) 741 | { 742 | printf("4-20mA input calibration value out of range!\n"); 743 | return ERROR; 744 | } 745 | raw = (u16)ceil(val * VOLT_TO_MILIVOLT); 746 | memcpy(buff, &raw, 2); 747 | buff[2] = ch + CAL_4_20mA_IN_START_ID - 1; 748 | buff[3] = CALIBRATION_KEY; 749 | 750 | if (OK != i2cMem8Write(dev, I2C_MEM_CALIB_VALUE, buff, 4)) 751 | { 752 | printf("Fail to write calibration data!\n"); 753 | return ERROR; 754 | } 755 | 756 | getCalStat(dev); 757 | } 758 | else 759 | { 760 | return ARG_CNT_ERR; 761 | } 762 | return OK; 763 | } 764 | 765 | int doUInCalRst(int argc, char *argv[]); 766 | const CliCmdType CMD_UIN_CAL_RST = 767 | {"uincalrst", 2, &doUInCalRst, 768 | "\tuincalrst: Reset the calibration for one 0-10V input channel\n", 769 | "\tUsage: megaind uincalrst \n", "", 770 | "\tExample: megaind 0 uincalrst 2 ; Reset the calibration on 0-10V input channel #2 on Board #0 at factory default\n"}; 771 | 772 | int doUInCalRst(int argc, char *argv[]) 773 | { 774 | int ch = 0; 775 | 776 | int dev = 0; 777 | u8 buff[4] = {0, 0, 0, 0}; 778 | 779 | dev = doBoardInit(atoi(argv[1])); 780 | if (dev <= 0) 781 | { 782 | return ERROR; 783 | } 784 | 785 | if (argc == 4) 786 | { 787 | ch = atoi(argv[3]); 788 | if ( (ch < CHANNEL_NR_MIN) || (ch > U_IN_CH_NR_MAX)) 789 | { 790 | printf("0-10V input channel out of range!\n"); 791 | return ERROR; 792 | } 793 | 794 | buff[2] = ch + CAL_0_10V_IN_START_ID - 1; 795 | 796 | buff[3] = RESET_CALIBRATION_KEY; 797 | 798 | if (OK != i2cMem8Write(dev, I2C_MEM_CALIB_VALUE, buff, 4)) 799 | { 800 | printf("Fail to write calibration data!\n"); 801 | return ERROR; 802 | } 803 | 804 | getCalStat(dev); 805 | } 806 | else 807 | { 808 | return ARG_CNT_ERR; 809 | } 810 | return OK; 811 | } 812 | 813 | int doIInCalRst(int argc, char *argv[]); 814 | const CliCmdType CMD_IIN_CAL_RST = 815 | {"iincalrst", 2, &doIInCalRst, 816 | "\tiincalrst: Reset the calibration for one 4-20mA input channel\n", 817 | "\tUsage: megaind iincalrst \n", "", 818 | "\tExample: megaind 0 iincalrst 2 ; Reset the calibration on 4-20mA input channel #2 on Board #0 at factory default\n"}; 819 | 820 | int doIInCalRst(int argc, char *argv[]) 821 | { 822 | int ch = 0; 823 | 824 | int dev = 0; 825 | u8 buff[4] = {0, 0, 0, 0}; 826 | 827 | dev = doBoardInit(atoi(argv[1])); 828 | if (dev <= 0) 829 | { 830 | return ERROR; 831 | } 832 | 833 | if (argc == 4) 834 | { 835 | ch = atoi(argv[3]); 836 | if ( (ch < CHANNEL_NR_MIN) || (ch > I_IN_CH_NR_MAX)) 837 | { 838 | printf("4-20mA input channel out of range!\n"); 839 | return ERROR; 840 | } 841 | 842 | buff[2] = ch + CAL_4_20mA_IN_START_ID - 1; 843 | 844 | buff[3] = RESET_CALIBRATION_KEY; 845 | 846 | if (OK != i2cMem8Write(dev, I2C_MEM_CALIB_VALUE, buff, 4)) 847 | { 848 | printf("Fail to write calibration data!\n"); 849 | return ERROR; 850 | } 851 | 852 | getCalStat(dev); 853 | } 854 | else 855 | { 856 | return ARG_CNT_ERR; 857 | } 858 | return OK; 859 | } 860 | ////////////////////////////////////////////////////////////////////////////////////////////////////////// 861 | int doUOutCal(int argc, char *argv[]); 862 | const CliCmdType CMD_UOUT_CAL = 863 | {"uoutcal", 2, &doUOutCal, 864 | "\tuoutcal: Calibrate one 0-10V output channel, the calibration must be done in 2 points at min 5V apart\n", 865 | "\tUsage: megaind uoutcal \n", "", 866 | "\tExample: megaind 0 uoutcal 2 0.5; Calibrate the 0-10V output channel #2 on Board #0 at 0.5V\n"}; 867 | 868 | int doUOutCal(int argc, char *argv[]) 869 | { 870 | int ch = 0; 871 | float val = 0; 872 | int dev = 0; 873 | u8 buff[4] = {0, 0}; 874 | u16 raw = 0; 875 | 876 | dev = doBoardInit(atoi(argv[1])); 877 | if (dev <= 0) 878 | { 879 | return ERROR; 880 | } 881 | 882 | if (argc == 5) 883 | { 884 | ch = atoi(argv[3]); 885 | if ( (ch < CHANNEL_NR_MIN) || (ch > U_IN_CH_NR_MAX)) 886 | { 887 | printf("0-10V output channel out of range!\n"); 888 | return ERROR; 889 | } 890 | 891 | val = atof(argv[4]); 892 | if ( (val < 0) || (val > 10)) 893 | { 894 | printf("0-10V input calibration value out of range!\n"); 895 | return ERROR; 896 | } 897 | raw = (u16)ceil(val * VOLT_TO_MILIVOLT); 898 | memcpy(buff, &raw, 2); 899 | buff[2] = ch + CAL_0_10V_OUT_START_ID - 1; 900 | buff[3] = CALIBRATION_KEY; 901 | 902 | if (OK != i2cMem8Write(dev, I2C_MEM_CALIB_VALUE, buff, 4)) 903 | { 904 | printf("Fail to write calibration data!\n"); 905 | return ERROR; 906 | } 907 | 908 | getCalStat(dev); 909 | } 910 | else 911 | { 912 | return ARG_CNT_ERR; 913 | } 914 | return OK; 915 | } 916 | 917 | int doIOutCal(int argc, char *argv[]); 918 | const CliCmdType CMD_IOUT_CAL = 919 | {"ioutcal", 2, &doIOutCal, 920 | "\tioutcal: Calibrate one 4-20mA output channel, the calibration must be done in 2 points at min 10mA apart\n", 921 | "\tUsage: megaind ioutcal \n", "", 922 | "\tExample: megaind 0 ioutcal 2 5.5; Calibrate the 4-20mA output channel #2 on Board #0 at 5.5mA\n"}; 923 | 924 | int doIOutCal(int argc, char *argv[]) 925 | { 926 | int ch = 0; 927 | float val = 0; 928 | int dev = 0; 929 | u8 buff[4] = {0, 0}; 930 | u16 raw = 0; 931 | 932 | dev = doBoardInit(atoi(argv[1])); 933 | if (dev <= 0) 934 | { 935 | return ERROR; 936 | } 937 | 938 | if (argc == 5) 939 | { 940 | ch = atoi(argv[3]); 941 | if ( (ch < CHANNEL_NR_MIN) || (ch > I_OUT_CH_NR_MAX)) 942 | { 943 | printf("4-20mA output channel out of range!\n"); 944 | return ERROR; 945 | } 946 | 947 | val = atof(argv[4]); 948 | if ( (val < 4) || (val > 20)) 949 | { 950 | printf("4-20mA output calibration value out of range!\n"); 951 | return ERROR; 952 | } 953 | raw = (u16)ceil(val * VOLT_TO_MILIVOLT); 954 | memcpy(buff, &raw, 2); 955 | buff[2] = ch + CAL_4_20mA_OUT_START_ID - 1; 956 | buff[3] = CALIBRATION_KEY; 957 | 958 | if (OK != i2cMem8Write(dev, I2C_MEM_CALIB_VALUE, buff, 4)) 959 | { 960 | printf("Fail to write calibration data!\n"); 961 | return ERROR; 962 | } 963 | 964 | getCalStat(dev); 965 | } 966 | else 967 | { 968 | return ARG_CNT_ERR; 969 | } 970 | return OK; 971 | } 972 | int doUOutCalRst(int argc, char *argv[]); 973 | const CliCmdType CMD_UOUT_CAL_RST = 974 | {"uoutcalrst", 2, &doUOutCalRst, 975 | "\tuoutcalrst: Reset the calibration for one 0-10V output channel\n", 976 | "\tUsage: megaind uoutcalrst \n", "", 977 | "\tExample: megaind 0 uoutcalrst 2 ; Reset the calibration on 0-10V output channel #2 on Board #0 at factory default\n"}; 978 | 979 | int doUOutCalRst(int argc, char *argv[]) 980 | { 981 | int ch = 0; 982 | 983 | int dev = 0; 984 | u8 buff[4] = {0, 0, 0, 0}; 985 | 986 | dev = doBoardInit(atoi(argv[1])); 987 | if (dev <= 0) 988 | { 989 | return ERROR; 990 | } 991 | 992 | if (argc == 4) 993 | { 994 | ch = atoi(argv[3]); 995 | if ( (ch < CHANNEL_NR_MIN) || (ch > U_OUT_CH_NR_MAX)) 996 | { 997 | printf("0-10V output channel out of range!\n"); 998 | return ERROR; 999 | } 1000 | 1001 | buff[2] = ch + CAL_0_10V_OUT_START_ID - 1; 1002 | 1003 | buff[3] = RESET_CALIBRATION_KEY; 1004 | 1005 | if (OK != i2cMem8Write(dev, I2C_MEM_CALIB_VALUE, buff, 4)) 1006 | { 1007 | printf("Fail to write calibration data!\n"); 1008 | return ERROR; 1009 | } 1010 | 1011 | getCalStat(dev); 1012 | } 1013 | else 1014 | { 1015 | return ARG_CNT_ERR; 1016 | } 1017 | return OK; 1018 | } 1019 | 1020 | int doIOutCalRst(int argc, char *argv[]); 1021 | const CliCmdType CMD_IOUT_CAL_RST = 1022 | {"ioutcalrst", 2, &doIOutCalRst, 1023 | "\tioutcalrst: Reset the calibration for one 4-20mA output channel\n", 1024 | "\tUsage: megaind ioutcalrst \n", "", 1025 | "\tExample: megaind 0 ioutcalrst 2 ; Reset the calibration on 4-20mA output channel #2 on Board #0 at factory default\n"}; 1026 | 1027 | int doIOutCalRst(int argc, char *argv[]) 1028 | { 1029 | int ch = 0; 1030 | 1031 | int dev = 0; 1032 | u8 buff[4] = {0, 0, 0, 0}; 1033 | 1034 | dev = doBoardInit(atoi(argv[1])); 1035 | if (dev <= 0) 1036 | { 1037 | return ERROR; 1038 | } 1039 | 1040 | if (argc == 4) 1041 | { 1042 | ch = atoi(argv[3]); 1043 | if ( (ch < CHANNEL_NR_MIN) || (ch > I_OUT_CH_NR_MAX)) 1044 | { 1045 | printf("4-20mA output channel out of range!\n"); 1046 | return ERROR; 1047 | } 1048 | 1049 | buff[2] = ch + CAL_4_20mA_OUT_START_ID - 1; 1050 | 1051 | buff[3] = RESET_CALIBRATION_KEY; 1052 | 1053 | if (OK != i2cMem8Write(dev, I2C_MEM_CALIB_VALUE, buff, 4)) 1054 | { 1055 | printf("Fail to write calibration data!\n"); 1056 | return ERROR; 1057 | } 1058 | 1059 | getCalStat(dev); 1060 | } 1061 | else 1062 | { 1063 | return ARG_CNT_ERR; 1064 | } 1065 | return OK; 1066 | } 1067 | -------------------------------------------------------------------------------- /src/cli.h: -------------------------------------------------------------------------------- 1 | #ifndef __CLI__ 2 | #define __CLI__ 3 | 4 | #include "megaind.h" 5 | 6 | 7 | typedef struct 8 | { 9 | const char *name; 10 | const int namePos; 11 | int (*pFunc)(int, char**); 12 | const char *help; 13 | const char *usage1; 14 | const char *usage2; 15 | const char *example; 16 | } CliCmdType; 17 | 18 | 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/cli_def.h: -------------------------------------------------------------------------------- 1 | #ifndef __CLI_DEF_H 2 | #define __CLI_DEF_H 3 | #include "cli.h" 4 | 5 | const CliCmdType *gCmdArray[]; 6 | 7 | #endif -------------------------------------------------------------------------------- /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 512 /* As specified in SMBus standard */ 41 | #define I2C_SMBUS_I2C_BLOCK_MAX 512 /* 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/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_ 12 | -------------------------------------------------------------------------------- /src/dout.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "megaind.h" 8 | #include "comm.h" 9 | 10 | int doDODRead(int argc, char *argv[]); 11 | const CliCmdType CMD_DOD_READ = 12 | { 13 | "dodrd", 14 | 2, 15 | &doDODRead, 16 | "\tdodrd: Read open-drain output digital value \n", 17 | "\tUsage: megaind dodrd \n", 18 | "\tUsage: megaind dodrd\n", 19 | "\tExample: megaind 0 dodrd 2; Read the digital value on open-drain output channel #2 on Board #0\n"}; 20 | 21 | int doDODRead(int argc, char *argv[]) 22 | { 23 | int ch = 0; 24 | u8 buff[2]; 25 | int dev = 0; 26 | 27 | dev = doBoardInit(atoi(argv[1])); 28 | if (dev <= 0) 29 | { 30 | return ERROR; 31 | } 32 | if (argc == 4) 33 | { 34 | ch = atoi(argv[3]); 35 | if ( (ch < CHANNEL_NR_MIN) || (ch > OD_CH_NR_MAX)) 36 | { 37 | printf("Open-drain Output channel out of range!\n"); 38 | return ERROR; 39 | } 40 | if (OK != i2cMem8Read(dev, I2C_MEM_RELAY_VAL, buff, 1)) 41 | { 42 | return COMM_ERR; 43 | } 44 | printf("%d\n", (int) (0x01 & (buff[0] >> (ch - 1)))); 45 | } 46 | else if (argc == 3) 47 | { 48 | if (OK != i2cMem8Read(dev, I2C_MEM_RELAY_VAL, buff, 1)) 49 | { 50 | return COMM_ERR; 51 | } 52 | printf("%d\n", (int) (0x0f & buff[0])); 53 | } 54 | else 55 | { 56 | return ARG_CNT_ERR; 57 | } 58 | return OK; 59 | } 60 | 61 | int doDODWrite(int argc, char *argv[]); 62 | const CliCmdType CMD_DOD_WRITE = 63 | { 64 | "dodwr", 65 | 2, 66 | &doDODWrite, 67 | "\tdodwr: Write open-drain output digital value \n", 68 | "\tUsage: megaind dodwr \n", 69 | "", 70 | "\tExample: megaind 0 dodwr 2 1; Set the digital value on open-drain output channel #2 on Board #0 to enable\n"}; 71 | 72 | int doDODWrite(int argc, char *argv[]) 73 | { 74 | int ch = 0; 75 | int val = 0; 76 | u8 buff[2]; 77 | int dev = 0; 78 | 79 | dev = doBoardInit(atoi(argv[1])); 80 | if (dev <= 0) 81 | { 82 | return ERROR; 83 | } 84 | 85 | if (argc == 5) 86 | { 87 | ch = atoi(argv[3]); 88 | if ( (ch < CHANNEL_NR_MIN) || (ch > OD_CH_NR_MAX)) 89 | { 90 | printf("Open-drain Output channel out of range!\n"); 91 | return ERROR; 92 | } 93 | val = atoi(argv[4]); 94 | buff[0] = ch; 95 | if (val > 0) 96 | { 97 | if (OK != i2cMem8Write(dev, I2C_MEM_RELAY_SET, buff, 1)) 98 | { 99 | return COMM_ERR; 100 | } 101 | } 102 | else 103 | { 104 | if (OK != i2cMem8Write(dev, I2C_MEM_RELAY_CLR, buff, 1)) 105 | { 106 | return COMM_ERR; 107 | } 108 | } 109 | } 110 | else 111 | { 112 | return ARG_CNT_ERR; 113 | } 114 | return OK; 115 | } 116 | 117 | 118 | int doLEDRead(int argc, char *argv[]); 119 | const CliCmdType CMD_LED_READ = 120 | { 121 | "ledrd", 122 | 2, 123 | &doLEDRead, 124 | "\tledrd: Read leds state \n", 125 | "\tUsage: megaind ledrd \n", 126 | "\tUsage: megaind ledrd\n", 127 | "\tExample: megaind 0 ledrd 2; Read the state of LED #2 on Board #0\n"}; 128 | 129 | int doLEDRead(int argc, char *argv[]) 130 | { 131 | int ch = 0; 132 | u8 buff[2]; 133 | int dev = 0; 134 | 135 | dev = doBoardInit(atoi(argv[1])); 136 | if (dev <= 0) 137 | { 138 | return ERROR; 139 | } 140 | if (argc == 4) 141 | { 142 | ch = atoi(argv[3]); 143 | if ( (ch < CHANNEL_NR_MIN) || (ch > LED_CH_NR_MAX)) 144 | { 145 | printf("LED number out of range!\n"); 146 | return ERROR; 147 | } 148 | if (OK != i2cMem8Read(dev, I2C_MEM_RELAY_VAL, buff, 1)) 149 | { 150 | return COMM_ERR; 151 | } 152 | printf("%d\n", (int) (0x01 & (buff[0] >> (ch - 1 + OD_CH_NR_MAX)))); 153 | } 154 | else if (argc == 3) 155 | { 156 | if (OK != i2cMem8Read(dev, I2C_MEM_RELAY_VAL, buff, 1)) 157 | { 158 | return COMM_ERR; 159 | } 160 | printf("%d\n", (int) (0x0f & (buff[0] >> OD_CH_NR_MAX))); 161 | } 162 | else 163 | { 164 | return ARG_CNT_ERR; 165 | } 166 | return OK; 167 | } 168 | 169 | int doLEDWrite(int argc, char *argv[]); 170 | const CliCmdType CMD_LED_WRITE = 171 | { 172 | "ledwr", 173 | 2, 174 | &doLEDWrite, 175 | "\tledwr: Write LED state \n", 176 | "\tUsage: megaind ledwr \n", 177 | "", 178 | "\tExample: megaind 0 ledwr 2 1; Turn on the led #2 on Board #0 \n"}; 179 | 180 | int doLEDWrite(int argc, char *argv[]) 181 | { 182 | int ch = 0; 183 | int val = 0; 184 | u8 buff[2]; 185 | int dev = 0; 186 | 187 | dev = doBoardInit(atoi(argv[1])); 188 | if (dev <= 0) 189 | { 190 | return ERROR; 191 | } 192 | 193 | if (argc == 5) 194 | { 195 | ch = atoi(argv[3]); 196 | if ( (ch < CHANNEL_NR_MIN) || (ch > LED_CH_NR_MAX)) 197 | { 198 | printf("Open-drain Output channel out of range!\n"); 199 | return ERROR; 200 | } 201 | val = atoi(argv[4]); 202 | buff[0] = ch + OD_CH_NR_MAX; 203 | if (val > 0) 204 | { 205 | if (OK != i2cMem8Write(dev, I2C_MEM_RELAY_SET, buff, 1)) 206 | { 207 | return COMM_ERR; 208 | } 209 | } 210 | else 211 | { 212 | if (OK != i2cMem8Write(dev, I2C_MEM_RELAY_CLR, buff, 1)) 213 | { 214 | return COMM_ERR; 215 | } 216 | } 217 | } 218 | else 219 | { 220 | return ARG_CNT_ERR; 221 | } 222 | return OK; 223 | } 224 | 225 | -------------------------------------------------------------------------------- /src/megaind.c: -------------------------------------------------------------------------------- 1 | /* 2 | * rtd.c: 3 | * Command-line interface to the Raspberry 4 | * Pi's MEGAS-RTD board. 5 | * Copyright (c) 2016-2022 Sequent Microsystem 6 | * 7 | *********************************************************************** 8 | * Author: Alexandru Burcea 9 | *********************************************************************** 10 | */ 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "megaind.h" 18 | #include "comm.h" 19 | #include "thread.h" 20 | #include "cli_def.h" 21 | 22 | #define VERSION_BASE (int)1 23 | #define VERSION_MAJOR (int)1 24 | #define VERSION_MINOR (int)8 25 | 26 | #define UNUSED(X) (void)X /* To avoid gcc/g++ warnings */ 27 | 28 | char *warranty = 29 | " Copyright (c) 2016-2025 Sequent Microsystems\n" 30 | " \n" 31 | " This program is free software; you can redistribute it and/or modify\n" 32 | " it under the terms of the GNU Leser General Public License as published\n" 33 | " by the Free Software Foundation, either version 3 of the License, or\n" 34 | " (at your option) any later version.\n" 35 | " \n" 36 | " This program is distributed in the hope that it will be useful,\n" 37 | " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" 38 | " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" 39 | " GNU Lesser General Public License for more details.\n" 40 | " \n" 41 | " You should have received a copy of the GNU Lesser General Public License\n" 42 | " along with this program. If not, see ."; 43 | 44 | void usage(void) 45 | { 46 | int i = 0; 47 | while (gCmdArray[i] != NULL) 48 | { 49 | if (gCmdArray[i]->name != NULL) 50 | { 51 | if (strlen(gCmdArray[i]->usage1) > 2) 52 | { 53 | printf("%s", gCmdArray[i]->usage1); 54 | } 55 | if (strlen(gCmdArray[i]->usage2) > 2) 56 | { 57 | printf("%s", gCmdArray[i]->usage2); 58 | } 59 | } 60 | i++; 61 | } 62 | printf("Where: = Board level id = 0..7\n"); 63 | printf("Type megaind -h for more help\n"); 64 | } 65 | 66 | int doBoardInit(int stack) 67 | { 68 | int dev = 0; 69 | int add = 0; 70 | uint8_t buff[8]; 71 | 72 | if ( (stack < 0) || (stack > 7)) 73 | { 74 | printf("Invalid stack level [0..7]!"); 75 | return ERROR; 76 | } 77 | add = stack + SLAVE_OWN_ADDRESS_BASE; 78 | dev = i2cSetup(add); 79 | if (dev == -1) 80 | { 81 | return ERROR; 82 | } 83 | if (ERROR == i2cMem8Read(dev, I2C_MEM_REVISION_MAJOR, buff, 1)) 84 | { 85 | printf("MEGA-IND id %d not detected\n", stack); 86 | return ERROR; 87 | } 88 | return dev; 89 | } 90 | 91 | int boardCheck(int stack) 92 | { 93 | int dev = 0; 94 | int add = 0; 95 | uint8_t buff[8]; 96 | 97 | if ( (stack < 0) || (stack > 7)) 98 | { 99 | printf("Invalid stack level [0..7]!"); 100 | return ERROR; 101 | } 102 | add = stack + SLAVE_OWN_ADDRESS_BASE; 103 | dev = i2cSetup(add); 104 | if (dev == -1) 105 | { 106 | return ERROR; 107 | } 108 | if (ERROR == i2cMem8Read(dev, I2C_MEM_REVISION_MAJOR, buff, 1)) 109 | { 110 | 111 | return ERROR; 112 | } 113 | return OK; 114 | } 115 | int doHelp(int argc, char *argv[]); 116 | const CliCmdType CMD_HELP = 117 | { 118 | "-h", 119 | 1, 120 | &doHelp, 121 | "\t-h Display the list of command options or one command option details\n", 122 | "\tUsage: megaind -h Display command options list\n", 123 | "\tUsage: megaind -h Display help for command option\n", 124 | "\tExample: megaind -h rread Display help for \"rread\" command option\n"}; 125 | 126 | int doHelp(int argc, char *argv[]) 127 | { 128 | int i = 0; 129 | if (argc == 3) 130 | { 131 | while (NULL != gCmdArray[i]) 132 | { 133 | if (gCmdArray[i]->name != NULL) 134 | { 135 | if (strcasecmp(argv[2], gCmdArray[i]->name) == 0) 136 | { 137 | printf("%s%s%s%s", gCmdArray[i]->help, gCmdArray[i]->usage1, 138 | gCmdArray[i]->usage2, gCmdArray[i]->example); 139 | break; 140 | } 141 | } 142 | i++; 143 | } 144 | if (NULL == gCmdArray[i]) 145 | { 146 | printf("Option \"%s\" not found\n", argv[2]); 147 | i = 0; 148 | while (NULL != gCmdArray[i]) 149 | { 150 | if (gCmdArray[i]->name != NULL) 151 | { 152 | printf("%s", gCmdArray[i]->help); 153 | break; 154 | } 155 | i++; 156 | } 157 | } 158 | } 159 | else 160 | { 161 | i = 0; 162 | while (NULL != gCmdArray[i]) 163 | { 164 | if (gCmdArray[i]->name != NULL) 165 | { 166 | printf("%s", gCmdArray[i]->help); 167 | } 168 | i++; 169 | } 170 | } 171 | return OK; 172 | } 173 | 174 | int doVersion(int argc, char *argv[]); 175 | const CliCmdType CMD_VERSION = 176 | { 177 | "-v", 178 | 1, 179 | &doVersion, 180 | "\t-v Display the megaind command version number\n", 181 | "\tUsage: megaind -v\n", 182 | "", 183 | "\tExample: megaind -v Display the version number\n"}; 184 | 185 | int doVersion(int argc, char *argv[]) 186 | { 187 | UNUSED(argc); 188 | UNUSED(argv); 189 | printf("megaind v%d.%d.%d Copyright (c) 2016 - 2020 Sequent Microsystems\n", 190 | VERSION_BASE, VERSION_MAJOR, VERSION_MINOR); 191 | printf("\nThis is free software with ABSOLUTELY NO WARRANTY.\n"); 192 | printf("For details type: megaind -warranty\n"); 193 | return OK; 194 | } 195 | 196 | int doWarranty(int argc, char *argv[]); 197 | const CliCmdType CMD_WAR = 198 | { 199 | "-warranty", 200 | 1, 201 | &doWarranty, 202 | "\t-warranty Display the warranty\n", 203 | "\tUsage: megaind -warranty\n", 204 | "", 205 | "\tExample: megaind -warranty Display the warranty text\n"}; 206 | 207 | int doWarranty(int argc UNU, char* argv[] UNU) 208 | { 209 | printf("%s\n", warranty); 210 | return OK; 211 | } 212 | 213 | int doList(int argc, char *argv[]); 214 | const CliCmdType CMD_LIST = 215 | { 216 | "-list", 217 | 1, 218 | &doList, 219 | "\t-list: List all megaind boards connected\n\t\t\treturn the # of boards and stack level for every board\n", 220 | "\tUsage: megaind -list\n", 221 | "", 222 | "\tExample: megaind -list display: 1,0 \n"}; 223 | 224 | int doList(int argc, char *argv[]) 225 | { 226 | int ids[8]; 227 | int i; 228 | int cnt = 0; 229 | 230 | UNUSED(argc); 231 | UNUSED(argv); 232 | 233 | for (i = 0; i < 8; i++) 234 | { 235 | if (boardCheck(i) == OK) 236 | { 237 | ids[cnt] = i; 238 | cnt++; 239 | } 240 | } 241 | printf("%d board(s) detected\n", cnt); 242 | if (cnt > 0) 243 | { 244 | printf("Id:"); 245 | } 246 | while (cnt > 0) 247 | { 248 | cnt--; 249 | printf(" %d", ids[cnt]); 250 | } 251 | printf("\n"); 252 | return OK; 253 | } 254 | 255 | int doBoard(int argc, char *argv[]); 256 | const CliCmdType CMD_BOARD = 257 | { 258 | "board", 259 | 2, 260 | &doBoard, 261 | "\tboard Display the board status and firmware version number\n", 262 | "\tUsage: megaind board\n", 263 | "", 264 | "\tExample: megaind 0 board Display vcc, temperature, firmware version \n"}; 265 | 266 | int doBoard(int argc, char *argv[]) 267 | { 268 | int dev = -1; 269 | u8 buff[5]; 270 | int resp = 0; 271 | int temperature = 25; 272 | float vIn = 24; 273 | float vRasp = 5; 274 | 275 | if (argc != 3) 276 | { 277 | return ARG_CNT_ERR; 278 | } 279 | dev = doBoardInit(atoi(argv[1])); 280 | if (dev <= 0) 281 | { 282 | return ERROR; 283 | } 284 | resp = i2cMem8Read(dev, I2C_MEM_DIAG_TEMPERATURE, buff, 5); 285 | if (FAIL == resp) 286 | { 287 | printf("Fail to read board info!\n"); 288 | return ERROR; 289 | } 290 | temperature = buff[0]; 291 | memcpy(&resp, &buff[1], 2); 292 | vIn = (float)resp / VOLT_TO_MILIVOLT; //read in milivolts 293 | 294 | memcpy(&resp, &buff[3], 2); 295 | vRasp = (float)resp / VOLT_TO_MILIVOLT; //read in milivolts 296 | 297 | resp = i2cMem8Read(dev, I2C_MEM_REVISION_MAJOR, buff, 2); 298 | if (FAIL == resp) 299 | { 300 | printf("Fail to read board info!\n"); 301 | return ERROR; 302 | } 303 | printf( 304 | "Firmware ver %02d.%02d, CPU temperature %d C, Power source %0.2f V, Raspberry %0.2f V\n", 305 | (int)buff[0], (int)buff[1], temperature, vIn, vRasp); 306 | return OK; 307 | } 308 | 309 | int doVbRead(int argc, char *argv[]); 310 | const CliCmdType CMD_VB_RD = 311 | { 312 | "vbrd", 313 | 2, 314 | &doVbRead, 315 | "\tvbrd Display the board RTC Backup battery voltage\n", 316 | "\tUsage: megaind vbrd\n", 317 | "", 318 | "\tExample: megaind 0 vbrd Display backup battery voltage \n"}; 319 | 320 | int doVbRead(int argc, char *argv[]) 321 | { 322 | int dev = -1; 323 | u8 buff[5]; 324 | int resp = 0; 325 | float vBattery = 5; 326 | 327 | if (argc != 3) 328 | { 329 | return ARG_CNT_ERR; 330 | } 331 | dev = doBoardInit(atoi(argv[1])); 332 | if (dev <= 0) 333 | { 334 | return ERROR; 335 | } 336 | resp = i2cMem8Read(dev, I2C_MEM_DIAG_3V, buff, 2); 337 | if (FAIL == resp) 338 | { 339 | printf("Fail to read board info!\n"); 340 | return ERROR; 341 | } 342 | 343 | memcpy(&resp, &buff[0], 2); 344 | vBattery = (float)resp / VOLT_TO_MILIVOLT; //read in milivolts 345 | 346 | printf("%0.3f V\n", vBattery); 347 | return OK; 348 | } 349 | 350 | int doOwbGet(int argc, char *argv[]); 351 | const CliCmdType CMD_OWB_RD = 352 | { 353 | "owbtrd", 354 | 2, 355 | &doOwbGet, 356 | "\towbtrd Display the temperature readed from a one wire bus connected sensor\n", 357 | "\tUsage: megaind owbtrd \n", 358 | "", 359 | "\tExample: megaind 0 owbtrd 1 Display the temperature of the sensor #1\n"}; 360 | 361 | int doOwbGet(int argc, char *argv[]) 362 | { 363 | int dev = -1; 364 | u8 buff[5]; 365 | int resp = 0; 366 | int16_t rawResp = 0; 367 | int channel = 0; 368 | float temp = 0; 369 | 370 | if (argc != 4) 371 | { 372 | return ARG_CNT_ERR; 373 | } 374 | channel = atoi(argv[3]); 375 | if(channel < 1 || channel > 16) 376 | { 377 | return ERROR; 378 | } 379 | dev = doBoardInit(atoi(argv[1])); 380 | if (dev <= 0) 381 | { 382 | return ERROR; 383 | } 384 | resp = i2cMem8Read(dev, I2C_MEM_1WB_DEV, buff, 1); 385 | if (FAIL == resp) 386 | { 387 | printf("Fail to read one wire bus info!\n"); 388 | return ERROR; 389 | } 390 | if(channel > buff[0]) 391 | { 392 | printf("Invalid channel number, only %d sensors connected!\n", buff[0]); 393 | return ERROR; 394 | } 395 | 396 | resp = i2cMem8Read(dev, I2C_MEM_1WB_T1 + (channel - 1) *OWB_TEMP_SIZE_B , buff, OWB_TEMP_SIZE_B); 397 | if (FAIL == resp) 398 | { 399 | printf("Fail to read one wire bus info!\n"); 400 | return ERROR; 401 | } 402 | // if (buff[0] == 0) 403 | // { 404 | // printf("No sensor connected!\n"); 405 | // return OK; 406 | // } 407 | memcpy(&rawResp, &buff[0], 2); 408 | temp = (float)rawResp / 100; 409 | 410 | printf("%0.2f C\n", temp); 411 | return OK; 412 | } 413 | 414 | 415 | int doOwbIdGet(int argc, char *argv[]); 416 | const CliCmdType CMD_OWB_ID_RD = 417 | { 418 | "owbidrd", 419 | 2, 420 | &doOwbIdGet, 421 | "\towbidrd Display the 64bits ROM ID of the one wire bus connected sensor\n", 422 | "\tUsage: megaind owbidrd \n", 423 | "", 424 | "\tExample: megaind 0 owbidrd 1 Display the ROM ID of the sensor #1\n"}; 425 | 426 | int doOwbIdGet(int argc, char *argv[]) 427 | { 428 | int dev = -1; 429 | u8 buff[8]; 430 | int resp = 0; 431 | int channel = 0; 432 | uint64_t romID = 0; 433 | 434 | if (argc != 4) 435 | { 436 | return ARG_CNT_ERR; 437 | } 438 | channel = atoi(argv[3]); 439 | if(channel < 1 || channel > 16) 440 | { 441 | return ERROR; 442 | } 443 | dev = doBoardInit(atoi(argv[1])); 444 | if (dev <= 0) 445 | { 446 | return ERROR; 447 | } 448 | buff[0] = 0xff & (channel - 1); 449 | resp = i2cMem8Write(dev, I2C_MEM_1WB_ROM_CODE_IDX, buff, 1);//Select sensor ID to read 450 | if (FAIL == resp) 451 | { 452 | printf("Fail to read one wire bus info!\n"); 453 | return ERROR; 454 | } 455 | resp = i2cMem8Read(dev, I2C_MEM_1WB_DEV, buff, 1);//check the number of connected sensors 456 | if (FAIL == resp) 457 | { 458 | printf("Fail to read one wire bus info!\n"); 459 | return ERROR; 460 | } 461 | if(channel > buff[0]) 462 | { 463 | printf("Invalid channel number, only %d sensors connected!\n", buff[0]); 464 | return ERROR; 465 | } 466 | 467 | resp = i2cMem8Read(dev, I2C_MEM_1WB_ROM_CODE , buff, 8); 468 | if (FAIL == resp) 469 | { 470 | printf("Fail to read one wire bus info!\n"); 471 | return ERROR; 472 | } 473 | 474 | memcpy(&romID, &buff[0], 8); 475 | 476 | printf("0x%llx\n", romID); 477 | return OK; 478 | } 479 | 480 | int doOwbSensCountRead(int argc, char *argv[]); 481 | const CliCmdType CMD_OWB_SNS_CNT_RD = 482 | { 483 | "owbcntrd", 484 | 2, 485 | &doOwbSensCountRead, 486 | "\towbcntrd Display the number of One Wire Bus connected sensors\n", 487 | "\tUsage: megaind owbcntrd\n", 488 | "", 489 | "\tExample: megaind 0 owbcntrd Display the number of sensors connected\n"}; 490 | 491 | int doOwbSensCountRead(int argc, char *argv[]) 492 | { 493 | int dev = -1; 494 | u8 buff[2]; 495 | int resp = 0; 496 | 497 | if (argc != 3) 498 | { 499 | return ARG_CNT_ERR; 500 | } 501 | dev = doBoardInit(atoi(argv[1])); 502 | if (dev <= 0) 503 | { 504 | return ERROR; 505 | } 506 | resp = i2cMem8Read(dev, I2C_MEM_1WB_DEV, buff, 1); 507 | if (FAIL == resp) 508 | { 509 | printf("Fail to read!\n"); 510 | return ERROR; 511 | } 512 | 513 | printf("%d\n", buff[0]); 514 | return OK; 515 | } 516 | 517 | 518 | int doOwbScan(int argc, char *argv[]); 519 | const CliCmdType CMD_OWB_SCAN = 520 | { 521 | "owbscan", 522 | 2, 523 | &doOwbScan, 524 | "\towbscan Start One Wire Bus scaning procedure\n", 525 | "\tUsage: megaind owbscan\n", 526 | "", 527 | "\tExample: megaind 0 owbscan Start One Wire Bus scaning procedure\n"}; 528 | 529 | int doOwbScan(int argc, char *argv[]) 530 | { 531 | int dev = -1; 532 | u8 buff[2]; 533 | int resp = 0; 534 | 535 | if (argc != 3) 536 | { 537 | return ARG_CNT_ERR; 538 | } 539 | dev = doBoardInit(atoi(argv[1])); 540 | if (dev <= 0) 541 | { 542 | return ERROR; 543 | } 544 | buff[0] = 0xaa; 545 | resp = i2cMem8Write(dev, I2C_MEM_1WB_START_SEARCH, buff, 1); 546 | if (FAIL == resp) 547 | { 548 | printf("Fail to write!\n"); 549 | return ERROR; 550 | } 551 | 552 | printf("OK\n"); 553 | return OK; 554 | } 555 | 556 | const CliCmdType *gCmdArray[] = 557 | { 558 | &CMD_VERSION, 559 | &CMD_HELP, 560 | &CMD_WAR, 561 | &CMD_LIST, 562 | &CMD_BOARD, 563 | &CMD_VB_RD, 564 | &CMD_OPTO_READ, 565 | &CMD_COUNTER_READ, 566 | &CMD_IF_READ, 567 | &CMD_COUNTER_RST, 568 | &CMD_EDGE_READ, 569 | &CMD_EDGE_WRITE, 570 | &CMD_UOUT_READ, 571 | &CMD_UOUT_WRITE, 572 | &CMD_IOUT_READ, 573 | &CMD_IOUT_WRITE, 574 | &CMD_OD_READ, 575 | &CMD_OD_FREQ_READ, 576 | &CMD_OD_WRITE, 577 | &CMD_OD_FREQ_WRITE, 578 | &CMD_DOD_READ, 579 | &CMD_DOD_WRITE, 580 | &CMD_LED_READ, 581 | &CMD_LED_WRITE, 582 | &CMD_UIN_READ, 583 | &CMD_PMUIN_READ, 584 | &CMD_IIN_READ, 585 | &CMD_UIN_CAL, 586 | &CMD_IIN_CAL, 587 | &CMD_UIN_CAL_RST, 588 | &CMD_IIN_CAL_RST, 589 | &CMD_UOUT_CAL, 590 | &CMD_IOUT_CAL, 591 | &CMD_UOUT_CAL_RST, 592 | &CMD_IOUT_CAL_RST, 593 | &CMD_WDT_RELOAD, 594 | &CMD_WDT_SET_PERIOD, 595 | &CMD_WDT_GET_PERIOD, 596 | &CMD_WDT_SET_INIT_PERIOD, 597 | &CMD_WDT_GET_INIT_PERIOD, 598 | &CMD_WDT_SET_OFF_PERIOD, 599 | &CMD_WDT_GET_OFF_PERIOD, 600 | &CMD_WDT_GET_RESETS_COUNT, 601 | &CMD_WDT_CLR_RESETS_COUNT, 602 | &CMD_RS485_READ, 603 | &CMD_RS485_WRITE, 604 | &CMD_RTC_GET, 605 | &CMD_RTC_SET, 606 | &CMD_OWB_RD, 607 | &CMD_OWB_ID_RD, 608 | &CMD_OWB_SNS_CNT_RD, 609 | &CMD_OWB_SCAN, 610 | NULL}; //null terminated array of cli structure pointers 611 | 612 | int main(int argc, char *argv[]) 613 | { 614 | int i = 0; 615 | int ret = OK; 616 | 617 | if (argc == 1) 618 | { 619 | usage(); 620 | return ERROR; 621 | } 622 | while (NULL != gCmdArray[i]) 623 | { 624 | if ( (gCmdArray[i]->name != NULL) && (gCmdArray[i]->namePos < argc)) 625 | { 626 | if (strcasecmp(argv[gCmdArray[i]->namePos], gCmdArray[i]->name) == 0) 627 | { 628 | ret = gCmdArray[i]->pFunc(argc, argv); 629 | if (ARG_CNT_ERR == ret) 630 | { 631 | printf("Invalid parameters number!\n"); 632 | printf("%s", gCmdArray[i]->usage1); 633 | if (strlen(gCmdArray[i]->usage2) > 2) 634 | { 635 | printf("%s", gCmdArray[i]->usage2); 636 | } 637 | } 638 | else if (COMM_ERR == ret) 639 | { 640 | printf("Unable to communicate with the card!\n"); 641 | } 642 | return ret; 643 | } 644 | } 645 | i++; 646 | } 647 | printf("Invalid command option\n"); 648 | usage(); 649 | 650 | return ERROR; 651 | } 652 | -------------------------------------------------------------------------------- /src/megaind.h: -------------------------------------------------------------------------------- 1 | #ifndef RELAY8_H_ 2 | #define RELAY8_H_ 3 | 4 | #include 5 | #include "cli.h" 6 | 7 | #define ADC_RAW_VAL_SIZE 2 8 | #define DAC_MV_VAL_SIZE 2 9 | #define VOLT_TO_MILIVOLT 1000 10 | #define OPTO_CH_NO 8 11 | #define GPIO_CH_NO 4 12 | #define COUNTER_SIZE 4 13 | #define DRY_CONTACT_COUNT 8 14 | #define DRY_CONTACT_CONTOR_SIZE 4 15 | 16 | #define RETRY_TIMES 10 17 | #define CALIBRATION_KEY 0xaa 18 | #define RESET_CALIBRATION_KEY 0x55 19 | #define WDT_RESET_SIGNATURE 0xCA 20 | #define WDT_MAX_OFF_INTERVAL_S 4147200 //48 days 21 | #define UI_VAL_SIZE 2 22 | #define CAN_FIFO_SIZE 10 23 | #define OWB_SENS_CNT 16 24 | #define OWB_TEMP_SIZE_B 2 25 | 26 | enum 27 | { 28 | I2C_MEM_RELAY_VAL = 0,//reserved 4 bits for open-drain and 4 bits for leds 29 | I2C_MEM_RELAY_SET, 30 | I2C_MEM_RELAY_CLR, 31 | I2C_MEM_OPTO_IN_VAL, 32 | 33 | I2C_MEM_U0_10_OUT_VAL1, 34 | I2C_MEM_U0_10_OUT_VAL2 = I2C_MEM_U0_10_OUT_VAL1 + UI_VAL_SIZE, 35 | I2C_MEM_U0_10_OUT_VAL3 = I2C_MEM_U0_10_OUT_VAL2 + UI_VAL_SIZE, 36 | I2C_MEM_U0_10_OUT_VAL4 = I2C_MEM_U0_10_OUT_VAL3 + UI_VAL_SIZE, 37 | I2C_MEM_I4_20_OUT_VAL1 = I2C_MEM_U0_10_OUT_VAL4 + UI_VAL_SIZE, 38 | I2C_MEM_I4_20_OUT_VAL2 = I2C_MEM_I4_20_OUT_VAL1 + UI_VAL_SIZE, 39 | I2C_MEM_I4_20_OUT_VAL3 = I2C_MEM_I4_20_OUT_VAL2 + UI_VAL_SIZE, 40 | I2C_MEM_I4_20_OUT_VAL4 = I2C_MEM_I4_20_OUT_VAL3 + UI_VAL_SIZE, 41 | I2C_MEM_OD_PWM1 = I2C_MEM_I4_20_OUT_VAL4 + UI_VAL_SIZE, 42 | I2C_MEM_OD_PWM2 = I2C_MEM_OD_PWM1 + UI_VAL_SIZE, 43 | I2C_MEM_OD_PWM3 = I2C_MEM_OD_PWM2 + UI_VAL_SIZE, 44 | I2C_MEM_OD_PWM4 = I2C_MEM_OD_PWM3 + UI_VAL_SIZE, 45 | 46 | I2C_MEM_U0_10_IN_VAL1 = I2C_MEM_OD_PWM4 + UI_VAL_SIZE, 47 | I2C_MEM_U0_10_IN_VAL2 = I2C_MEM_U0_10_IN_VAL1 + UI_VAL_SIZE, 48 | I2C_MEM_U0_10_IN_VAL3 = I2C_MEM_U0_10_IN_VAL2 + UI_VAL_SIZE, 49 | I2C_MEM_U0_10_IN_VAL4 = I2C_MEM_U0_10_IN_VAL3 + UI_VAL_SIZE, 50 | I2C_MEM_U_PM_10_IN_VAL1 = I2C_MEM_U0_10_IN_VAL4 + UI_VAL_SIZE, 51 | I2C_MEM_U_PM_10_IN_VAL2 = I2C_MEM_U_PM_10_IN_VAL1 + UI_VAL_SIZE, 52 | I2C_MEM_U_PM_10_IN_VAL3 = I2C_MEM_U_PM_10_IN_VAL2 + UI_VAL_SIZE, 53 | I2C_MEM_U_PM_10_IN_VAL4 = I2C_MEM_U_PM_10_IN_VAL3 + UI_VAL_SIZE, 54 | I2C_MEM_I4_20_IN_VAL1 = I2C_MEM_U_PM_10_IN_VAL4 + UI_VAL_SIZE, 55 | I2C_MEM_I4_20_IN_VAL2 = I2C_MEM_I4_20_IN_VAL1 + UI_VAL_SIZE, 56 | I2C_MEM_I4_20_IN_VAL3 = I2C_MEM_I4_20_IN_VAL2 + UI_VAL_SIZE, 57 | I2C_MEM_I4_20_IN_VAL4 = I2C_MEM_I4_20_IN_VAL3 + UI_VAL_SIZE, 58 | 59 | I2C_MEM_I4_20_OUT_VALID1 = I2C_MEM_I4_20_IN_VAL4 + UI_VAL_SIZE, 60 | I2C_MEM_I4_20_OUT_VALID2 = I2C_MEM_I4_20_OUT_VALID1 + UI_VAL_SIZE, 61 | I2C_MEM_I4_20_OUT_VALID3 = I2C_MEM_I4_20_OUT_VALID2 + UI_VAL_SIZE, 62 | I2C_MEM_I4_20_OUT_VALID4 = I2C_MEM_I4_20_OUT_VALID3 + UI_VAL_SIZE, 63 | I2C_MEM_CALIB_VALUE = 60, 64 | I2C_MEM_CALIB_CHANNEL = I2C_MEM_CALIB_VALUE + 2, //0-10V out [1,4]; 0-10V in [5, 12]; R 1K in [13, 20]; R 10K in [21, 28] 65 | I2C_MEM_CALIB_KEY, //set calib point 0xaa; reset calibration on the channel 0x55 66 | I2C_MEM_CALIB_STATUS, 67 | I2C_MODBUS_SETINGS_ADD = 65, 68 | I2C_NBS1, 69 | I2C_MBS2, 70 | I2C_MBS3, 71 | I2C_MODBUS_ID_OFFSET_ADD, 72 | I2C_RTC_YEAR_ADD, 73 | I2C_RTC_MONTH_ADD, 74 | I2C_RTC_DAY_ADD, 75 | I2C_RTC_HOUR_ADD, 76 | I2C_RTC_MINUTE_ADD, 77 | I2C_RTC_SECOND_ADD, 78 | I2C_RTC_SET_YEAR_ADD, 79 | I2C_RTC_SET_MONTH_ADD, 80 | I2C_RTC_SET_DAY_ADD, 81 | I2C_RTC_SET_HOUR_ADD, 82 | I2C_RTC_SET_MINUTE_ADD, 83 | I2C_RTC_SET_SECOND_ADD, 84 | I2C_RTC_CMD_ADD, 85 | I2C_MEM_WDT_RESET_ADD, 86 | I2C_MEM_WDT_INTERVAL_SET_ADD, 87 | I2C_MEM_WDT_INTERVAL_GET_ADD = I2C_MEM_WDT_INTERVAL_SET_ADD + 2, 88 | I2C_MEM_WDT_INIT_INTERVAL_SET_ADD = I2C_MEM_WDT_INTERVAL_GET_ADD + 2, 89 | I2C_MEM_WDT_INIT_INTERVAL_GET_ADD = I2C_MEM_WDT_INIT_INTERVAL_SET_ADD + 2, 90 | I2C_MEM_WDT_RESET_COUNT_ADD = I2C_MEM_WDT_INIT_INTERVAL_GET_ADD + 2, 91 | I2C_MEM_WDT_CLEAR_RESET_COUNT_ADD = I2C_MEM_WDT_RESET_COUNT_ADD + 2, 92 | I2C_MEM_WDT_POWER_OFF_INTERVAL_SET_ADD, 93 | I2C_MEM_WDT_POWER_OFF_INTERVAL_GET_ADD = I2C_MEM_WDT_POWER_OFF_INTERVAL_SET_ADD 94 | + 4, 95 | I2C_MEM_OPTO_RISING_ENABLE = I2C_MEM_WDT_POWER_OFF_INTERVAL_GET_ADD + 4, 96 | I2C_MEM_OPTO_FALLING_ENABLE, 97 | I2C_MEM_OPTO_CH_CONT_RESET, 98 | I2C_MEM_OPTO_COUNT1, 99 | I2C_MEM_OPTO_COUNT2 = I2C_MEM_OPTO_COUNT1 + 2, 100 | I2C_MEM_OPTO_COUNT3 = I2C_MEM_OPTO_COUNT2 + 2, 101 | I2C_MEM_OPTO_COUNT4 = I2C_MEM_OPTO_COUNT3 + 2, 102 | I2C_MEM_DIAG_TEMPERATURE = 0x72, 103 | I2C_MEM_DIAG_24V, 104 | I2C_MEM_DIAG_24V_1, 105 | I2C_MEM_DIAG_5V, 106 | I2C_MEM_DIAG_5V_1, 107 | CAN_REC_MPS_MEM, 108 | 109 | I2C_MEM_REVISION_MAJOR = 0x78, 110 | I2C_MEM_REVISION_MINOR, 111 | I2C_MEM_PM_IN_SW, 112 | #ifdef CAN 113 | I2C_MEM_CAN_TX_FIFO_LEVEL, 114 | I2C_MEM_CAN_TX_FIFO, 115 | I2C_MEM_CAN_RX_FIFO_LEVEL = I2C_MEM_CAN_TX_FIFO + CAN_FIFO_SIZE, 116 | I2C_MEM_CAN_RX_FIFO, 117 | I2C_MEM_CAN_RX_FIFO_MARK = I2C_MEM_CAN_RX_FIFO + CAN_FIFO_SIZE, 118 | #endif 119 | I2C_MEM_DIAG_3V = 145, 120 | I2C_MEM_DIAG_3V_1, 121 | I2C_MEM_1WB_DEV, 122 | I2C_MEM_1WB_TEMP_ALL, 123 | I2C_MEM_1WB_ROM_CODE_IDX = I2C_MEM_1WB_TEMP_ALL + OWB_TEMP_SIZE_B, 124 | I2C_MEM_1WB_ROM_CODE,//rom code 64 bits 125 | I2C_MEM_1WB_ROM_CODE_END = I2C_MEM_1WB_ROM_CODE + 7, 126 | I2C_MEM_OPTO_FREQ1, 127 | I2C_MEM_OPTO_FREQ2 = I2C_MEM_OPTO_FREQ1 + UI_VAL_SIZE, 128 | I2C_MEM_OPTO_FREQ3 = I2C_MEM_OPTO_FREQ2 + UI_VAL_SIZE, 129 | I2C_MEM_OPTO_FREQ4 = I2C_MEM_OPTO_FREQ3 + UI_VAL_SIZE, 130 | 131 | 132 | I2C_MEM_CPU_RESET = 0xaa, 133 | I2C_MEM_HSI_LO, 134 | I2C_MEM_HSI_HI, 135 | I2C_MEM_1WB_START_SEARCH, 136 | I2C_MEM_1WB_T1, 137 | I2C_MEM_1WB_T16 = I2C_MEM_1WB_T1 + OWB_SENS_CNT * OWB_TEMP_SIZE_B, 138 | I2C_MEM_OD1_FREQ = I2C_MEM_1WB_T16 + UI_VAL_SIZE, 139 | I2C_MEM_OD2_FREQ = I2C_MEM_OD1_FREQ + UI_VAL_SIZE, 140 | I2C_MEM_OD3_FREQ = I2C_MEM_OD2_FREQ + UI_VAL_SIZE, 141 | I2C_MEM_SIZE = 255 142 | //SLAVE_BUFF_SIZE = 255 143 | }; 144 | 145 | enum CAL_CH_START_ID{ 146 | CAL_0_10V_OUT_START_ID = 1, 147 | CAL_0_10V_OUT_STOP_ID = 4, 148 | CAL_4_20mA_OUT_START_ID, 149 | CAL_4_20mA_OUT_STOP_ID = 8, 150 | CAL_0_10V_IN_START_ID, 151 | CAL_0_10V_IN_STOP_ID = 12, 152 | CAL_PM_10V_IN_START_ID, 153 | CAL_PM_10V_IN_STOP_ID = 16, 154 | CAL_4_20mA_IN_START_ID, 155 | CAL_4_20mA_IN_STOP_ID = 24, 156 | }; 157 | 158 | #define CHANNEL_NR_MIN 1 159 | #define RELAY_CH_NR_MAX 4 160 | 161 | #define OPTO_CH_NR_MAX 4 162 | #define OD_CH_NR_MAX 4 163 | #define I_OUT_CH_NR_MAX 4 164 | #define U_OUT_CH_NR_MAX 4 165 | #define U_IN_CH_NR_MAX 4 166 | #define I_IN_CH_NR_MAX 4 167 | #define LED_CH_NR_MAX 4 168 | 169 | #define OD_PWM_VAL_MAX 10000 170 | 171 | #define ERROR -1 172 | #define OK 0 173 | #define FAIL -1 174 | #define ARG_CNT_ERR -3 175 | #define COMM_ERR -4 176 | 177 | #define SLAVE_OWN_ADDRESS_BASE 0x50 178 | 179 | typedef uint8_t u8; 180 | typedef int8_t s8; 181 | typedef uint16_t u16; 182 | typedef int16_t s16; 183 | typedef uint32_t u32; 184 | typedef int32_t s32; 185 | 186 | typedef enum 187 | { 188 | OFF = 0, 189 | ON, 190 | STATE_COUNT 191 | } OutStateEnumType; 192 | 193 | 194 | typedef struct 195 | __attribute__((packed)) 196 | { 197 | unsigned int mbBaud :24; 198 | unsigned int mbType :4; 199 | unsigned int mbParity :2; 200 | unsigned int mbStopB :2; 201 | unsigned int add:8; 202 | } ModbusSetingsType; 203 | 204 | 205 | int doBoardInit(int stack); 206 | 207 | //opto CLI structures 208 | extern const CliCmdType CMD_OPTO_READ; 209 | extern const CliCmdType CMD_COUNTER_READ; 210 | extern const CliCmdType CMD_COUNTER_RST; 211 | extern const CliCmdType CMD_EDGE_READ; 212 | extern const CliCmdType CMD_EDGE_WRITE; 213 | 214 | // watchdog CLI structures 215 | extern const CliCmdType CMD_WDT_RELOAD; 216 | extern const CliCmdType CMD_WDT_SET_PERIOD; 217 | extern const CliCmdType CMD_WDT_GET_PERIOD; 218 | extern const CliCmdType CMD_WDT_SET_INIT_PERIOD; 219 | extern const CliCmdType CMD_WDT_GET_INIT_PERIOD; 220 | extern const CliCmdType CMD_WDT_SET_OFF_PERIOD; 221 | extern const CliCmdType CMD_WDT_GET_OFF_PERIOD; 222 | extern const CliCmdType CMD_WDT_GET_RESETS_COUNT; 223 | extern const CliCmdType CMD_WDT_CLR_RESETS_COUNT; 224 | 225 | 226 | // RTC CLI structures 227 | extern const CliCmdType CMD_RTC_GET; 228 | extern const CliCmdType CMD_RTC_SET; 229 | 230 | // RS-485 CLI structures 231 | extern const CliCmdType CMD_RS485_READ; 232 | extern const CliCmdType CMD_RS485_WRITE; 233 | 234 | // analog in/out CLI structures 235 | extern const CliCmdType CMD_UOUT_READ; 236 | extern const CliCmdType CMD_IOUT_READ; 237 | extern const CliCmdType CMD_OD_READ; 238 | extern const CliCmdType CMD_OD_FREQ_READ; 239 | extern const CliCmdType CMD_IF_READ; 240 | extern const CliCmdType CMD_UOUT_WRITE; 241 | extern const CliCmdType CMD_IOUT_WRITE; 242 | extern const CliCmdType CMD_OD_WRITE; 243 | extern const CliCmdType CMD_UIN_READ; 244 | extern const CliCmdType CMD_PMUIN_READ; 245 | extern const CliCmdType CMD_IIN_READ; 246 | extern const CliCmdType CMD_UIN_CAL; 247 | extern const CliCmdType CMD_IIN_CAL; 248 | extern const CliCmdType CMD_UIN_CAL_RST; 249 | extern const CliCmdType CMD_IIN_CAL_RST; 250 | extern const CliCmdType CMD_UOUT_CAL; 251 | extern const CliCmdType CMD_IOUT_CAL; 252 | extern const CliCmdType CMD_UOUT_CAL_RST; 253 | extern const CliCmdType CMD_IOUT_CAL_RST; 254 | extern const CliCmdType CMD_OD_FREQ_WRITE; 255 | 256 | 257 | // digital control OD 258 | extern const CliCmdType CMD_DOD_READ; 259 | extern const CliCmdType CMD_DOD_WRITE; 260 | 261 | // LED control 262 | extern const CliCmdType CMD_LED_READ; 263 | extern const CliCmdType CMD_LED_WRITE; 264 | 265 | #endif //RELAY8_H_ 266 | -------------------------------------------------------------------------------- /src/opto.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "megaind.h" 9 | #include "comm.h" 10 | #include "thread.h" 11 | 12 | 13 | int optoChGet(int dev, u8 channel, OutStateEnumType* state) 14 | { 15 | u8 buff[2]; 16 | 17 | if (NULL == state) 18 | { 19 | return ERROR; 20 | } 21 | 22 | if ( (channel < CHANNEL_NR_MIN) || (channel > OPTO_CH_NR_MAX)) 23 | { 24 | printf("Invalid OPTO channel nr!\n"); 25 | return ERROR; 26 | } 27 | 28 | if (FAIL == i2cMem8Read(dev, I2C_MEM_OPTO_IN_VAL, buff, 1)) 29 | { 30 | return ERROR; 31 | } 32 | 33 | if (buff[0] & (1 << (channel - 1))) 34 | { 35 | *state = ON; 36 | } 37 | else 38 | { 39 | *state = OFF; 40 | } 41 | return OK; 42 | } 43 | 44 | int optoGet(int dev, int* val) 45 | { 46 | u8 buff[2]; 47 | 48 | if (NULL == val) 49 | { 50 | return ERROR; 51 | } 52 | if (FAIL == i2cMem8Read(dev, I2C_MEM_OPTO_IN_VAL, buff, 1)) 53 | { 54 | return ERROR; 55 | } 56 | *val = buff[0]; 57 | return OK; 58 | } 59 | 60 | int optoCountGet(int dev, u8 ch, u16* val) 61 | { 62 | u8 buff[2]; 63 | 64 | if (NULL == val) 65 | { 66 | return ERROR; 67 | } 68 | if ( (ch < CHANNEL_NR_MIN) || (ch > OPTO_CH_NR_MAX)) 69 | { 70 | printf("Invalid OPTO channel nr!\n"); 71 | return ERROR; 72 | } 73 | if (FAIL == i2cMem8Read(dev, I2C_MEM_OPTO_COUNT1 + 2 * (ch - 1), buff, 2)) 74 | { 75 | return ERROR; 76 | } 77 | memcpy(val, buff, 2); 78 | return OK; 79 | } 80 | 81 | int optoCountReset(int dev, u8 channel) 82 | { 83 | u8 buff[4]; 84 | 85 | if ( (channel < CHANNEL_NR_MIN) || (channel > OPTO_CH_NR_MAX)) 86 | { 87 | printf("Invalid OPTO channel nr!\n"); 88 | return ERROR; 89 | } 90 | buff[0] = channel; 91 | if (FAIL == i2cMem8Write(dev, I2C_MEM_OPTO_CH_CONT_RESET, buff, 1)) 92 | { 93 | printf("Fail to reset counter! \n"); 94 | return ERROR; 95 | } 96 | return OK; 97 | } 98 | 99 | int optoCountRisingSet(int dev, u8 channel, u8 state) 100 | { 101 | u8 buff[4]; 102 | 103 | if ( (channel < CHANNEL_NR_MIN) || (channel > OPTO_CH_NR_MAX)) 104 | { 105 | printf("Invalid DRY OPTO nr!\n"); 106 | return ERROR; 107 | } 108 | 109 | if (FAIL == i2cMem8Read(dev, I2C_MEM_OPTO_RISING_ENABLE, buff, 1)) 110 | { 111 | return ERROR; 112 | } 113 | if (state != 0) 114 | { 115 | buff[0] |= 1 << (channel - 1); 116 | } 117 | else 118 | { 119 | buff[0] &= ~ (1 << (channel - 1)); 120 | } 121 | if (FAIL == i2cMem8Write(dev, I2C_MEM_OPTO_RISING_ENABLE, buff, 1)) 122 | { 123 | return ERROR; 124 | } 125 | return OK; 126 | } 127 | 128 | int optoCountFallingSet(int dev, u8 channel, u8 state) 129 | { 130 | u8 buff[4]; 131 | 132 | if ( (channel < CHANNEL_NR_MIN) || (channel > OPTO_CH_NR_MAX)) 133 | { 134 | printf("Invalid OPTO channel nr!\n"); 135 | return ERROR; 136 | } 137 | 138 | if (FAIL == i2cMem8Read(dev, I2C_MEM_OPTO_FALLING_ENABLE, buff, 1)) 139 | { 140 | return ERROR; 141 | } 142 | if (state != 0) 143 | { 144 | buff[0] |= 1 << (channel - 1); 145 | } 146 | else 147 | { 148 | buff[0] &= ~ (1 << (channel - 1)); 149 | } 150 | if (FAIL == i2cMem8Write(dev, I2C_MEM_OPTO_FALLING_ENABLE, buff, 1)) 151 | { 152 | return ERROR; 153 | } 154 | return OK; 155 | } 156 | 157 | int optoCountRisingGet(int dev, u8 channel, u8* state) 158 | { 159 | u8 buff[4]; 160 | 161 | if (NULL == state) 162 | { 163 | return ERROR; 164 | } 165 | if ( (channel < CHANNEL_NR_MIN) || (channel > OPTO_CH_NR_MAX)) 166 | { 167 | printf("Invalid OPTO channel nr!\n"); 168 | return ERROR; 169 | } 170 | 171 | if (FAIL == i2cMem8Read(dev, I2C_MEM_OPTO_RISING_ENABLE, buff, 1)) 172 | { 173 | return ERROR; 174 | } 175 | if ( (buff[0] & (1 << (channel - 1))) != 0) 176 | { 177 | *state = 1; 178 | } 179 | else 180 | { 181 | *state = 0; 182 | } 183 | return OK; 184 | } 185 | 186 | int optoCountFallingGet(int dev, u8 channel, u8* state) 187 | { 188 | u8 buff[4]; 189 | 190 | if (NULL == state) 191 | { 192 | return ERROR; 193 | } 194 | if ( (channel < CHANNEL_NR_MIN) || (channel > OPTO_CH_NR_MAX)) 195 | { 196 | printf("Invalid OPTO channel nr!\n"); 197 | return ERROR; 198 | } 199 | 200 | if (FAIL == i2cMem8Read(dev, I2C_MEM_OPTO_FALLING_ENABLE, buff, 1)) 201 | { 202 | return COMM_ERR; 203 | } 204 | if ( (buff[0] & (1 << (channel - 1))) != 0) 205 | { 206 | *state = 1; 207 | } 208 | else 209 | { 210 | *state = 0; 211 | } 212 | return OK; 213 | } 214 | 215 | int doOptoRead(int argc, char *argv[]); 216 | const CliCmdType CMD_OPTO_READ = 217 | { 218 | "optord", 219 | 2, 220 | &doOptoRead, 221 | "\toptord: Read dry opto status\n", 222 | "\tUsage: megaind optord \n", 223 | "\tUsage: megaind optord\n", 224 | "\tExample: megaind 0 optord 2; Read Status of opto input pin #2 on Board #0\n"}; 225 | 226 | int doOptoRead(int argc, char *argv[]) 227 | { 228 | int pin = 0; 229 | int val = 0; 230 | int dev = 0; 231 | OutStateEnumType state = STATE_COUNT; 232 | 233 | dev = doBoardInit(atoi(argv[1])); 234 | if (dev <= 0) 235 | { 236 | return ERROR; 237 | } 238 | 239 | if (argc == 4) 240 | { 241 | pin = atoi(argv[3]); 242 | if ( (pin < CHANNEL_NR_MIN) || (pin > OPTO_CH_NR_MAX)) 243 | { 244 | printf("Opto channel number value out of range!\n"); 245 | return ERROR; 246 | } 247 | 248 | if (OK != optoChGet(dev, pin, &state)) 249 | { 250 | return COMM_ERR; 251 | } 252 | if (state != 0) 253 | { 254 | printf("1\n"); 255 | } 256 | else 257 | { 258 | printf("0\n"); 259 | } 260 | } 261 | else if (argc == 3) 262 | { 263 | if (OK != optoGet(dev, &val)) 264 | { 265 | return COMM_ERR; 266 | } 267 | printf("%d\n", val); 268 | } 269 | else 270 | { 271 | return ARG_CNT_ERR; 272 | } 273 | return OK; 274 | } 275 | 276 | int doCountRead(int argc, char *argv[]); 277 | const CliCmdType CMD_COUNTER_READ = 278 | { 279 | "countrd", 280 | 2, 281 | &doCountRead, 282 | "\tcountrd: Read dry opto transitions count\n", 283 | "\tUsage: megaind countrd \n", 284 | "", 285 | "\tExample: megaind 0 countrd 2; Read transitions count of opto input pin #2 on Board #0\n"}; 286 | 287 | int doCountRead(int argc, char *argv[]) 288 | { 289 | u8 pin = 0; 290 | u16 val = 0; 291 | int dev = 0; 292 | 293 | dev = doBoardInit(atoi(argv[1])); 294 | if (dev <= 0) 295 | { 296 | return ERROR; 297 | } 298 | 299 | if (argc == 4) 300 | { 301 | pin = (u8)atoi(argv[3]); 302 | 303 | if (OK != optoCountGet(dev, pin, &val)) 304 | { 305 | return COMM_ERR; 306 | } 307 | printf("%u\n", (unsigned int)val); 308 | } 309 | else 310 | { 311 | return ARG_CNT_ERR; 312 | } 313 | return OK; 314 | } 315 | 316 | int doCountReset(int argc, char *argv[]); 317 | const CliCmdType CMD_COUNTER_RST = 318 | { 319 | "countrst", 320 | 2, 321 | &doCountReset, 322 | "\tcountrst: Reset opto transitions countors\n", 323 | "\tUsage: megaind countrst \n", 324 | "", 325 | "\tExample: megaind 0 countrst 2; Reset transitions count of opto input pin #2 on Board #0\n"}; 326 | 327 | int doCountReset(int argc, char *argv[]) 328 | { 329 | u8 pin = 0; 330 | int dev = 0; 331 | 332 | dev = doBoardInit(atoi(argv[1])); 333 | if (dev <= 0) 334 | { 335 | return ERROR; 336 | } 337 | 338 | if (argc == 4) 339 | { 340 | pin = (u8)atoi(argv[3]); 341 | 342 | if (OK != optoCountReset(dev, pin)) 343 | { 344 | printf("Fail to read!\n"); 345 | return ERROR; 346 | } 347 | printf("done\n"); 348 | } 349 | else 350 | { 351 | return ARG_CNT_ERR; 352 | } 353 | return OK; 354 | } 355 | 356 | int doEdgeRead(int argc, char *argv[]); 357 | const CliCmdType CMD_EDGE_READ = 358 | { 359 | "edgerd", 360 | 2, 361 | &doEdgeRead, 362 | "\tedgerd: Read opto inputs transitions type, ret 0 - disable, 1 - rising, 2 - falling, 3 - both\n", 363 | "\tUsage: megaind edgerd \n", 364 | "", 365 | "\tExample: megaind 0 edgerd 2; Read transitions type of opto input pin #2 on Board #0\n"}; 366 | 367 | int doEdgeRead(int argc, char *argv[]) 368 | { 369 | u8 pin = 0; 370 | u8 rising = 0; 371 | u8 falling = 0; 372 | int dev = 0; 373 | 374 | dev = doBoardInit(atoi(argv[1])); 375 | if (dev <= 0) 376 | { 377 | return ERROR; 378 | } 379 | 380 | if (argc == 4) 381 | { 382 | pin = (u8)atoi(argv[3]); 383 | 384 | if (OK != optoCountRisingGet(dev, pin, &rising)) 385 | { 386 | printf("Fail to read!\n"); 387 | return ERROR; 388 | } 389 | if (OK != optoCountFallingGet(dev, pin, &falling)) 390 | { 391 | printf("Fail to read!\n"); 392 | return ERROR; 393 | } 394 | printf("%d\n", (int) (rising + falling * 2)); 395 | } 396 | else 397 | { 398 | return ARG_CNT_ERR; 399 | } 400 | return OK; 401 | } 402 | 403 | int doEdgeWrite(int argc, char *argv[]); 404 | const CliCmdType CMD_EDGE_WRITE = 405 | { 406 | "edgewr", 407 | 2, 408 | &doEdgeWrite, 409 | "\tedgewr: Writ opto inputs transitions type: 0 - disable, 1 - rising, 2 - falling, 3 - both\n", 410 | "\tUsage: megaind edgewr \n", 411 | "", 412 | "\tExample: megaind 0 edgewr 2 1; Set transitions type of opto input #2 on Board #0 to rising\n"}; 413 | 414 | int doEdgeWrite(int argc, char *argv[]) 415 | { 416 | u8 pin = 0; 417 | u8 rising = 0; 418 | u8 falling = 0; 419 | int dev = 0; 420 | 421 | dev = doBoardInit(atoi(argv[1])); 422 | if (dev <= 0) 423 | { 424 | return ERROR; 425 | } 426 | 427 | if (argc == 5) 428 | { 429 | pin = (u8)atoi(argv[3]); 430 | 431 | if ( (1 & atoi(argv[4])) != 0) 432 | { 433 | rising = 1; 434 | } 435 | if ( (2 & atoi(argv[4])) != 0) 436 | { 437 | falling = 1; 438 | } 439 | if (OK != optoCountRisingSet(dev, pin, rising)) 440 | { 441 | printf("Fail to write!\n"); 442 | return ERROR; 443 | } 444 | if (OK != optoCountFallingSet(dev, pin, falling)) 445 | { 446 | printf("Fail to write!\n"); 447 | return ERROR; 448 | } 449 | } 450 | else 451 | { 452 | return ARG_CNT_ERR; 453 | } 454 | return OK; 455 | } 456 | -------------------------------------------------------------------------------- /src/rs485.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "megaind.h" 8 | #include "comm.h" 9 | 10 | int rs485Set(int dev, u8 mode, u32 baud, u8 stopB, u8 parity, u8 add) { 11 | ModbusSetingsType settings; 12 | u8 buff[5]; 13 | if (mode > 2) { 14 | printf("Invalid RS485 mode : 0 = disable, 1= Modbus RTU (Slave)!\n"); 15 | return ERROR; 16 | } 17 | if (mode == 0) { 18 | settings.mbBaud = 9600; 19 | settings.mbType = 0; 20 | settings.mbParity = 0; 21 | settings.mbStopB = 1; 22 | settings.add = 1; 23 | } else if (mode == 1) { 24 | if (baud > 920600 || baud < 1200) { 25 | printf("Invalid RS485 Baudrate(%d), must be [1200, 920600]!\n", (int)baud); 26 | return ERROR; 27 | } 28 | if (stopB < 1 || stopB > 2) { 29 | printf("Invalid RS485 stop bits(%d), must be [1, 2]!\n", (int)stopB); 30 | return ERROR; 31 | } 32 | if (parity > 2) { 33 | printf("Invalid RS485 parity(%d), must be 0 = none; 1 = even; 2 = odd!\n", (int)parity); 34 | return ERROR; 35 | } 36 | if (add < 1) { 37 | printf("Invalid MODBUS device address(%d), must be [1, 255]!\n", (int)add); 38 | return ERROR; 39 | } 40 | settings.mbBaud = baud; 41 | settings.mbType = mode; 42 | settings.mbParity = parity; 43 | settings.mbStopB = stopB; 44 | settings.add = add; 45 | } 46 | memcpy(buff, &settings, sizeof(ModbusSetingsType)); 47 | if (OK != i2cMem8Write(dev, I2C_MODBUS_SETINGS_ADD, buff, 5)) { 48 | printf("Fail to write RS485 settings!\n"); 49 | return ERROR; 50 | } 51 | return OK; 52 | } 53 | 54 | int rs485Get(int dev) 55 | { 56 | ModbusSetingsType settings; 57 | u8 buff[5]; 58 | 59 | if (OK != i2cMem8Read(dev, I2C_MODBUS_SETINGS_ADD, buff, 5)) 60 | { 61 | printf("Fail to read RS485 settings!\n"); 62 | return ERROR; 63 | } 64 | memcpy(&settings, buff, sizeof(ModbusSetingsType)); 65 | printf(" %d %d %d %d %d\n", 66 | (int)settings.mbType, (int)settings.mbBaud, (int)settings.mbStopB, 67 | (int)settings.mbParity, (int)settings.add); 68 | return OK; 69 | } 70 | 71 | int doRs485Read(int argc, char *argv[]); 72 | const CliCmdType CMD_RS485_READ = 73 | { 74 | "rs485rd", 75 | 2, 76 | &doRs485Read, 77 | "\trs485rd: Read the RS485 communication settings\n", 78 | "\tUsage: megaind rs485rd\n", 79 | "", 80 | "\tExample: megaind 0 rs485rd; Read the RS485 settings on Board #0\n"}; 81 | 82 | int doRs485Read(int argc, char *argv[]) 83 | { 84 | int dev = 0; 85 | 86 | dev = doBoardInit(atoi(argv[1])); 87 | if (dev <= 0) 88 | { 89 | return ERROR; 90 | } 91 | 92 | if (argc == 3) 93 | { 94 | if (OK != rs485Get(dev)) 95 | { 96 | return ERROR; 97 | } 98 | } 99 | else 100 | { 101 | return ARG_CNT_ERR; 102 | } 103 | return OK; 104 | } 105 | 106 | int doRs485Write(int argc, char *argv[]); 107 | const CliCmdType CMD_RS485_WRITE = 108 | { 109 | "rs485wr", 110 | 2, 111 | &doRs485Write, 112 | "\trs485wr: Write the RS485 communication settings\n", 113 | "\tUsage: megaind rs485wr \n", 114 | "", 115 | "\tExample: megaind 0 rs485wr 1 9600 1 0 1; Write the RS485 settings on Board #0 \n" 116 | "\t\t\t(mode = Modbus RTU; baudrate = 9600 bps; stop bits one; parity none; modbus slave address = 1)\n" 117 | "\tExample 2: megaind 0 rs485wr 0; Disable modbus\n" 118 | }; 119 | 120 | int doRs485Write(int argc, char *argv[]) 121 | { 122 | int dev = 0; 123 | u8 mode = 0; 124 | u32 baud = 9600; 125 | u8 stopB = 1; 126 | u8 parity = 0; 127 | u8 add = 1; 128 | 129 | dev = doBoardInit(atoi(argv[1])); 130 | if (dev <= 0) 131 | { 132 | return ERROR; 133 | } 134 | 135 | if (argc == 4 && strcmp(argv[3], "0") == 0) 136 | { 137 | mode = 0; 138 | if (OK != rs485Set(dev, mode, baud, stopB, parity, add)) 139 | { 140 | return ERROR; 141 | } 142 | printf("done\n"); 143 | } 144 | else 145 | if (argc == 8) 146 | { 147 | mode = 0xff & atoi(argv[3]); 148 | baud = atoi(argv[4]); 149 | stopB = 0xff & atoi(argv[5]); 150 | parity = 0xff & atoi(argv[6]); 151 | add = 0xff & atoi(argv[7]); 152 | if (OK != rs485Set(dev, mode, baud, stopB, parity, add)) 153 | { 154 | return ERROR; 155 | } 156 | printf("done\n"); 157 | } 158 | else 159 | { 160 | return ARG_CNT_ERR; 161 | } 162 | return OK; 163 | } 164 | -------------------------------------------------------------------------------- /src/rtc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "megaind.h" 8 | #include "comm.h" 9 | 10 | int doRTCGet(int argc, char *argv[]); 11 | const CliCmdType CMD_RTC_GET = 12 | { 13 | "rtcrd", 14 | 2, 15 | &doRTCGet, 16 | "\trtcrd: Get the internal RTC date and time(mm/dd/yy hh:mm:ss)\n", 17 | "\tUsage: megaind rtcrd \n", 18 | "", 19 | "\tExample: megaind 0 rtcrd; Get the nternal RTC time and date on Board #0\n"}; 20 | 21 | int doRTCGet(int argc, char *argv[]) 22 | { 23 | int dev = 0; 24 | u8 buff[6]; 25 | 26 | dev = doBoardInit(atoi(argv[1])); 27 | if (dev <= 0) 28 | { 29 | return ERROR; 30 | } 31 | 32 | if (argc == 3) 33 | { 34 | if (OK != i2cMem8Read(dev, I2C_RTC_YEAR_ADD, buff, 6)) 35 | { 36 | printf("Fail to read RTC!\n"); 37 | return ERROR; 38 | } 39 | 40 | printf("%02u/%02u/%02u %02u:%02u:%02u\n", buff[1], buff[2], buff[0], 41 | buff[3], buff[4], buff[5]); 42 | } 43 | else 44 | { 45 | return ARG_CNT_ERR; 46 | } 47 | return OK; 48 | } 49 | 50 | int doRTCSet(int argc, char *argv[]); 51 | const CliCmdType CMD_RTC_SET = 52 | { 53 | "rtcwr", 54 | 2, 55 | &doRTCSet, 56 | "\trtcwr: Set the internal RTC date and time(mm/dd/yy hh:mm:ss)\n", 57 | "\tUsage: megaind rtcwr
\n", 58 | "", 59 | "\tExample: megaind 0 rtcwr 9 15 20 21 43 15; Set the internal RTC time and date on Board #0 at Sept/15/2020 21:43:15\n"}; 60 | 61 | int doRTCSet(int argc, char *argv[]) 62 | { 63 | int dev = 0; 64 | u8 buff[7]; 65 | int i = 0; 66 | 67 | dev = doBoardInit(atoi(argv[1])); 68 | if (dev <= 0) 69 | { 70 | return ERROR; 71 | } 72 | 73 | if (argc == 9) 74 | { 75 | i = atoi(argv[3]); //month 76 | if (i < 1 || i > 12) 77 | { 78 | printf("Invalid month!\n"); 79 | return ERROR; 80 | } 81 | buff[1] = i; 82 | i = atoi(argv[4]); 83 | if (i < 1 || i > 31) 84 | { 85 | printf("Invalid date!\n"); 86 | return ERROR; 87 | } 88 | buff[2] = i; 89 | i = atoi(argv[5]); 90 | if (i < 0 || i > 99) 91 | { 92 | printf("Invalid year!\n"); 93 | return ERROR; 94 | } 95 | buff[0] = i; 96 | i = atoi(argv[6]); 97 | if (i < 0 || i > 23) 98 | { 99 | printf("Invalid hour!\n"); 100 | return ERROR; 101 | } 102 | buff[3] = i; 103 | i = atoi(argv[7]); 104 | if (i < 0 || i > 59) 105 | { 106 | printf("Invalid minute!\n"); 107 | return ERROR; 108 | } 109 | buff[4] = i; 110 | i = atoi(argv[8]); 111 | if (i < 0 || i > 59) 112 | { 113 | printf("Invalid second!\n"); 114 | return ERROR; 115 | } 116 | buff[5] = i; 117 | buff[6] = CALIBRATION_KEY; 118 | if (OK != i2cMem8Write(dev, I2C_RTC_SET_YEAR_ADD, buff, 7)) 119 | { 120 | printf("Fail to read RTC!\n"); 121 | return ERROR; 122 | } 123 | printf("done\n"); 124 | } 125 | else 126 | { 127 | return ARG_CNT_ERR; 128 | } 129 | return OK; 130 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 17 | -------------------------------------------------------------------------------- /src/wdt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "megaind.h" 8 | #include "comm.h" 9 | 10 | 11 | int doWdtReload(int argc, char *argv[]); 12 | const CliCmdType CMD_WDT_RELOAD = 13 | { 14 | "wdtr", 15 | 2, 16 | &doWdtReload, 17 | "\twdtr: Reload the watchdog timer and enable the watchdog if is disabled\n", 18 | "\tUsage: megaind wdtr\n", 19 | "", 20 | "\tExample: megaind 0 wdtr; Reload the watchdog timer on Board #0 with the period \n"}; 21 | 22 | int doWdtReload(int argc, char *argv[]) 23 | { 24 | int dev = 0; 25 | u8 buff[2] = 26 | { 27 | 0, 28 | 0}; 29 | 30 | dev = doBoardInit(atoi(argv[1])); 31 | if (dev <= 0) 32 | { 33 | return ERROR; 34 | } 35 | 36 | if (argc == 3) 37 | { 38 | buff[0] = WDT_RESET_SIGNATURE; 39 | if (OK != i2cMem8Write(dev, I2C_MEM_WDT_RESET_ADD, buff, 1)) 40 | { 41 | printf("Fail to write watchdog reset key!\n"); 42 | return ERROR; 43 | } 44 | } 45 | else 46 | { 47 | return ARG_CNT_ERR; 48 | } 49 | return OK; 50 | } 51 | 52 | int doWdtSetPeriod(int argc, char *argv[]); 53 | const CliCmdType CMD_WDT_SET_PERIOD = 54 | { 55 | "wdtpwr", 56 | 2, 57 | &doWdtSetPeriod, 58 | "\twdtpwr: Set the watchdog period in seconds, \n\t\t\treload command must be issue in this interval to prevent Raspberry Pi power off\n", 59 | "\tUsage: megaind wdtpwr \n", 60 | "", 61 | "\tExample: megaind 0 wdtpwr 10; Set the watchdog timer period on Board #0 at 10 seconds \n"}; 62 | 63 | int doWdtSetPeriod(int argc, char *argv[]) 64 | { 65 | int dev = 0; 66 | u16 period; 67 | u8 buff[2] = 68 | { 69 | 0, 70 | 0}; 71 | 72 | dev = doBoardInit(atoi(argv[1])); 73 | if (dev <= 0) 74 | { 75 | return ERROR; 76 | } 77 | 78 | if (argc == 4) 79 | { 80 | period = (u16)atoi(argv[3]); 81 | if (0 == period) 82 | { 83 | printf("Invalid period!\n"); 84 | return ERROR; 85 | } 86 | memcpy(buff, &period, 2); 87 | if (OK != i2cMem8Write(dev, I2C_MEM_WDT_INTERVAL_SET_ADD, buff, 2)) 88 | { 89 | printf("Fail to write watchdog period!\n"); 90 | return ERROR; 91 | } 92 | } 93 | else 94 | { 95 | return ARG_CNT_ERR; 96 | } 97 | return OK; 98 | } 99 | 100 | int doWdtGetPeriod(int argc, char *argv[]); 101 | const CliCmdType CMD_WDT_GET_PERIOD = 102 | { 103 | "wdtprd", 104 | 2, 105 | &doWdtGetPeriod, 106 | "\twdtprd: Get the watchdog period in seconds, \n\t\t\treload command must be issue in this interval to prevent Raspberry Pi power off\n", 107 | "\tUsage: megaind wdtprd \n", 108 | "", 109 | "\tExample: megaind 0 wdtprd; Get the watchdog timer period on Board #0\n"}; 110 | 111 | int doWdtGetPeriod(int argc, char *argv[]) 112 | { 113 | int dev = 0; 114 | u16 period; 115 | u8 buff[2] = 116 | { 117 | 0, 118 | 0}; 119 | 120 | dev = doBoardInit(atoi(argv[1])); 121 | if (dev <= 0) 122 | { 123 | return ERROR; 124 | } 125 | 126 | if (argc == 3) 127 | { 128 | if (OK != i2cMem8Read(dev, I2C_MEM_WDT_INTERVAL_GET_ADD, buff, 2)) 129 | { 130 | printf("Fail to read watchdog period!\n"); 131 | return ERROR; 132 | } 133 | memcpy(&period, buff, 2); 134 | printf("%d\n", (int)period); 135 | } 136 | else 137 | { 138 | return ARG_CNT_ERR; 139 | } 140 | return OK; 141 | } 142 | 143 | int doWdtSetInitPeriod(int argc, char *argv[]); 144 | const CliCmdType CMD_WDT_SET_INIT_PERIOD = 145 | { 146 | "wdtipwr", 147 | 2, 148 | &doWdtSetInitPeriod, 149 | "\twdtipwr: Set the watchdog initial period in seconds, \n\t\t\tThis period is loaded after power cycle, giving Raspberry time to boot\n", 150 | "\tUsage: megaind wdtipwr \n", 151 | "", 152 | "\tExample: megaind 0 wdtipwr 10; Set the watchdog timer initial period on Board #0 at 10 seconds \n"}; 153 | 154 | int doWdtSetInitPeriod(int argc, char *argv[]) 155 | { 156 | int dev = 0; 157 | u16 period; 158 | u8 buff[2] = 159 | { 160 | 0, 161 | 0}; 162 | 163 | dev = doBoardInit(atoi(argv[1])); 164 | if (dev <= 0) 165 | { 166 | return ERROR; 167 | } 168 | 169 | if (argc == 4) 170 | { 171 | period = (u16)atoi(argv[3]); 172 | if (0 == period) 173 | { 174 | printf("Invalid period!\n"); 175 | return ERROR; 176 | } 177 | memcpy(buff, &period, 2); 178 | if (OK != i2cMem8Write(dev, I2C_MEM_WDT_INIT_INTERVAL_SET_ADD, buff, 2)) 179 | { 180 | printf("Fail to write watchdog period!\n"); 181 | return ERROR; 182 | } 183 | } 184 | else 185 | { 186 | return ARG_CNT_ERR; 187 | } 188 | return OK; 189 | } 190 | 191 | int doWdtGetInitPeriod(int argc, char *argv[]); 192 | const CliCmdType CMD_WDT_GET_INIT_PERIOD = 193 | { 194 | "wdtiprd", 195 | 2, 196 | &doWdtGetInitPeriod, 197 | "\twdtiprd: Get the watchdog initial period in seconds. \n\t\t\tThis period is loaded after power cycle, giving Raspberry time to boot\n", 198 | "\tUsage: megaind wdtiprd \n", 199 | "", 200 | "\tExample: megaind 0 wdtiprd; Get the watchdog timer initial period on Board #0\n"}; 201 | 202 | int doWdtGetInitPeriod(int argc, char *argv[]) 203 | { 204 | int dev = 0; 205 | u16 period; 206 | u8 buff[2] = 207 | { 208 | 0, 209 | 0}; 210 | 211 | dev = doBoardInit(atoi(argv[1])); 212 | if (dev <= 0) 213 | { 214 | return ERROR; 215 | } 216 | 217 | if (argc == 3) 218 | { 219 | if (OK != i2cMem8Read(dev, I2C_MEM_WDT_INIT_INTERVAL_GET_ADD, buff, 2)) 220 | { 221 | printf("Fail to read watchdog period!\n"); 222 | return ERROR; 223 | } 224 | memcpy(&period, buff, 2); 225 | printf("%d\n", (int)period); 226 | } 227 | else 228 | { 229 | return ARG_CNT_ERR; 230 | } 231 | return OK; 232 | } 233 | 234 | int doWdtSetOffPeriod(int argc, char *argv[]); 235 | const CliCmdType CMD_WDT_SET_OFF_PERIOD = 236 | { 237 | "wdtopwr", 238 | 2, 239 | &doWdtSetOffPeriod, 240 | "\twdtopwr: Set the watchdog off period in seconds (max 48 days). \n\t\t\tThis is the time that watchdog mantain Raspberry turned off \n", 241 | "\tUsage: megaind wdtopwr \n", 242 | "", 243 | "\tExample: megaind 0 wdtopwr 10; Set the watchdog off interval on Board #0 at 10 seconds \n"}; 244 | 245 | int doWdtSetOffPeriod(int argc, char *argv[]) 246 | { 247 | int dev = 0; 248 | u32 period; 249 | u8 buff[4] = 250 | { 251 | 0, 252 | 0, 253 | 0, 254 | 0}; 255 | 256 | dev = doBoardInit(atoi(argv[1])); 257 | if (dev <= 0) 258 | { 259 | return ERROR; 260 | } 261 | 262 | if (argc == 4) 263 | { 264 | period = (u32)atoi(argv[3]); 265 | if ( (0 == period) || (period > WDT_MAX_OFF_INTERVAL_S)) 266 | { 267 | printf("Invalid period!\n"); 268 | return ERROR; 269 | } 270 | memcpy(buff, &period, 4); 271 | if (OK 272 | != i2cMem8Write(dev, I2C_MEM_WDT_POWER_OFF_INTERVAL_SET_ADD, buff, 4)) 273 | { 274 | printf("Fail to write watchdog period!\n"); 275 | return ERROR; 276 | } 277 | } 278 | else 279 | { 280 | return ARG_CNT_ERR; 281 | } 282 | return OK; 283 | } 284 | 285 | int doWdtGetOffPeriod(int argc, char *argv[]); 286 | const CliCmdType CMD_WDT_GET_OFF_PERIOD = 287 | { 288 | "wdtoprd", 289 | 2, 290 | &doWdtGetOffPeriod, 291 | "\twdtoprd: Get the watchdog off period in seconds (max 48 days) \n\t\t\tThis is the time that watchdog mantain Raspberry turned off \n", 292 | "\tUsage: megaind wdtoprd \n", 293 | "", 294 | "\tExample: megaind 0 wdtoprd; Get the watchdog off period on Board #0\n"}; 295 | 296 | int doWdtGetOffPeriod(int argc, char *argv[]) 297 | { 298 | int dev = 0; 299 | u32 period; 300 | u8 buff[4] = 301 | { 302 | 0, 303 | 0, 304 | 0, 305 | 0}; 306 | 307 | dev = doBoardInit(atoi(argv[1])); 308 | if (dev <= 0) 309 | { 310 | return ERROR; 311 | } 312 | 313 | if (argc == 3) 314 | { 315 | if (OK 316 | != i2cMem8Read(dev, I2C_MEM_WDT_POWER_OFF_INTERVAL_GET_ADD, buff, 4)) 317 | { 318 | printf("Fail to read watchdog period!\n"); 319 | return ERROR; 320 | } 321 | memcpy(&period, buff, 4); 322 | printf("%d\n", (int)period); 323 | } 324 | else 325 | { 326 | return ARG_CNT_ERR; 327 | } 328 | return OK; 329 | 330 | } 331 | 332 | int doWdtGetResetCount(int argc, char *argv[]); 333 | const CliCmdType CMD_WDT_GET_RESETS_COUNT = 334 | { 335 | "wdtrcrd", 336 | 2, 337 | &doWdtGetResetCount, 338 | "\twdtrcrd: Get the watchdog numbers of performed repowers\n", 339 | "\tUsage: megaind wdtrcrd \n", 340 | "", 341 | "\tExample: megaind 0 wdtrcrd; Get the watchdog reset count on Board #0\n"}; 342 | 343 | int doWdtGetResetCount(int argc, char *argv[]) 344 | { 345 | int dev = 0; 346 | u16 period; 347 | u8 buff[2] = 348 | { 349 | 0, 350 | 0}; 351 | 352 | dev = doBoardInit(atoi(argv[1])); 353 | if (dev <= 0) 354 | { 355 | return ERROR; 356 | } 357 | 358 | if (argc == 3) 359 | { 360 | if (OK != i2cMem8Read(dev, I2C_MEM_WDT_RESET_COUNT_ADD, buff, 2)) 361 | { 362 | printf("Fail to read watchdog reset count!\n"); 363 | return ERROR; 364 | } 365 | memcpy(&period, buff, 2); 366 | printf("%d\n", (int)period); 367 | } 368 | else 369 | { 370 | return ARG_CNT_ERR; 371 | } 372 | return OK; 373 | } 374 | 375 | 376 | int doWdtClearResets(int argc, char *argv[]); 377 | const CliCmdType CMD_WDT_CLR_RESETS_COUNT = 378 | { 379 | "wdtrcclr", 380 | 2, 381 | &doWdtClearResets, 382 | "\twdtrcclr: Clear the resets count\n", 383 | "\tUsage: megaind wdtrcclr\n", 384 | "", 385 | "\tExample: megaind 0 wdtrcclr -> Clear the watchdog resets count on Board #0\n"}; 386 | 387 | int doWdtClearResets(int argc, char *argv[]) 388 | { 389 | int dev = 0; 390 | u8 buff[2] = 391 | { 392 | 0, 393 | 0}; 394 | 395 | dev = doBoardInit(atoi(argv[1])); 396 | if (dev <= 0) 397 | { 398 | return ERROR; 399 | } 400 | 401 | if (argc == 3) 402 | { 403 | buff[0] = RESET_CALIBRATION_KEY; 404 | if (OK != i2cMem8Write(dev, I2C_MEM_WDT_CLEAR_RESET_COUNT_ADD, buff, 1)) 405 | { 406 | printf("Fail to clear the reset count!\n"); 407 | return ERROR; 408 | } 409 | } 410 | else 411 | { 412 | return ARG_CNT_ERR; 413 | } 414 | return OK; 415 | } -------------------------------------------------------------------------------- /update/README.md: -------------------------------------------------------------------------------- 1 | # update 2 | 3 | This is the [Sequent Microsystems](https://www.sequentmicrosystems.com) Industrial Automation Systems board firmware update tool. 4 | 5 | ## Usage 6 | 7 | ```bash 8 | ~$ git clone https://github.com/SequentMicrosystems/megaind-rpi.git 9 | ~$ cd megaind-rpi/update/ 10 | ~/megaind-rpi/update$ ./update 0 11 | ``` 12 | For 64-bit OS use the program located in the ```ubuntu/``` directory. 13 | 14 | If you clone the repository already, skip the first step. 15 | The command will download the newest firmware version from our server and write it to the board. 16 | The stack level of the board must be provided as a parameter. 17 | 18 | During firmware updates, we strongly recommend to: 19 | - disconnecting all outputs from the board since they can change state unpredictably. 20 | - Stop any script/program/Node-RED flow that accesses the I2C port 21 | -------------------------------------------------------------------------------- /update/ubuntu/update: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/megaind-rpi/652448333b5c9d04be1ffd88889b6e10751076aa/update/ubuntu/update -------------------------------------------------------------------------------- /update/update: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SequentMicrosystems/megaind-rpi/652448333b5c9d04be1ffd88889b6e10751076aa/update/update --------------------------------------------------------------------------------