├── P1AM-100 Example Series ├── README.md ├── ES1_P1AM-100_MQTT_Call_And_Response │ ├── README.md │ └── ES1_P1AM-100_MQTT_Call_And_Response.ino └── ES2_P1AM-100_Analog_Webserver │ ├── README.md │ └── ES2_P1AM-100_Analog_Webserver.ino ├── P1AM-100_ModbusRTU_HMI ├── P1AM Driver Cmore Micro V4.51.mgp ├── README.md └── P1AM-100_ModbusRTU_Server_HMI.ino ├── P1AM-100_ModbusTCP_Server_HMI ├── P1AM Driver CmoreV6.52.eap9 ├── P1AM Driver Cmore Micro V4.40.mgp ├── README.md └── P1AM-100_ModbusTCP_Server_HMI.ino ├── P1AM-100_ModbusTCP_ModuleIO ├── P1AM-100_ModbusTCP_ModuleIO.ino └── README.md ├── P1AM-100_ModbusTCP_KitchenSink └── README.md ├── README.md ├── P1AM-100_RTC_NTP_SYNC ├── readme.md └── P1AM-100_RTC_NTP_SYNC.ino ├── P1AM-100_StateChangeDetectionModule ├── Readme.md └── P1AM-100_StateChangeDetectionModule.ino ├── P1AM-100_StateChangeDetectionSwitch ├── Readme.md └── P1AM-100_StateChangeDetectionSwitch.ino ├── P1AM-100_ModbusTCP_Client_Simple ├── Readme.md └── P1AM-100_ModbusTCP_Client_Simple.ino ├── P1AM-100_ModbusTCP_Client_Multiple ├── README.md └── P1AM-100_ModbusTCP_Client_Multiple.ino ├── P1AM-100_Datalogger ├── README.md └── P1AM-100_Datalogger.ino └── P1AM-100_IsBaseActive ├── README.md └── P1AM-100_IsBaseActive.ino /P1AM-100 Example Series/README.md: -------------------------------------------------------------------------------- 1 | Purpose: 2 | 3 | The examples in this section have been created to help customers use the P1AM-100 with ADC products. -------------------------------------------------------------------------------- /P1AM-100_ModbusRTU_HMI/P1AM Driver Cmore Micro V4.51.mgp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AutomationDirect/P1AM-Examples/HEAD/P1AM-100_ModbusRTU_HMI/P1AM Driver Cmore Micro V4.51.mgp -------------------------------------------------------------------------------- /P1AM-100_ModbusTCP_Server_HMI/P1AM Driver CmoreV6.52.eap9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AutomationDirect/P1AM-Examples/HEAD/P1AM-100_ModbusTCP_Server_HMI/P1AM Driver CmoreV6.52.eap9 -------------------------------------------------------------------------------- /P1AM-100_ModbusTCP_ModuleIO/P1AM-100_ModbusTCP_ModuleIO.ino: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AutomationDirect/P1AM-Examples/HEAD/P1AM-100_ModbusTCP_ModuleIO/P1AM-100_ModbusTCP_ModuleIO.ino -------------------------------------------------------------------------------- /P1AM-100_ModbusTCP_Server_HMI/P1AM Driver Cmore Micro V4.40.mgp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AutomationDirect/P1AM-Examples/HEAD/P1AM-100_ModbusTCP_Server_HMI/P1AM Driver Cmore Micro V4.40.mgp -------------------------------------------------------------------------------- /P1AM-100_ModbusTCP_KitchenSink/README.md: -------------------------------------------------------------------------------- 1 | This example has been renamed P1AM100_Modbus_TCP_Server_HMI & can be found at the below link along with all asscociated files: 2 | 3 | https://github.com/AutomationDirect/P1AM-Examples/tree/master/P1AM-100_ModbusTCP_Server_HMI 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # P1AM-Examples 2 | This is a collection of example code for the AutomationDirect P1AM-100. 3 | Some of the examples are generic to show you how to get started and the end user must modify for your application. AutomationDirect does not provide programming services for specific applications or for third party hardware. 4 | 5 | If an issue is found with any of the examples we provide please use the GitHub "Issue" tab within the example repository to report to AutomationDirect. 6 | -------------------------------------------------------------------------------- /P1AM-100_RTC_NTP_SYNC/readme.md: -------------------------------------------------------------------------------- 1 | ** Example: RTC NTP Sync ** 2 | 3 | Synchronizes internal RTC to Google's NTP server for up to date time 4 | Default Sync period in this code is once a day and the timezeone 5 | is set for Eastern Standard Time 6 | 7 | Based off of Arduino Udp NTP Client Example 8 | and Arduino RTCZero Examples 9 | 10 | Arduino Udp NTP Client Example 11 | created 4 Sep 2010 12 | by Michael Margolis 13 | modified 9 Apr 2012 14 | by Tom Igoe 15 | modified 4 Mar 2020 16 | by FACTS Engineering 17 | 18 | This code is in the public domain. 19 | -------------------------------------------------------------------------------- /P1AM-100_StateChangeDetectionModule/Readme.md: -------------------------------------------------------------------------------- 1 | P1AM-100 State change detection module (edge detection) 2 | 3 | This example shows how to detect when a P1AM input module 4 | changes from off to on and on to off. 5 | 6 | Often, you don't need to know the state of a digital input all the time, but 7 | you just need to know when the input changes from one state to another. 8 | For example, you want to know when a button goes from OFF to ON. This is called 9 | state change detection, or edge detection. 10 | 11 | The circuit: 12 | - P1-08SIM Input Simulator 13 | 14 | created 27 Sep 2005\ 15 | modified 30 Aug 2011\ 16 | by Tom Igoe 17 | 18 | modified 27 Feb 2020 19 | by AutomationDirect 20 | 21 | This example code is in the public domain.\ 22 | http://www.arduino.cc/en/Tutorial/ButtonStateChange 23 | 24 | Required Library which needs to be installed.\ 25 | https://github.com/facts-engineering/P1AM 26 | -------------------------------------------------------------------------------- /P1AM-100_StateChangeDetectionSwitch/Readme.md: -------------------------------------------------------------------------------- 1 | P1AM-100 State change detection switch (edge detection) 2 | 3 | This example shows how to detect when the P1AM-100 Toggle Swith 4 | changes from off to on and on to off. 5 | 6 | Often, you don't need to know the state of a digital input all the time, but 7 | you just need to know when the input changes from one state to another. 8 | For example, you want to know when a button goes from OFF to ON. This is called 9 | state change detection, or edge detection. 10 | 11 | The circuit: 12 | - Toggle Switch on P1AM-100 "SWITCH_BUILTIN" 13 | - LED attached to P1AM-100 "LED_BUILTIN" 14 | 15 | created 27 Sep 2005\ 16 | modified 30 Aug 2011\ 17 | by Tom Igoe 18 | 19 | modified 27 Feb 2020 20 | by AutomationDirect 21 | 22 | This example code is in the public domain.\ 23 | http://www.arduino.cc/en/Tutorial/ButtonStateChange 24 | 25 | Required Library which needs to be installed.\ 26 | https://github.com/facts-engineering/P1AM 27 | -------------------------------------------------------------------------------- /P1AM-100_ModbusTCP_Client_Simple/Readme.md: -------------------------------------------------------------------------------- 1 | Purpose: 2 | 3 | Modbus TCP Client Simple for P1AM-100/P1AM-ETH 4 | 5 | This sketch toggles the coil of a Modbus TCP server connected 6 | on and off every second. 7 | 8 | This example uses Serial.print() to display status information. 9 | The Serial Monitor must be running for the sketch to start. 10 | 11 | 12 | created 16 July 2018 13 | by Sandeep Mistry 14 | Modified by AutomationDirect for P1AM-100/P1AM-ETH 15 | 16 | Dependencies: 17 | 18 | https://github.com/arduino-libraries/ArduinoModbus \ 19 | https://github.com/arduino-libraries/ArduinoRS485 20 | 21 | *****The information provided in the AutomationDirect knowledge base is provided "as is" without warranty of any kind. AutomationDirect disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. In no event shall AutomationDirect or its suppliers be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages, even if AutomationDirect or its suppliers have been advised of the possibility of such damages. Some states do not allow the exclusion of limitation of liability for consequential or incidental damages so the foregoing limitation may not apply.***** 22 | -------------------------------------------------------------------------------- /P1AM-100_ModbusTCP_Client_Multiple/README.md: -------------------------------------------------------------------------------- 1 | PURPOSE: 2 | 3 | Modbus TCP Client Multiple for P1AM-100/P1AM-ETH 4 | 5 | This sketch increments a register of a Modbus TCP server. 6 | Multiple Clients can be created and maintained, but the Library 7 | code is blocking. So all delays in communictions will delay the 8 | main loop() execution. 9 | 10 | This example uses Serial.print() to display status information. 11 | The Serial Monitor must be running for the sketch to start. 12 | 13 | Dependencies: 14 | 15 | https://github.com/arduino-libraries/ArduinoModbus \ 16 | https://github.com/arduino-libraries/ArduinoRS485 17 | 18 | ******* The information provided in the AutomationDirect knowledge base is provided "as is" without warranty of any kind. AutomationDirect disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. In no event shall AutomationDirect or its suppliers be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages, even if AutomationDirect or its suppliers have been advised of the possibility of such damages. Some states do not allow the exclusion of limitation of liability for consequential or incidental damages so the foregoing limitation may not apply.****** 19 | -------------------------------------------------------------------------------- /P1AM-100_Datalogger/README.md: -------------------------------------------------------------------------------- 1 | PURPOSE: 2 | 3 | SD card datalogger for P1AM-100 4 | 5 | This example shows how to log data 6 | to an SD card using the SD library. 7 | 8 | The circuit: 9 | * The SD card uses an internal SPI bus that is not present on the header. 10 | * The SD library handles most of the pins, but you still need give it CS. 11 | ** CS - pin SDCARD_SS_PIN(28) 12 | 13 | created 24 Nov 2010 14 | modified 9 Apr 2012 15 | by Tom Igoe 16 | 17 | Modified by AutomationDirect for P1AM-100 18 | 19 | This example code is in the public domain.\ 20 | http://www.arduino.cc/en/Tutorial/ButtonStateChange 21 | 22 | Required Library which needs to be installed.\ 23 | https://github.com/facts-engineering/P1AM 24 | 25 | No Dependencies 26 | 27 | ****The information provided in the AutomationDirect knowledge base is provided "as is" without warranty of any kind. AutomationDirect disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. In no event shall AutomationDirect or its suppliers be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages, even if AutomationDirect or its suppliers have been advised of the possibility of such damages. Some states do not allow the exclusion of limitation of liability for consequential or incidental damages so the foregoing limitation may not apply.**** -------------------------------------------------------------------------------- /P1AM-100_ModbusTCP_ModuleIO/README.md: -------------------------------------------------------------------------------- 1 | PURPOSE: 2 | 3 | Ethernet Modbus TCP Server with P1 IO Modules 4 | for use with the P1AM-ETH module. 5 | 6 | This sketch creates a Modbus TCP Server for single connection. 7 | Slot1 Inputs are mapped to Modbus Input Bits 100001 to 100008. 8 | Slot2 Outputs are controlled by Modbus Coil Bits 000001 to 000008. 9 | Modbus Holding Register 400001 is always incrementing. 10 | 11 | The P1AM-100 cpu will require external 24vdc power 12 | for the IO modules to function. This example uses 13 | Serial.print() to display status information. The 14 | Serial Monitor must be running for the sketch to start. 15 | 16 | DEPENDENCIES: 17 | 18 | https://github.com/arduino-libraries/ArduinoModbus \ 19 | https://github.com/arduino-libraries/ArduinoRS485 20 | 21 | ******The information provided in the AutomationDirect knowledge base is provided "as is" without warranty of any kind. AutomationDirect disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. In no event shall AutomationDirect or its suppliers be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages, even if AutomationDirect or its suppliers have been advised of the possibility of such damages. Some states do not allow the exclusion of limitation of liability for consequential or incidental damages so the foregoing limitation may not apply.***** 22 | -------------------------------------------------------------------------------- /P1AM-100_ModbusTCP_Server_HMI/README.md: -------------------------------------------------------------------------------- 1 | PURPOSE: 2 | 3 | Modbus TCP Server for P1AM-ETH with access to P1 Input/Output Modules 4 | 5 | This sketch creates a Modbus TCP Server for up to 8 connections. 6 | Slot1 Inputs are mapped to Modbus Input Bits 100001 to 100008. 7 | Slot2 Outputs are controlled by Modbus Coil Bits 000001 to 000008. 8 | Modbus Holding Register 400001 is always incrementing. 9 | 10 | 1.) The included EAP9 file can be opened with the C-more Programming Software (EA9-PGMSW ver. 6.52) for use with a C-more HMI Operator Panel. 11 | 12 | 2.) The included EA3 .MGP file can be opened with the C-more Micro Programming Software (EA-MG-PGMSW ver. 4.40) for use with a C-micro HMI Operator Panel. 13 | 14 | The P1AM-100 cpu will require external 24vdc power for the IO modules to function. This example uses Serial.print() to display status information. The 15 | Serial Monitor must be running for the sketch to start. 16 | 17 | 18 | DEPENDENCIES: 19 | 20 | https://github.com/arduino-libraries/ArduinoModbus 21 | https://github.com/arduino-libraries/ArduinoRS485 22 | 23 | ******The information provided in the AutomationDirect knowledge base is provided "as is" without warranty of any kind. AutomationDirect disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. In no event shall AutomationDirect or its suppliers be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages, even if AutomationDirect or its suppliers have been advised of the possibility of such damages. Some states do not allow the exclusion of limitation of liability for consequential or incidental damages so the foregoing limitation may not apply.***** 24 | 25 | -------------------------------------------------------------------------------- /P1AM-100 Example Series/ES1_P1AM-100_MQTT_Call_And_Response/README.md: -------------------------------------------------------------------------------- 1 | Purpose: 2 | 3 | This example uses an MQTT broker to update the outputs of a P1-08TRS the inputs of a P1-08SIM. You can easily switch these out for any discrete input or output module. 4 | 5 | I used shiftr.io for this example. This is a free MQTT broker that provides visualisation and is great for testing. If you want to use a different broker, update the broker string to the proper URL and update any login credentials. 6 | 7 | Both the sending and receiving portions are included in this code, but these could be separated out into separate P1AM units or web interfaces. 8 | 9 | With a little bit of modification, you can turn this example into a remote monitoring or combine it with Modbus TCP and connect an aging PLC to the cloud. 10 | 11 | Dependencies: 12 | 13 | P1AM - In the library manager or or on GitHub: https://github.com/facts-engineering/P1AM 14 | ArduinoMqttClient - In the library manager or on GitHub: https://github.com/arduino-libraries/ArduinoMqttClient 15 | 16 | Products Used: 17 | P1AM-100 18 | P1AM-ETH 19 | P1-08SIM 20 | P1-08TRS 21 | 22 | ****The information provided in the AutomationDirect knowledge base is provided "as is" without warranty of any kind. AutomationDirect disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. In no event shall AutomationDirect or its suppliers be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages, even if AutomationDirect or its suppliers have been advised of the possibility of such damages. Some states do not allow the exclusion of limitation of liability for consequential or incidental damages so the foregoing limitation may not apply.**** -------------------------------------------------------------------------------- /P1AM-100_ModbusRTU_HMI/README.md: -------------------------------------------------------------------------------- 1 | PURPOSE: 2 | 3 | Modbus RTU Server for P1AM-SERIAL with access to P1 Input/Output Modules 4 | 5 | This sketch creates a Modbus RTU Server (slave ID 10) on port 2 of the P1AM-SERIAL shield. Port 2 is configured as RS232, 9600 baud rate, 8 data bits, even parity, and 1 stop bit. 6 | 7 | Slot1 Inputs are mapped to Modbus Input Bits 100001 to 100008. Slot2 Outputs are controlled by Modbus Coil Bits 000001 to 000008. Modbus Holding Register 400001 increments every second (or 1000 milliseconds). 8 | 9 | Commented out example code is also included for an analog input module at Slot 3 mapped to Modbus Input Registers 30001 to 30004 and an analog output module at Slot 4 mapped to Modbus Holding Registers 40003 to 40006. 10 | 11 | The included EA3 .MGP file can be opened with the C-more Micro Programming Software (EA-MG-PGMSW ver. 4.51) for use with a C-micro HMI Operator Panel. 12 | 13 | The P1AM-100 cpu will require external 24vdc power for the IO modules to function. This example uses Serial.print() to display status information. The Serial Monitor must be running for the sketch to start. 14 | 15 | DEPENDENCIES: 16 | 17 | https://github.com/facts-engineering/P1AM_Serial 18 | 19 | *The information provided in the AutomationDirect knowledge base is provided "as is" without warranty of any kind. AutomationDirect disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. In no event shall AutomationDirect or its suppliers be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages, even if AutomationDirect or its suppliers have been advised of the possibility of such damages. Some states do not allow the exclusion of limitation of liability for consequential or incidental damages so the foregoing limitation may not apply. -------------------------------------------------------------------------------- /P1AM-100_IsBaseActive/README.md: -------------------------------------------------------------------------------- 1 | PURPOSE: 2 | 3 | This example will use the "P1.isBaseActive" functions to demonstrate how the Base Controller 4 | can be monitored for a loss of 24V power. When this occurs, the Base Controller will need to be 5 | re-initialized using P1.init(). USB will need to be connected for serial communication and power. 6 | 7 | This example works with all P1000 Series: 8 | - Discrete output modules such as P1-08TRS, P1-08TD1, P1-16TR, etc. 9 | - Discrete combo modules such as P1-15CDD1, P1-15CDD2, P1-16CDR, etc. 10 | 11 | This example will toggle channel 2 of slot 1 on and off every second after a base reset. 12 | 13 | To detect a base failure and re-init: 14 | - 1: Load program in current form. 15 | - 2: Connect USB cable from PC to P1AM-100. 16 | - 3: Remove 24Vdc power and reapply. *Note: Open serial Monitor. You should have no errors. 17 | - 4: Verify Slot 1 Channel 2 output is blinking. 18 | 19 | 20 | 21 | To see a failure: 22 | - 5: Comment out lines 55-62. 23 | - 6: Upload to the P1AM-100. 24 | - 7: Remove 24Vdc power & reapply.*Note: Open serial Monitor you will get a "Base Sync Timeout" error. 25 | - 8: The output on Slot 1 Channel 2 should not be blinking. 26 | 27 | It will also provide error feedback to the serial monitor on loss of 24Vdc. 28 | 29 | *The information provided in the AutomationDirect knowledge base is provided "as is" without warranty of any kind. AutomationDirect disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. In no event shall AutomationDirect or its suppliers be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages, even if AutomationDirect or its suppliers have been advised of the possibility of such damages. Some states do not allow the exclusion of limitation of liability for consequential or incidental damages so the foregoing limitation may not apply. 30 | -------------------------------------------------------------------------------- /P1AM-100 Example Series/ES2_P1AM-100_Analog_Webserver/README.md: -------------------------------------------------------------------------------- 1 | Purpose: 2 | 3 | This example uses the P1AM-ETH shield to create a webserver that displays the input values of a P1-04ADL02 to a webpage. 4 | You can visit this webpage by entering it's IP address in a browser. the default address is 192.168.1.177. 5 | This page will display the readings of all 4 channels in volts and in counts. 6 | 7 | You can find information on the P1-04ADL-2 and other modules here: https://facts-engineering.github.io/...1-04ADL-2.html 8 | 9 | The analog input module we used is the P1-04ADL-2, though you can easily use any analog or discrete module as long as you tweak 10 | the conversions for your range and resolution. 11 | If you use another input module you may find our configuration tool handy for setting ranges: https://facts-engineering.github.io/config.html 12 | 13 | This example can easily be changed to display multiple modules and their data. This is great for any sort of remote monitoring you might want. 14 | If you're comfortable with basic web development you can spruce up the website and add plenty of features of your own design. 15 | 16 | Dependencies: 17 | 18 | P1AM - In the library manager or or on GitHub: https://github.com/facts-engineering/P1AM 19 | 20 | Products Used: 21 | P1AM-100 22 | P1AM-ETH 23 | P1-04ADL-2 24 | 25 | 26 | ****The information provided in the AutomationDirect knowledge base is provided "as is" without warranty of any kind. AutomationDirect disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. In no event shall AutomationDirect or its suppliers be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages, even if AutomationDirect or its suppliers have been advised of the possibility of such damages. Some states do not allow the exclusion of limitation of liability for consequential or incidental damages so the foregoing limitation may not apply.**** -------------------------------------------------------------------------------- /P1AM-100_Datalogger/P1AM-100_Datalogger.ino: -------------------------------------------------------------------------------- 1 | /* 2 | SD card datalogger for P1AM-100 3 | 4 | This example shows how to log data 5 | to an SD card using the SD library. 6 | 7 | The circuit: 8 | * The SD card uses an internal SPI bus that is not present on the header. 9 | * The SD library handles most of the pins, but you still need give it CS. 10 | ** CS - pin SDCARD_SS_PIN(28) 11 | 12 | created 24 Nov 2010 13 | modified 9 Apr 2012 14 | by Tom Igoe 15 | Modified by AutomationDirect for P1AM-100 16 | 17 | This example code is in the public domain. 18 | */ 19 | 20 | 21 | #include 22 | 23 | const int chipSelect = SDCARD_SS_PIN; 24 | int myInt1=0; 25 | int myInt2=0; 26 | int myInt3=0; 27 | 28 | void setup() { 29 | // Open serial communications and wait for port to open: 30 | Serial.begin(115200); 31 | while (!Serial) { 32 | ; // wait for serial port to connect. Needed for native USB port only 33 | } 34 | SD_Begin(); 35 | } 36 | 37 | void loop() { 38 | // make a string for assembling the data to log: 39 | String dataString = ""; 40 | 41 | delay(1000); 42 | myInt1+=1; 43 | myInt2+=10; 44 | myInt3+=100; 45 | dataString += String(myInt1); 46 | dataString += ","; 47 | dataString += String(myInt2); 48 | dataString += ","; 49 | dataString += String(myInt3); 50 | 51 | // open the file. note that only one file can be open at a time, 52 | // so you have to close this one before opening another. 53 | File dataFile = SD.open("datalog.csv", FILE_WRITE); 54 | 55 | // if the file is available, write to it: 56 | if (dataFile) { 57 | dataFile.println(dataString); 58 | dataFile.close(); 59 | // print to the serial port too: 60 | Serial.println(dataString); 61 | } 62 | // if the file isn't open, pop up an error: 63 | else { 64 | Serial.println("error opening datalog.csv"); 65 | SD_Begin(); 66 | } 67 | } 68 | 69 | bool SD_Begin(void){ 70 | Serial.print("Initializing SD card..."); 71 | if (!SD.begin(chipSelect)) { 72 | Serial.println("Card failed, or not present"); 73 | return(0); 74 | } 75 | else { 76 | Serial.println("card initialized."); 77 | return(1); 78 | } 79 | } -------------------------------------------------------------------------------- /P1AM-100_StateChangeDetectionModule/P1AM-100_StateChangeDetectionModule.ino: -------------------------------------------------------------------------------- 1 | /* 2 | P1AM-100 State change detection module (edge detection) 3 | 4 | This example shows how to detect when a P1AM input module 5 | changes from off to on and on to off. 6 | 7 | Often, you don't need to know the state of a digital input all the time, but 8 | you just need to know when the input changes from one state to another. 9 | For example, you want to know when a button goes from OFF to ON. This is called 10 | state change detection, or edge detection. 11 | 12 | The circuit: 13 | - P1-08SIM Input Simulator 14 | 15 | created 27 Sep 2005\ 16 | modified 30 Aug 2011\ 17 | by Tom Igoe 18 | 19 | modified 27 Feb 2020 20 | by AutomationDirect 21 | 22 | This example code is in the public domain. 23 | http://www.arduino.cc/en/Tutorial/ButtonStateChange 24 | 25 | Required Library which needs to be installed. 26 | https://github.com/facts-engineering/P1AM 27 | */ 28 | 29 | #include //Needed for the Pin definitions 30 | 31 | int newInputs = 0; // current state of input Simulator switches 32 | bool newInputBits[8] = {}; // store the inputs into a boolean array 33 | bool oldInputBits[8] = {}; // copy of previous states 34 | 35 | void setup() { 36 | Serial.begin(115200); //initialize serial communication at 115200 bits per second 37 | while (!P1.init()){ 38 | ; //Wait for Modules to Sign on 39 | } 40 | } 41 | 42 | void loop() { 43 | newInputs = P1.readDiscrete(1); //Read the state of Slot 1, bits stored into a word. 44 | for(int i=0;i<8;i++){ //unpack from int to boolean array 45 | int testBit = 1 << i; //create bit mask 46 | newInputBits[i]=newInputs & testBit; //copy from bit of word into array 47 | } 48 | 49 | for(int i=0;i<8;i++){ //scan for differences between old and new states. 50 | if(newInputBits[i] != oldInputBits[i]){ 51 | if (newInputBits[i]) { 52 | // if the current state is HIGH then the input went from off to on: 53 | Serial.print(i+1); 54 | Serial.println(" On"); 55 | } else { 56 | // if the current state is LOW then the input went from on to off: 57 | Serial.print(i+1); 58 | Serial.println(" Off"); 59 | } 60 | } 61 | } 62 | memcpy(oldInputBits,newInputBits,8); //Copy new states to old states. 63 | } 64 | -------------------------------------------------------------------------------- /P1AM-100_StateChangeDetectionSwitch/P1AM-100_StateChangeDetectionSwitch.ino: -------------------------------------------------------------------------------- 1 | /* 2 | P1AM-100 State change detection switch (edge detection) 3 | 4 | This example shows how to detect when the P1AM-100 Toggle Swith 5 | changes from off to on and on to off. 6 | 7 | Often, you don't need to know the state of a digital input all the time, but 8 | you just need to know when the input changes from one state to another. 9 | For example, you want to know when a button goes from OFF to ON. This is called 10 | state change detection, or edge detection. 11 | 12 | The circuit: 13 | - Toggle Switch on P1AM-100 "SWITCH_BUILTIN" 14 | - LED attached to P1AM-100 "LED_BUILTIN" 15 | 16 | created 27 Sep 2005\ 17 | modified 30 Aug 2011\ 18 | by Tom Igoe 19 | 20 | modified 27 Feb 2020 21 | by AutomationDirect 22 | 23 | This example code is in the public domain. 24 | http://www.arduino.cc/en/Tutorial/ButtonStateChange 25 | 26 | Required Library which needs to be installed. 27 | https://github.com/facts-engineering/P1AM 28 | */ 29 | 30 | #include //Needed for the Pin definitions 31 | 32 | int newSwitchState = 0; // current state of the switch 33 | int oldSwitchState = 0; // previous state of the switch 34 | 35 | void setup() { 36 | Serial.begin(115200); //initialize serial communication at 115200 bits per second 37 | pinMode(LED_BUILTIN,OUTPUT); //Set our LED (Pin 32) to be an output 38 | pinMode(SWITCH_BUILTIN,INPUT);//Set our Switch (Pin 31) to be an input 39 | } 40 | 41 | 42 | void loop() { 43 | newSwitchState = digitalRead(SWITCH_BUILTIN);//Read the state of the switch 44 | 45 | // compare the buttonState to its previous state 46 | if (newSwitchState != oldSwitchState) { 47 | // if the state has changed, increment the counter 48 | if (newSwitchState == HIGH) { 49 | // if the current state is HIGH then the switch went from off to on: 50 | Serial.println("On"); 51 | digitalWrite(LED_BUILTIN,newSwitchState); //Update the LED 52 | } else { 53 | // if the current state is LOW then the switch went from on to off: 54 | Serial.println("Off"); 55 | digitalWrite(LED_BUILTIN,newSwitchState); //Update the LED 56 | } 57 | // Delay a little bit to avoid bouncing 58 | delay(50); 59 | } 60 | // save the current state as the last state, for next time through the loop 61 | oldSwitchState = newSwitchState; 62 | 63 | } 64 | -------------------------------------------------------------------------------- /P1AM-100_ModbusTCP_Client_Simple/P1AM-100_ModbusTCP_Client_Simple.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Modbus TCP Client Simple for P1AM-100/P1AM-ETH 3 | 4 | This sketch toggles the coil of a Modbus TCP server connected 5 | on and off every second. 6 | 7 | This example uses Serial.print() to display status information. 8 | The Serial Monitor must be running for the sketch to start. 9 | 10 | Required Libraries which need to be installed. 11 | https://github.com/arduino-libraries/ArduinoModbus 12 | https://github.com/arduino-libraries/ArduinoRS485 13 | 14 | created 16 July 2018 15 | by Sandeep Mistry 16 | Modified by AutomationDirect for P1AM-100/P1AM-ETH 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include // ArduinoModbus depends on the ArduinoRS485 library 23 | #include 24 | 25 | byte mac[] = { //Use the Reserved MAC printed on the right-side label of your P1AM-ETH. 26 | 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED 27 | }; 28 | IPAddress ip(10, 11, 0, 177);//IP Address of the P1AM-ETH module. 29 | 30 | EthernetClient client; 31 | ModbusTCPClient modbusTCPClient(client); 32 | 33 | IPAddress server(10, 11, 0, 22); //IP Addresses of the Server 34 | 35 | void setup() { 36 | Ethernet.begin(mac, ip); 37 | modbusTCPClient.setTimeout(500);//Adjust Response Timeout from 30 seconds to 500 ms. 38 | Serial.begin(115200); 39 | while (!Serial) {};// wait for serial port to connect. 40 | Serial.println("Modbus TCP Client Simple Example"); 41 | Serial.print("P1AM-ETH at IP:"); 42 | Serial.println(ip); 43 | } 44 | 45 | void loop() { 46 | 47 | if (!modbusTCPClient.connected()) {// client not connected, start the Modbus TCP client 48 | Serial.print("Attempting to connect to Modbus TCP server at IP:"); 49 | Serial.println(server); 50 | if (!modbusTCPClient.begin(server)) { 51 | Serial.println("Modbus TCP Client failed to connect!"); 52 | delay(1000); 53 | } else { 54 | Serial.println("Modbus TCP Client connected"); 55 | } 56 | } else {// client connected 57 | // write the value of 0x01, to the coil at address 0x00 58 | if (!modbusTCPClient.coilWrite(0x00, 0x01)) { 59 | Serial.print("Failed to write coil! "); 60 | Serial.println(modbusTCPClient.lastError()); 61 | } 62 | 63 | // wait for 1 second 64 | delay(1000); 65 | 66 | // write the value of 0x00, to the coil at address 0x00 67 | if (!modbusTCPClient.coilWrite(0x00, 0x00)) { 68 | Serial.print("Failed to write coil! "); 69 | Serial.println(modbusTCPClient.lastError()); 70 | } 71 | 72 | // wait for 1 second 73 | delay(1000); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /P1AM-100_IsBaseActive/P1AM-100_IsBaseActive.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Example: IsBaseActive 3 | 4 | This example will use the "P1.isBaseActive" functions to demonstrate how the Base Controller 5 | can be monitored for a loss of 24V power. When this occurs, the Base Controller will need to be 6 | re-initialized using P1.init(). USB will need to be connected for serial communication and power. 7 | 8 | This example works with all P1000 Series: 9 | - Discrete output modules such as P1-08TRS, P1-08TD1, P1-16TR, etc. 10 | - Discrete combo modules such as P1-15CDD1, P1-15CDD2, P1-16CDR, etc. 11 | 12 | This example will toggle channel 2 of slot 1 on and off every second after a base reset. 13 | 14 | //To detect a base failure and re-init 15 | Step 1: Load program in current form. 16 | Step 2: Connect USB cable from PC to P1AM-100. 17 | Step 3: Remove 24Vdc power and reapply. *Note: Open serial Monitor. You should have no errors. 18 | Step 4: Verify Slot 1 Channel 2 output is blinking. 19 | 20 | // To see a failure 21 | Step 5: Comment out lines 55-62. 22 | Step 6: Upload to the P1AM-100. 23 | Step 7: Remove 24Vdc power & reapply.*Note: Open serial Monitor you will get a "Base Sync Timeout" error. 24 | Step 8: The output on Slot 1 Channel 2 should not be blinking. 25 | 26 | It will also provide error feedback to the serial monitor on loss of 24Vdc. 27 | 28 | _____ _____ 29 | | P || S | 30 | | 1 || L | 31 | | A || O | 32 | | M || T | 33 | | - || | 34 | | 1 || 0 | 35 | | 0 || 1 | 36 | | 0 || | 37 | ¯¯¯¯¯ ¯¯¯¯¯ 38 | 39 | Written by FACTS Engineering and AutomationDirect. 40 | Copyright (c) 2020 FACTS Engineering, LLC 41 | Licensed under the MIT license. 42 | */ 43 | 44 | #include 45 | 46 | void setup(){ // the setup routine runs once: 47 | 48 | Serial.begin(115200); //initialize serial communication at 115200 bits per second 49 | while (!P1.init()){ 50 | ; //Wait for Modules to Sign on 51 | } 52 | } 53 | 54 | void loop(){ // the loop routine runs over and over again forever: 55 | if(P1.isBaseActive() == false){ 56 | Serial.println("Re-init() the base modules."); 57 | delay(10); 58 | while (!P1.init()){ 59 | Serial.println("Waiting for 24V"); 60 | delay(1000); 61 | } 62 | } 63 | P1.writeDiscrete(HIGH,1,2); //Turn slot 1 channel 2 on 64 | delay(1000); //wait 1 second 65 | P1.writeDiscrete(LOW,1,2); //Turn slot 1 channel 2 off 66 | delay(1000); //wait 1 second 67 | 68 | } 69 | -------------------------------------------------------------------------------- /P1AM-100_ModbusTCP_Client_Multiple/P1AM-100_ModbusTCP_Client_Multiple.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Modbus TCP Client Multiple for P1AM-100/P1AM-ETH 3 | 4 | This sketch increments a register of a Modbus TCP server. 5 | Multiple Clients can be created and maintained, but the Library 6 | code is blocking. So all delays in communictions will delay the 7 | main loop() execution. 8 | 9 | This example uses Serial.print() to display status information. 10 | The Serial Monitor must be running for the sketch to start. 11 | 12 | Required Libraries which need to be installed. 13 | https://github.com/arduino-libraries/ArduinoModbus 14 | https://github.com/arduino-libraries/ArduinoRS485 15 | */ 16 | 17 | #include 18 | #include 19 | 20 | #include // ArduinoModbus depends on the ArduinoRS485 library 21 | #include 22 | 23 | byte mac[] = { //Use the Reserved MAC printed on the right-side label of your P1AM-ETH. 24 | 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED 25 | }; 26 | IPAddress ip(10, 11, 0, 177);//IP Address of the P1AM-ETH module. 27 | int HR400001; 28 | int HR400002; 29 | 30 | EthernetClient clients[4]; 31 | ModbusTCPClient modbusTCPClient[4]={ 32 | ModbusTCPClient(clients[0]), 33 | ModbusTCPClient(clients[1]), 34 | ModbusTCPClient(clients[2]), 35 | ModbusTCPClient(clients[3]) 36 | }; 37 | 38 | IPAddress servers[4]={//IP Addresses of the Servers 39 | IPAddress(10, 11, 0, 22), 40 | IPAddress(10, 11, 0, 23), 41 | IPAddress(10, 11, 0, 24), 42 | IPAddress(10, 11, 0, 25) 43 | }; 44 | 45 | void setup() { 46 | Ethernet.begin(mac, ip); 47 | modbusTCPClient[0].setTimeout(500);//Adjust Response Timeout from 30 seconds to 500 ms. 48 | modbusTCPClient[1].setTimeout(500);//Adjust Response Timeout from 30 seconds to 500 ms. 49 | Serial.begin(115200);// wait for serial port to connect. 50 | while (!Serial) {};// wait for serial port to connect. 51 | Serial.println("Modbus TCP Client Multiple Example"); 52 | Serial.print("P1AM-ETH at IP:"); 53 | Serial.println(ip); 54 | } 55 | 56 | void loop() { 57 | 58 | if (!modbusTCPClient[0].connected()) {// client not connected, start the Modbus TCP client 59 | Serial.print("Attempting to connect to Modbus TCP server at IP:"); 60 | Serial.println(servers[0]); 61 | if (!modbusTCPClient[0].begin(servers[0])) { 62 | Serial.println("Modbus TCP Client failed to connect!"); 63 | } else { 64 | Serial.println("Modbus TCP Client connected"); 65 | } 66 | } else {// client connected 67 | if (!modbusTCPClient[0].holdingRegisterWrite(0x00, HR400001++)) { 68 | Serial.print("Failed to write! "); 69 | Serial.println(modbusTCPClient[0].lastError()); 70 | } 71 | } 72 | 73 | if (!modbusTCPClient[1].connected()) {// client not connected, start the Modbus TCP client 74 | Serial.print("Attempting to connect to Modbus TCP server at IP:"); 75 | Serial.println(servers[1]); 76 | if (!modbusTCPClient[1].begin(servers[1])) { 77 | Serial.println("Modbus TCP Client failed to connect!"); 78 | } else { 79 | Serial.println("Modbus TCP Client connected"); 80 | } 81 | } else {// client connected 82 | if (!modbusTCPClient[1].holdingRegisterWrite(0x01, HR400002--)) { 83 | Serial.print("Failed to write! "); 84 | Serial.println(modbusTCPClient[1].lastError()); 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /P1AM-100 Example Series/ES1_P1AM-100_MQTT_Call_And_Response/ES1_P1AM-100_MQTT_Call_And_Response.ino: -------------------------------------------------------------------------------- 1 | /* P1AM Example Series #1 - MQTT Call and Response 2 | * 3 | * This example uses an MQTT broker to update the outputs of a P1-08TRS 4 | * to the inputs of a P1-08SIM. You can easily switch these out for any 5 | * discrete input or output module. 6 | * 7 | * I used shiftr.io for this example. This is a free MQTT broker that provides 8 | * nice visualisation and is great for testing. If you want to use a different broker, 9 | * just update the broker string to the proper URL and update any login credentials 10 | * 11 | * Both the sending and receiving portions are included in this code, but these could 12 | * easily be separated out into separate P1AM units or web interfaces. 13 | * 14 | * With a little bit of modification, you can turn this example into a remote monitoring 15 | * solution or combine it with Modbus TCP and connect an aging PLC to the cloud. 16 | * 17 | * 18 | * 19 | * Written by FACTS Engineering 20 | * Copyright (c) 2019 FACTS Engineering, LLC 21 | * Licensed under the MIT license. 22 | */ 23 | 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | 30 | channelLabel TRS = {1,0}; //P1-08TRS Slot 1. Channel = 0 means we will be using the bitmapped method. 31 | channelLabel SIM = {2,0}; //P1-08SIM Slot 2. Channel = 0 means we will be using the bitmapped method. 32 | 33 | EthernetClient client; 34 | MqttClient mqttClient(client); 35 | 36 | byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // P1AM-ETH have unique MAC IDs on their product label 37 | const char broker[] = "broker.shiftr.io"; // MQTT Broker URL 38 | int port = 1883; 39 | uint8_t lastSentReading = 0; //Stores last Input Reading sent to the broker 40 | 41 | void setup() { 42 | Serial.begin(115200); 43 | while(!P1.init()); //Wait for module sign-on 44 | Ethernet.init(5); //CS pin for P1AM-ETH 45 | Ethernet.begin(mac); // Get IP from DHCP 46 | 47 | mqttClient.setUsernamePassword("username", "password"); // Username and Password tokens for Shiftr.io namespace. These can be found in the namespace settings. 48 | Serial.print("Connecting to the MQTT broker: "); 49 | Serial.println(broker); 50 | while (!mqttClient.connect(broker, port)) { 51 | Serial.print("MQTT connection failed! Error code = "); 52 | Serial.println(mqttClient.connectError()); 53 | } 54 | 55 | Serial.println("You're connected to the MQTT broker!"); 56 | Serial.println(); 57 | 58 | mqttClient.subscribe("InputReading"); //Subscribe to "InputReading" topic 59 | } 60 | 61 | void loop() { 62 | //Sending Updates 63 | uint8_t inputReading = P1.readDiscrete(SIM); //Check inputs right now 64 | if(inputReading != lastSentReading){ // If the state doesn't match the last sent state, update the broker 65 | mqttClient.beginMessage("InputReading"); //Topic name 66 | mqttClient.print(inputReading); //Value to send 67 | mqttClient.endMessage(); 68 | lastSentReading = inputReading; //Update our last sent reading 69 | Serial.println("Sent " + (String)inputReading + " to broker"); 70 | } 71 | 72 | //Receiving updates 73 | int mqttValue = checkBroker(); //Check for new messages 74 | if(mqttValue != -1){ // -1 means we didn't get a new message 75 | P1.writeDiscrete(mqttValue, TRS); //If we get a new message update the TRS 76 | } 77 | } 78 | 79 | int checkBroker(){ 80 | String mqttMessage = ""; 81 | int messageValue = 0; 82 | 83 | int messageSize = mqttClient.parseMessage(); 84 | if (messageSize) { 85 | // we received a message, print out the topic and contents 86 | Serial.print("Received a message with topic "); 87 | Serial.println(mqttClient.messageTopic()); 88 | if(mqttClient.messageTopic() == "InputReading"){ 89 | while (mqttClient.available()){ 90 | mqttMessage +=(char)mqttClient.read(); //Add all message characters to the string 91 | } 92 | messageValue = mqttMessage.toInt(); //convert ascii string to integer value 93 | } 94 | } 95 | else{ 96 | messageValue = -1; //If we didn't receive anything set our value to -1. Since our SIM 97 | //value can't be -1, this makes it easy to filter out later 98 | } 99 | return messageValue; 100 | } 101 | -------------------------------------------------------------------------------- /P1AM-100_ModbusTCP_Server_HMI/P1AM-100_ModbusTCP_Server_HMI.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Modbus TCP Server for P1AM-ETH with 3 | access to P1 Input/Output Modules 4 | This sketch creates a Modbus TCP Server for up to 8 connections. 5 | Slot1 Inputs are mapped to Modbus Input Bits 100001 to 100008. 6 | Slot2 Outputs are controlled by Modbus Coil Bits 000001 to 000008. 7 | Modbus Holding Register 400001 is always incrementing. 8 | The P1AM-100 cpu will require external 24vdc power 9 | for the IO modules to function. This example uses 10 | Serial.print() to display status information. The 11 | Serial Monitor must be running for the sketch to start. 12 | Required Libraries which need to be installed. 13 | https://github.com/arduino-libraries/ArduinoModbus 14 | https://github.com/arduino-libraries/ArduinoRS485 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include // ArduinoModbus depends on the ArduinoRS485 library 22 | #include 23 | 24 | byte mac[] = { //Use the �Reserved MAC� printed on the right-side label of your P1AM-ETH. 25 | 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED 26 | }; 27 | IPAddress ip(10, 11, 0, 177); //IP Address of the P1AM-ETH module. 28 | boolean MB_C[16]; //Modbus Coil Bits 29 | boolean MB_I[16]; //Modbus Input Bits 30 | int MB_HR[16]; //Modbus Holding Registers 31 | int MB_IR[16]; //Modbus Input Registers 32 | 33 | EthernetServer server(502); 34 | EthernetClient clients[8]; 35 | ModbusTCPServer modbusTCPServer; 36 | int client_cnt; 37 | 38 | void setup() { 39 | Serial.begin(115200); 40 | while (!Serial){}// wait for serial port to connect. 41 | Serial.println("Modbus TCP Server and Module I/O Example"); 42 | while (!P1.init()){} ; //Wait for P1 Modules to Sign on 43 | Ethernet.begin(mac, ip); 44 | server.begin(); // start the server to begin listening 45 | 46 | if (!modbusTCPServer.begin()) { // start the Modbus TCP server 47 | Serial.println("Failed to start Modbus TCP Server!"); 48 | while (1); //If it can't be started no need to contine, stay here forever. 49 | } 50 | 51 | modbusTCPServer.configureCoils(0x00, 16); //Coils 52 | modbusTCPServer.configureDiscreteInputs(0x00, 16); //Discrete Inputs 53 | modbusTCPServer.configureHoldingRegisters(0x00, 16); //Holding Register Words 54 | modbusTCPServer.configureInputRegisters(0x00, 16); //Input Register Words 55 | 56 | Serial.println("Done with setup()"); 57 | } 58 | 59 | void loop() { 60 | EthernetClient newClient = server.accept(); //listen for incoming clients 61 | if (newClient) { //process new connection if possible 62 | for (byte i = 0; i < 8; i++) { //Eight connections possible, find first available. 63 | if (!clients[i]) { 64 | clients[i] = newClient; 65 | client_cnt++; 66 | Serial.print("Client Accept:"); //a new client connected 67 | Serial.print(newClient.remoteIP()); 68 | Serial.print(" , Total:"); 69 | Serial.println(client_cnt); 70 | break; 71 | } 72 | } 73 | } 74 | 75 | //If there are packets available, receive them and process them. 76 | for (byte i = 0; i < 8; i++) { 77 | if (clients[i].available()) { //if data is available 78 | modbusTCPServer.accept(clients[i]); //accept that data 79 | modbusTCPServer.poll();// service any Modbus TCP requests, while client connected 80 | } 81 | } 82 | for (byte i = 0; i < 8; i++) { // Stop any clients which are disconnected 83 | if (clients[i] && !clients[i].connected()) { 84 | clients[i].stop(); 85 | client_cnt--; 86 | Serial.print("Client Stopped, Total: "); 87 | Serial.println(client_cnt); 88 | } 89 | } 90 | 91 | //Read from P1-08SIM Input Module and then write into Modbus memory 92 | int Slot1_Inputs = P1.readDiscrete(1,0); //Read from P1-08SIM Input Simulator Module 93 | for (int i=0;i<8;i++){ 94 | MB_I[i]=Slot1_Inputs&(1< 23 | #include 24 | #include 25 | 26 | // Enter a MAC address for your controller below. 27 | byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 28 | IPAddress ip(192, 168, 1, 177); //IP Address of the P1AM-ETH module. 29 | IPAddress subnet(255, 255, 0, 0); //IP Address of the network mask. 30 | IPAddress gateway(192, 168, 1, 1); //IP Address of the gateway. 31 | IPAddress dns(8, 8, 8, 8); //IP Address of the DNS server. 32 | IPAddress timeServer(216, 239, 35, 8); // Google's ntp server address 33 | 34 | RTCZero rtc; //rtc object to get the time from 35 | 36 | #define MINUTE 60 37 | #define HOUR 60 * MINUTE 38 | #define DAY 24 * HOUR 39 | const unsigned int ntpSyncTime = DAY; //86400 seconds. Resync every 24 hours. 40 | 41 | //If your timezone isn't here, lookup an epoch offset list to find the correct value 42 | const long EDT = -4*HOUR;// epoch offset for EDT timezone 43 | const long EST = -5*HOUR;// epoch offset for EST timezone 44 | const long PDT = -7*HOUR;// epoch offset for PDT timezone 45 | const long PST = -8*HOUR;// epoch offset for PST timezone 46 | long timeZoneOffset = EDT; // timezone to report time from. 47 | 48 | 49 | unsigned int localPort = 8888; 50 | const int NTP_PACKET_SIZE = 48; //size of ntp buffer 51 | byte packetBuffer[NTP_PACKET_SIZE]; // buffer to store ntp data 52 | EthernetUDP Udp; // udp instance 53 | unsigned long ntpLastUpdate = 0; // var to store time of last sync 54 | 55 | void setup() 56 | { 57 | Serial.begin(115200); 58 | //Ethernet.begin(mac);//Use with a DHCP Server 59 | Ethernet.begin(mac,ip,dns,gateway,subnet);//Use for a fixed IP 60 | delay(1000); 61 | getTimeAndDate(); // initial sync from ntp server 62 | rtc.begin(); // start real-time-clock 63 | } 64 | 65 | void loop() 66 | { 67 | checkForResync(); // See if we are due to resync with the NTP server 68 | printTime(); //Print current time and date from RTC 69 | delay(1000); 70 | } 71 | void checkForResync(){ 72 | 73 | unsigned long currentEpoch = rtc.getEpoch(); // current total RTC seconds 74 | if (currentEpoch - ntpLastUpdate > ntpSyncTime) { // resync time after ntpSyncTime interval. We have this for every day above 75 | if(getTimeAndDate()) { // try to resync time from ntp server 76 | Serial.println("Resync successful."); 77 | } 78 | else { 79 | Serial.println("Resync failed."); 80 | } 81 | } 82 | 83 | } 84 | 85 | void printTime() { // print time and date from rtc 86 | Serial.print(rtc.getHours()); 87 | printDigits(rtc.getMinutes()); 88 | printDigits(rtc.getSeconds()); 89 | Serial.println(); 90 | Serial.print(rtc.getMonth()); 91 | Serial.print("/"); 92 | Serial.print(rtc.getDay()); 93 | Serial.print("/"); 94 | Serial.println(rtc.getYear()); 95 | Serial.println(); 96 | } 97 | 98 | void printDigits(int digits) { // print preceding ':' and '0' for time 99 | Serial.print(':'); 100 | if (digits < 10) 101 | Serial.print('0'); 102 | Serial.print(digits); 103 | } 104 | 105 | 106 | // send an NTP request to the time server at the given address 107 | unsigned long sendNTPpacket(IPAddress& address) { 108 | // set all bytes in the buffer to 0 109 | memset(packetBuffer, 0, NTP_PACKET_SIZE); 110 | // Initialize values needed to form NTP request 111 | // (see URL above for details on the packets) 112 | packetBuffer[0] = 0b11100011; // LI, Version, Mode 113 | packetBuffer[1] = 0; // Stratum, or type of clock 114 | packetBuffer[2] = 6; // Polling Interval 115 | packetBuffer[3] = 0xEC; // Peer Clock Precision 116 | // 8 bytes of zero for Root Delay & Root Dispersion 117 | packetBuffer[12] = 49; 118 | packetBuffer[13] = 0x4E; 119 | packetBuffer[14] = 49; 120 | packetBuffer[15] = 52; 121 | 122 | // all NTP fields have been given values, now 123 | // you can send a packet requesting a timestamp: 124 | Udp.beginPacket(address, 123); //NTP requests are to port 123 125 | Udp.write(packetBuffer, NTP_PACKET_SIZE); 126 | Udp.endPacket(); 127 | } 128 | int getTimeAndDate() { // sync time on rtc from ntp server 129 | int flag = 0; 130 | Udp.begin(localPort); 131 | sendNTPpacket(timeServer); // send packet to ntp server 132 | delay(250); 133 | if (Udp.parsePacket()) { // read response from ntp server 134 | Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer 135 | unsigned long highWord, lowWord, epoch; 136 | highWord = word(packetBuffer[40], packetBuffer[41]); 137 | lowWord = word(packetBuffer[42], packetBuffer[43]); 138 | epoch = highWord << 16 | lowWord; 139 | epoch = epoch - 2208988800 + timeZoneOffset; 140 | flag = 1; 141 | rtc.setEpoch(epoch); // set rtc to up-to-date epoch 142 | ntpLastUpdate = epoch; // set last time time was synced 143 | } 144 | return flag; // return true on successful update 145 | } 146 | -------------------------------------------------------------------------------- /P1AM-100_ModbusRTU_HMI/P1AM-100_ModbusRTU_Server_HMI.ino: -------------------------------------------------------------------------------- 1 | /* 2 | created 18 July 2018 3 | by Sandeep Mistry 4 | 5 | modified 28 April 2023 6 | by AutomationDirect.com 7 | 8 | Modbus RTU Server for P1AM-SERIAL with access to P1 Input/Output Modules 9 | This sketch creates a Modbus RTU Server on Port 2 of the P1AM-SERIAL shield. 10 | 11 | Slot 1 Inputs are mapped to Modbus Input Bits 10001 to 10008. 12 | Slot 2 Outputs are controlled by Modbus Coil Bits 00001 to 00008. 13 | Slot 3 Analog Inputs are mapped to Modbus Input Registers 30001 to 30004 and are disabled by default. 14 | Slot 4 Analog Outputs are controlled by Modbus Holding Registers 40003 to 40006 and are disabled by default. 15 | Modbus Holding Register 40001 increments once every second. 16 | 17 | The P1AM-100 cpu will require external 24vdc power for the IO modules to function. 18 | This example uses Serial.print() to display status information. 19 | The Serial Monitor must be running for the sketch to start. 20 | 21 | Serial wiring between port 1 RJ12 connector on HMI 22 | and port 2 terminal block on P1AM-SERIAL shield is as follows: 23 | RJ12 pin 1 - terminal G 24 | RJ12 pin 3 - terminal TX/- 25 | RJ12 pin 4 - terminal RX/+ 26 | 27 | For wiring, consider using a ZL-RJ12-CBL-2 cable and ZL-RTB-RJ12 terminal block 28 | 29 | Required Libraries which need to be installed. 30 | https://github.com/facts-engineering/P1AM_Serial 31 | */ 32 | 33 | #include //include the FACTS Engineering P1AM library 34 | #include //include the FACTS Engineering P1AM-SERIAL library 35 | #include 36 | 37 | boolean MB_C[16]; //Modbus Coil Bits 38 | boolean MB_I[16]; //Modbus Input Bits 39 | int MB_HR[16]; //Modbus Holding Registers 40 | int MB_IR[16]; //Modbus Input Registers 41 | unsigned long lastMillis = 0; // used to reference last time millis() was called 42 | 43 | ModbusRTUServerClass port2_server(Port2); 44 | 45 | void setup() { 46 | Serial.begin(115200); 47 | while (!Serial); // wait for serial port to connect. 48 | Serial.println("Modbus RTU Server and Module I/O Example"); 49 | while (!P1.init()); //Wait for P1 Modules to Sign on 50 | 51 | serial_port_config(2, RS232_MODE); // Set port 2 to RS232 prior to starting modbus client 52 | 53 | // start the Modbus RTU server on Port 2, with (slave) id 10, 9600 baud rate, 8 data bits, Even parity, 1 stop bit 54 | // see https://www.arduino.cc/reference/en/language/functions/communication/serial/begin/ for other configurations 55 | if (!port2_server.begin(10, 9600, SERIAL_8E1)) { 56 | Serial.println("Failed to start Modbus RTU Server!"); 57 | while (1); 58 | } 59 | 60 | // configure Modbus addresses 61 | port2_server.configureCoils(0x00, 16); //Coils 62 | port2_server.configureDiscreteInputs(0x00, 16); //Discrete Inputs 63 | port2_server.configureHoldingRegisters(0x00, 16); //Holding Register Words 64 | port2_server.configureInputRegisters(0x00, 16); //Input Register Words 65 | 66 | Serial.println("Done with setup()"); 67 | } 68 | 69 | void loop() { 70 | 71 | // poll for Modbus RTU requests 72 | int packetReceived = port2_server.poll(); 73 | 74 | //If there are packets available, receive them and process them. 75 | if (packetReceived) { 76 | updateCoils(); //Read the Coil bits from the Modbus Library 77 | updateHoldingRegisters(); //Read the Holding Register words from the Modbus Library 78 | for (int i = 0; i < 8; i++) { 79 | P1.writeDiscrete(MB_C[i], 2, i + 1); //Data,Slot,Channel ... Channel is one-based. 80 | } 81 | } 82 | 83 | //Read from Discrete Input Module and then write into Modbus memory 84 | int Slot1_Inputs = P1.readDiscrete(1); //Read from Discrete Input Module in slot 1 85 | for (int i = 0; i < 8; i++) { 86 | MB_I[i] = Slot1_Inputs & (1 << i); 87 | } 88 | updateInputs(); //Write current state of the Modbus Inputs into MB_I[] 89 | 90 | //To Read from Analog Input Module in slot 3 and then write into Modbus memory, uncomment the following 4 lines 91 | // for (int i=0; i<4; i++){ 92 | // MB_IR[i] = P1.readAnalog(3, i+1); //Slot, Channel (channel is one-based) 93 | // } 94 | // updateInputRegisters(); //Write current state of the Modbus Inputs into MB_IR[] 95 | 96 | //To write the values in Modbus Registers 40003, 40004, 40005, & 40006 to Analog Output Module channels in slot 4, 97 | //uncomment the following 3 lines 98 | // for (int i=0; i<4; i++){ 99 | // P1.writeAnalog(MB_HR[2+i], 4, i+1); //Value, Slot, Channel (channel is one-based) 100 | // } 101 | //NOTE: for P1.writeAnalog, the value being written to the analog output channel must be within the resolution of the module 102 | //(e.g. 12-bit resolution is 0-4095) 103 | 104 | //Compare current millisecond value to previous to execute every 1 second 105 | if (millis() - lastMillis > 1000) { 106 | lastMillis = millis(); 107 | MB_HR[0]++; 108 | port2_server.holdingRegisterWrite(0, MB_HR[0]); //increment the Modbus Register 40001 every second just to have data changing. 109 | } 110 | } 111 | 112 | void updateCoils() { //Read the Coil bits from the Modbus Library 113 | for (int i = 0; i < 16; i++) { 114 | MB_C[i] = port2_server.coilRead(i); 115 | } 116 | } 117 | 118 | void updateInputs() { //Write the Input bits to the Modbus Library 119 | for (int i = 0; i < 16; i++) { 120 | port2_server.discreteInputWrite(i, MB_I[i]); 121 | } 122 | } 123 | 124 | void updateHoldingRegisters() { //Read the Holding Register words from the Modbus Library 125 | for (int i = 0; i < 16; i++) { 126 | MB_HR[i] = port2_server.holdingRegisterRead(i); 127 | } 128 | } 129 | 130 | void updateInputRegisters() { //Write the Input Registers to the Modbus Library 131 | for (int i = 0; i < 16; i++) { 132 | port2_server.inputRegisterWrite(i, MB_IR[i]); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /P1AM-100 Example Series/ES2_P1AM-100_Analog_Webserver/ES2_P1AM-100_Analog_Webserver.ino: -------------------------------------------------------------------------------- 1 | /* 2 | P1AM Example Series #2 - Web Server Analog Input 3 | This example uses the P1AM-ETH shield to create a webserver that displays the input values of a P1-04ADL02 to a webpage. 4 | You can visit this webpage by entering it's IP address in a browser. the default address is 192.168.1.177. 5 | This page will display the readings of all 4 channels in volts and in counts. 6 | 7 | You can find information on the P1-04ADL-2 and other modules here: https://facts-engineering.github.io/modules/P1-04ADL-2/P1-04ADL-2.html 8 | The analog input module we used is the P1-04ADL-2, though you can easily use any analog or discrete module as long as you tweak 9 | the conversions for your range and resolution. 10 | If you use another input module you may find our configuration tool handy for setting ranges: https://facts-engineering.github.io/config.html 11 | This example can easily be changed to display multiple modules and their data. This is great for any sort of remote monitoring you might want. 12 | If you're comfortable with basic web development you can spruce up the website and add plenty of features of your own design. 13 | 14 | created 18 Dec 2009 15 | by David A. Mellis 16 | 17 | modified 9 Apr 2012 18 | by Tom Igoe 19 | 20 | modified 02 Sept 2015 21 | by Arturo Guadalupi 22 | 23 | modified 13 Feb 2020 24 | by Kevin Cochran and Adam Cummick for FACTS Engineering 25 | */ 26 | 27 | 28 | #include 29 | #include 30 | 31 | byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // P1AM-ETH have unique MAC IDs on their product label 32 | IPAddress ip(192, 168, 1, 177); //IP Address our webpage will be at 33 | EthernetServer server(80); //initilizes ethernet using port 80 34 | 35 | void setup() { 36 | 37 | Serial.begin(115200); 38 | while(!Serial); //Wait for Serial port to open. Remove this line if you want it to start automatically 39 | while(!P1.init()); //Wait for module sign-on 40 | Ethernet.init(5); //CS pin for P1AM-ETH 41 | Ethernet.begin(mac, ip); // start the Ethernet connection 42 | 43 | if (Ethernet.hardwareStatus() == EthernetNoHardware) { // Check for Ethernet hardware present 44 | Serial.println("Ethernet shield is missing. Please try again"); 45 | while (true) { 46 | delay(1); // do nothing, no point running without Ethernet hardware 47 | } 48 | } 49 | if (Ethernet.linkStatus() == LinkOFF) { 50 | Serial.println("Ethernet cable is not connected."); 51 | } 52 | 53 | // start the server 54 | server.begin(); 55 | Serial.print("server is at "); 56 | Serial.println(Ethernet.localIP()); 57 | } 58 | 59 | void loop() { 60 | // listen for incoming clients 61 | EthernetClient client = server.available(); 62 | if (client) { 63 | Serial.println("new client"); 64 | // an http request ends with a blank line 65 | boolean currentLineIsBlank = true; 66 | while (client.connected()) { 67 | if (client.available()) { 68 | char c = client.read(); 69 | Serial.write(c); 70 | // if you've gotten to the end of the line (received a newline 71 | // character) and the line is blank, the http request has ended, 72 | // so you can send a reply 73 | if (c == '\n' && currentLineIsBlank) { 74 | // send a standard http response header 75 | client.println("HTTP/1.1 200 OK"); 76 | client.println("Content-Type: text/html"); 77 | client.println("Connection: close"); // the connection will be closed after completion of the response 78 | client.println("Refresh: 5"); // refresh the page automatically every 5 sec 79 | client.println(); 80 | client.println(""); 81 | client.println(""); //Start our HTML here 82 | client.println("

Slot 1 Module Readings

"); //Print the title 83 | client.println("
"); 84 | client.println(""); //This line makes our text larger. Since we need to send the " character we have to "escape" it using a /" 85 | 86 | //----------------------This code handles the Analog values displayed on the webpage---------------------------- 87 | 88 | for (int i = 1; i <= 4; i++) { //Run our loop for i at 1, 2, 3, and 4 89 | int inputCounts = P1.readAnalog(1, i); //reads analog data from slot 1 for the channel indiciated by our loop variable "i" 90 | Serial.println(inputCounts); //Debug print to serial monitor 91 | float inputVolts = 10 * ((float)inputCounts / 8191); //math to convert 13-bit counts to a floating point voltage value 92 | Serial.println(inputVolts); //Debug print to serial monitor 93 | 94 | client.print("Voltage input in counts is "); //Print this text exactly 95 | client.print(inputCounts); //Print our current reading on counts 96 | client.print(" for channel "); 97 | client.println(i); //Print Current channel 98 | client.println("
"); //New line 99 | 100 | client.print("Voltage input in volts is "); //Print this text exactly 101 | client.print(inputVolts); //Print our current reading on Volts 102 | client.print(" for channel "); 103 | client.println(i); 104 | client.println("
"); //New line 105 | client.println("
"); 106 | } 107 | 108 | //------------------------------------------------------------------------------------------------------------ 109 | 110 | client.println("
"); 111 | client.println(""); //Our HTML ends here 112 | break; 113 | } 114 | if (c == '\n') { 115 | // you're starting a new line 116 | currentLineIsBlank = true; 117 | } else if (c != '\r') { 118 | // you've gotten a character on the current line 119 | currentLineIsBlank = false; 120 | } 121 | } 122 | } 123 | // close the connection: 124 | client.stop(); 125 | Serial.println("client disconnected"); 126 | } 127 | } 128 | --------------------------------------------------------------------------------