In this work, a Power over Ethernet device was designed with characteristics that makes it industrial grade capable. A DIN rail 3D enclosure was designed for completeness of the hardware. A new software module was developed for an existing SCADA open source solution, the software module allows the discovery and configuration of the device. The solution was tested using the modbus industrial protocol on a simulator and also on an industrial energy meter to finally highlight its configuration process and real usage.
IntroductionThe WIZ750SR device is a complete solution for Ethernet to Serial devices, including even options for RS-232 and RS-485 levels. The device has an embedded firmware that allows configuration using an open source software developed by WIZnet which makes it easy for prototyping and testing. Nonetheless, the device itself needs some extra hardware to be able to effectively be used in a commercial product, different factors such as serial connectors, power supply and even a stable fixture for enclosure design are needed in order to be ready for the market. Commercial market is even different from Industrial or Electrical sector, mainly because different standards need to be meet for product acceptance but not always a product that meets with hardware standard break the barrier of acceptance, there is also a factor that plays an important role, the term to consider is Integration. An easy integration makes the product more prone to be adopted.
All these factors have been taken into consideration in this work to present a solution that meets hardware standards for the Industrial market and makes easier the work of field engineers by integrating a configuration interface in a well known open source SCADA software. Complete instructions for Integrator persons are provided but also developing details are explained.
About This DocumentThe document is structured in a way to present in the first sections the solution from the point of view of integrator personnel that want a quick introduction and to see results along with easy-to-follow steps that allow to understand its general functioning and configuration.
Following those sections will be presented more detailed discussion about how the project works, detailed diagram and software explanation useful for better understanding by developers that want to use, customized or even make new adaptations to the solution.
The Industrial PoE to Serial Bridge ProductThe product is based on the WIZ750SR, which is a tiny module well designed by WIZnet, especially because after studying the hardware I notice that with some minor modifications I can use the module in a PoE design. Power over Ethernet requires different hardware because the power goes along the signal lines and needs, among other things, rectification in order to extract DC level using a DC-DC converter. PoE is also well suited for the industrial market since it has isolation which is always a plus when interfacing with other equipment. In order to not lose the isolation, the WIZnet module built-in serial interfaces cannot be used, fortunately there is the TTL version that comes with I/O directly tied to WIZ750P processor. In order to make the solution as compact as possible, only one GPIO is exposed to be used as Digital Input only since it's connected to an optically isolated device. Nonetheless, the project software module was developed with full GPIO control using the WIZnet development board.
The following images show the different sections of hardware located on the double-sided PCB.
- Yellow: External PoE Cat-5 magnetic and rectifiers
- Magenta: PoE ultra miniature SMD module
- Blue: Isolated 5V power supply
- Green: Isolated RS-485 transceiver
- Light Blue: Optocoupler input
- Yellow: WIZ750SR module
- Blue: IDC replacement for RJ-45
- Green: RJ-45 communication signals Tx+ Tx-, Rx+ Rx-, CT.
- Light Blue: POE RJ-45 input
This project uses only commercially available components, the schematics contains the part numbers for the PCB components. You also need the IDC or ribbon cable and connectors for the RJ-45 replacement. For this an 8 position ribbon cable of 1.5 inches is enough. The RJ-45 is replaced by TE Connectivity part number 215570-8 available here at mouser. Only thing is that this connector comes with a guide pin for PCB mounting which is easily removed as shown below.
It happens that most of the 8 positions connectors similar to this that you can find these days are double row but pins are aligned, it happens that we need a zig zag type of connector here which is difficult to find. For volume quantities these might not be a problem since exists the option to build a similar part number without the guide pin using customization from TE as I understand.
The other end of the cable uses any compatible connector such this P/N 89108-0101 from 3M.
The images seen before correspond to the RS-485 interface version, which uses a 3-wire system which is the most common approach. The transceiver has an embedded coil transformer outputs to generate the isolated 6V output. The 6V is down regulated to 5V stable output to supply the isolated or external section of the transceiver, as well as 5V pull up to Digital Input in case a dry contact is used.
The POE ultra miniature SMD module outputs a DC level of 3.3V required for WIZnet module operation and provides 1500V isolation input to output while complying with IEEE802.3af PoE standard. The module is set to Class 0 (0.44Watts to 12.95Watts) operation and special care was taken to leave more than 3mm of clearance to maintain isolation on the base board.
The reason the module's minimum power is 0.44 Watts is because the module is not recommended to be operated below minimum load; audible noise will be an issue if the load is below the minimum. For this reason, a 270 Ohm resistor is placed on the base PCB to achieve minimum load.
The EIA-232 version.The version for RS-232 share the same design for the PoE power supply. This version is also isolated by using a 2 channel in / 2 channel out digital isolation IC to interface with the serial rs232 transceiver. Only four signals are used on this version to save space. Actually the board is significantly bigger than the 485 version mainly because of the connector.
The components can be easily soldered by hand thou is still time consuming.
The dimensions of the 232 board are 4823 by 1800 mils or 122.5 mm by 45.72. Keeping the width identical to the 485 version. The length of 485 version is 99 mm.
Industrial Enclosure - DIN RailA common material used for industrial cabinets is the known the DIN rail; it serves to fix a variety of equipment, from terminal blocks to relays and even PLCs. For this reason it was the choice to encapsulate the PoE bridge.
The enclosure is composed of two parts - the PCB slides in by its side. The internal is made in such a way that it doesn't need screw to fix the PCB in place. The lateral lid seals the unit; the lid has a lip/groove feature that closes the unit really tight without screws, however, provision for three screws has been taken.
The DIN rail feature prints really well in 3D. It's based on the popular DIN rail design here for Raspberry Pi. However, the part that prevents the clip from bending into one side was made completely as one piece, which means it attaches on both ends to the enclosure. This was an improvement after seeing the wall bend to the outside during a print.
The DIN rail enclosure for RS-485 version needs apertures on the top for the terminal blocks of communication port and optically isolated input.
The RS-232 DIN rail enclosure is different since the communication port uses a DB-9 connector. The Optically isolated Input uses a screw terminal block plug type and it's exposed on the side of the cover (or lateral side cover).
A view showing the design details of DB-9 connector and DIN Rail clip mechanism. This print was made using White ABS from Arrow Labs.
The design has 3 screw positions for secure fixation, thou the cover remain fix with a fixture done on the mechanical design software without them facilitating tests.
Before getting into the details of the software, it's worthwhile to say that the device is not restricted to use with a particular software or protocol, the Ethernet to Serial conversion is transparent and so any protocol can be used.
Having said that, I have taken the path to use modbus communication protocol to highlight the device usage in the industrial sector, as well as test the functionality.
The WIZnet WIZ750SR module has a special firmware that, apart from making the conversion from Ethernet to Serial transparent, it allows configuration by different methods, which basically differs in the used communication port such as Ethernet or Serial.
SCADA SoftwareThe open source project called Rapid SCADA is used for demonstration because, first of all, it's open source, but also because it provides some convenient features to easily evaluate the modbus conversion. A basic module for enabling the discovery and configuration of devices was developed and it will be used throughout this section that will go on to show the configuration steps and results.
The Rapid SCADA installation steps can be found here. After you have installed it on your system, familiarized with it a little bit, for this you can see here and here for some basic modbus example, you must download the KpTest.dll file from the download section of this project and copy to C:\\Scada\ScadaComm\KP assuming you have default install dir.
After that you can start the Communicator and setup the configuration of WIZnet.
- Start Communicator Interface and click "Add Communication Line"
This will bring a new Communication Line in which we are going to use the WIZnet Device Driver.
- Select TCP Client as the Communication Channel and click Properties to setup the Port number.
The communication channel for WIZnet can be UDP or TCP Client; I have tested mostly using TCP Client at default port 50001.
- Add the Device Driver by going to the Request Sequence Tab. Click the plus symbol and fill in Name by using any name of your preference. Also select the DLL from the dropdown list. A unique number is also required. The call number is the IP address of WIZnet.
To find out the IP address of the Wiznet module, select the recently added device driver (by selecting it on the table list) and click the lower right button called device properties. The following window will open.
- Click the Discovery button on the recently opened DLL property windows. The information should fill up automatically as the example below.
The available devices should appear in the Devices list as MAC addresses. For now, the code is in demo version and only one device will appear.
Here you can setup a Static IP address or device name if you need to.
- Now that you know your device IP address, take note of it. You can close the DLL windows to go back to the Request Sequence tab and fill in the call number with the device IP address found. The discovery command is implemented using an UDP broadcast command that does not use the Communication Channel. This is mainly because it uses a broadcast message which might be cumbersome to implement using the communication channel, but also because the communication channel will be use for the rest of the setup.
- Open the WIZnet driver properties and change Serial settings or GPIO settings by going on to the corresponding button as shown below.
- If you have followed the guide, you might be wondering about the check box "Enable Polling". This property allows to sending commands to the device periodically to read the GPIO. You can try it by enabling it, but before doing so make sure you set up the period in the main window correctly. By default it's all in zero. Go back to the communication channel and set up some minimum delay between commands, such as 100ms in the input field called "Delay after request cycle, ms". Although I introduce intentionally a delay of around 100ms in the code between reads, I am not so confident how fast you can read without compromising things.
So far, we have discovered and configured the bridge. It's time to setup the communication protocol and test it against a simulator and real device.
For this part, we need to open the Administrator software of Rapid SCADA.
- Add a new device under System -> devices with the following information.
- Open communicator software and import the recently added device.
- Download the Satec template KpModbus_SATEC.xml from the download section of this project. The template contains the definition of modbus basic register set for Satec energy meter PM175. Open the device driver properties and select the location of the template, which should be
C:\\SCADA\ScadaComm\Config\KpModbus_SATEC.xml
.
Save the changes, close and open the Administrator to reflect the changes.
- Open the Administrator Input Channels and you should have something similar, except the formula might be missing.
- Fill in the formula for the first three voltage channels as
CnlVal*828/9999 + 0
, selecting D.D as format and V as unit to show. The last step of the formula will be used later for the table view and scheme view.
- Open the communicator and make sure the new modbus devices is Active (check box), the setup continues in next section.
The best way to check that the project is working is with the help of a simulator. For modbus the plcsimulator is of great help. Install it and follow the steps below.
If you have followed this guide so far and you know some details of WIZ750SR, you might know we have to interact with it using a set of commands described here. For the modbus section, we need the device to be in Data Mode. The command documentation of the evaluation board for WIZ750SR states that there is a jumper that dictates whether the device enters command mode or data mode, namely AT mode and GW mode. The problem is that I have found this to have a weird behavior, in which sometimes it enters AT mode regardless the switch state. In those cases, you have to send the EX command.
The EX command documentation says it will put the device into GW mode but I found that it actually toggles the mode, but the response is always the same, so I have not found a way with the current firmware of WIZ750SR to know which mode is active. For this reason I have updated the driver to have a new button that sends the EX command to the device. In case you attempt the following steps for modbus communication and you are not seeing the debug port, try to toggle between AT and GW mode, otherwise if you are using the development board, you can check the debug port and confirm that the following is displayed at the end of the boot process.
Otherwise issue an EX command using the GW mode driver property window as shown below.
- Connect the simulator to the serial interface of WIZ750SR. This might need a USB to Serial converter adapter. Find the COM port and open it under simulator as below.
- Open the Communicator modbus channel, then verify that it's active and that the TCP client port is correct.
- Start the Communicator Service by clicking the green "Play" button on top. You should see the commands going to the simulator. To simulate, just fill in the values of 40256 to 40258 with values around 1440 which will produce a voltage around 120V after applying the formula seen before.
- In order to visualize the data received, load the table view and scheme view files attached in this project to the SCADA interface folder:
C:\\Scada\Interface\WiznetDemo
. Logout (if you were already logged in) and login to SCADA web admin in order to reflect the changes (username: admin and password: 12345). Head up to the web admin at http://localhost/Scada/Login.aspx, you should see something similar to:
- Play with the simulator and see how the values change. This will end the simulator tests and confirm our WIZnet bridge is properly configured and working.
A must to have gadget of the PoE2S Industrial device.
The purpose of the optically isolated input is not only to read On/Off values, which might be an usage as well, i.e. a cabinet door. Other usages are i.e. to measure pulses or frequency of a digital signal, examples of such signals are common, for example old meters (such as a current or voltage meter) commonly provides a digital pulse signal. How do you then measure it using WIZ750SR?
The answer is that with the actual state of the firmware is not possible, but it does not mean is not possible. The W7500P MCU is very capable of this while handling the bridge communications as you will see in a moment.
I will show you in this section the result of the implementation, the device reading pulses from a benchtop function generator and showing up in Scada software. Refer to later section for development details.
An image of the setup for demonstrating the result can be seen below.
The Scada software (with recent modifications) allows to capture the frequency of the signal as shown below.
Here is a short video.
Development DetailsThe following section will cover more details about the hardware and software design, going deep to study the internal design.
HardwareThe design is fairly simple in terms of PCB design, a two layer board, 3.9" x 1.8" in size. The routing is simple but with a few considerations to achieve an industrial design.
PCB considerations- The PoE module is Silvertel AG990X-M. Different versions exist differing mainly in output voltage. There are industrial versions available, one of which are actually used in this project, that is the P/N AG9903-MT.
- As the Silvertel module is the DC-DC converter and only a few external components where needed, special care to follow their guidelines was taken, for example the 3 mm clearance between input and output tracks/planes. Also, enough copper area was used to dissipate any extra heat produced by the module into the base board. Though this might not be an issue because the WIZnet module is not such a power-hungry device.
- Because the size was kept as minimal as possible, the module has to be at the bottom layer. Which means only a few SMD 0.1" connectors from SAMTEC goes there. The rest of the components go into the top layer, this allows a lower cost for assembly if the connectors are left to be soldered by hand which is not so hard. The RJ-45 was placed at the bottom because it was preferred for mechanical design, otherwise it can be moved to the top layer.
- The reset button is not accessible from outside right now because when of the design used with the DIN rail. The modification to allow it to reach from outside is minor and consists of getting one button with side operation.
- To keep board size at a minimum just one input GPIO is accessible from outside, because the opto-coupler device is needed for isolation. For development there is a header that contains all GPIO inputs without protection.
- WIZnet module needs a modification, which is not really simple for the average user since it needs a heat gun and extra precautions to remove the RJ-45. This product was thought to possibly collaborate with WIZnet factory to provide the module without the RJ-45 which should be easy to handle for them in appropriate sales quantities. I have applied a similar technique as here, covering with copper tape the sensitive components since the board has to be heated a lot, this will prevent excessive heat on the semiconductor components which might damage them.
- No extra effort has been made on this board to achieve impedance matching on the Ethernet routing from the Pulse magnetic to the IDC connector because the routing is really short distance, for 10/100 Ethernet, the max speed of the signal might be 100 Mbps which yields a 1/10 wavelength of around 30 cm.
- A full industrial design will also include the EFT protection which might change a little bit the designed PCB. But since WIZ750SR has provision for ESD and Surge protection as per IEC 61000-4-2 and IEC 61000-4-5 (module comes unpopulated) it's a good starting point for our tests.
- The length of the IDC cable should not be an issue since it's really short (1.5 inches at most) and this application is only 10/100 Ethernet.
- The isolation for RS-485 uses a TI transceiver Ithat comes with the transformer driver built-in which also reduces space.
- The isolation of RS-232 need external transformer driver to produces the 6V isolated voltage that is down-regulated to 5V by using the same LDO as the 485 version.
- The TI isolated 4 channel (2/2) is used. This IC allows us to have 4 signal RS-232 isolation interface. The design can be customized to used other buses or I/O lines by using the same family of devices, i.e. Isolated SPI and extra I/O lines. This will require customization of the WIZ750SR firmware thou.
The mechanical design was done even before the PCB design, to be exact the PCB was done tat the stage when components were placed on the board close to the intended position. Then with the help of the source files from WIZnet Altium PCB and a trial license of Altium, the 3D step file of WIZ750SR PCB was imported into the 3D Design software. With that and other vendor components a first prototype was designed. This allows verification of mechanical design such as collisions, enclosure size, etc.
SoftwareThe software Rapid SCADA is an open source project. You will need VS 2017 and the project is written in C#. The modbus driver comes with the Rapid SCADA installation and no additional explanation will be given here. The WIZnet driver was developed almost from scratch, having taken the Test project as an example and starting point.
The code is under version control here. From what I learned about the Rapid SCADA, the code is divided between two different process and your code uses those process classes as their base class. This was what I think I learned the hard way, basically the UI derived classes cannot be directly interfaced with communicator derived classes. The structure is shown below:
KpWiznetView is a sort of wrapper class that triggers the property window form. The UI application code is contained in the Form classes WiznetConfig, SerForm and gpioForm.
The KpWiznetLogic class is where the communicator code is called, here are a few important tasks that are performed:
- The initial value of serial properties and GPIO configuration is gathered when the connection is first established.
- The function called session is periodically executed based on the period value under the main channel window.
- Because the interaction between UI and Communicator Channel was required in order to visualize GPIO values in "real time" and due to the aforementioned restriction regarding these two classes, the approach taken in this project for completeness and for sanity's sake was to create XML configuration files. The files are accessed by specific classes created to handle the data. For example, the serial device properties are handle by SerConfig class and GPIO data, and configuration is handle by gpioConfig class.
- There is a flag in each XML file that indicates whether the data has changed. Only the UI classes can make this value true. The Communicator checks those values periodically and executes code accordingly.
- There is a main configuration class Config for the General settings such as IP address, Network operation mode, polling status, etc. - essentially the controls that are seen in the main device driver window. The values are not changed in any way in the KpWiznetLogic class actually. Because of a rush design, the value polling is the only one that is read in that class. This might be considered to be moved to another class in the future, such as GPIO.
- The Communicator, once started by the user, attempts to establish a TCP connection with the server, in this case the WIZnet module. Thereafter, the serial and GPIO properties are handled according to user modifications in UI by interactions using read/write of XML files as explained before. This is not the best approach, but for demonstrating purposes serves very well.
- Because the Communicator needs an IP address to connect with device and because the discovery feature is highly desirable which uses UDP broadcast packets, the approach then was to implement UDP communication under the UI class for the sole purpose of discovery and basic parameters such IP address, gateway, mask, etc.
Let's take a look at some snippets that show how some of the above comments were coded.
The one time query after start is:
public override void Session()
{
base.Session();
if (starting == true)
{
starting = false;
if (GetSerDeviceCfg())
{
wiznetSerCfg.Modify = false;
SaveSerialConfig();
}
if (GetDeviceGpioCfg())
{
wiznetGpioCfg.Modify = false;
SaveGpioConfig();
}
}
// remaining code here
}
The GPIO, which is an interesting snippet, executes if and only if the polling variable is enabled and some delay time has elapsed since last query. This was to avoid making queries too fast if the user sets 0 as delay and period values in the channel configuration, which at the end dictate how fast Session is called.
// Load the configuration to reflect the changes
LoadConfig();
if (fatalError)
{
WriteToLog(state);
}
else
{
if (config.PollingCfg == true)
{
try
{
TimeSpan interval = DateTime.Now - lastGpioRead;
if (interval.TotalMilliseconds >= GPIO_REFRESH)
{
lastGpioRead = DateTime.Now;
if (GetInputGpioValues())
{
SaveGpioConfig();
}
}
}
catch //(Exception e)
{
// take action here
}
}
}
A more complicate snippet is the one to handle any change in GPIO configuration shown below.
LoadGpioConfig();
if (fatalError)
{
WriteToLog(state);
}
else
{
if(wiznetGpioCfg.Modify == true)
{
StringBuilder cmdbuilder = new StringBuilder("");
// Apply User Changes to GPIO Configuration
// Look Up specific I/O change
if(wiznetGpioCfg.GpioA.change == true)
{
// send command here to adjust GPIO A Setting
if (wiznetGpioCfg.GpioA.Mode == "INPUT")
{
// command to change GPIO to INPUT
cmdbuilder.Append("CA0\r\n");
// command to read INPUT GPIO
cmdbuilder.Append("GA\r\n");
}
else {
// command to change GPIO to OUTPUT
cmdbuilder.Append("CA1\r\n");
// Command to write the OUTPUT Value in XML File
cmdbuilder.Append("GA" + wiznetGpioCfg.GpioA.Value.ToString() + "\r\n");
}
wiznetGpioCfg.GpioA.change = false;
}
if (wiznetGpioCfg.GpioB.change == true)
{
// send command here to adjust GPIO B Setting
if (wiznetGpioCfg.GpioB.Mode == "INPUT")
{
// command to change GPIO to INPUT
cmdbuilder.Append("CB0\r\n");
// command to read INPUT GPIO
cmdbuilder.Append("GB\r\n");
}
else
{
// command to change GPIO to OUTPUT
cmdbuilder.Append("CB1\r\n");
// Command to write the OUTPUT Value in XML File
cmdbuilder.Append("GB" + wiznetGpioCfg.GpioB.Value.ToString() + "\r\n");
}
wiznetGpioCfg.GpioB.change = false;
}
if (wiznetGpioCfg.GpioC.change == true)
{
// send command here to adjust GPIO C Setting
if (wiznetGpioCfg.GpioC.Mode == "INPUT")
{
// command to change GPIO to INPUT
cmdbuilder.Append("CC0\r\n");
// command to read INPUT GPIO
cmdbuilder.Append("GC\r\n");
}
else
{
// command to change GPIO to OUTPUT
cmdbuilder.Append("CC1\r\n");
// Command to write the OUTPUT Value in XML File
cmdbuilder.Append("GC" + wiznetGpioCfg.GpioC.Value.ToString() + "\r\n");
}
wiznetGpioCfg.GpioC.change = false;
}
if (wiznetGpioCfg.GpioD.change == true)
{
// send command here to adjust GPIO D Setting
if (wiznetGpioCfg.GpioD.Mode == "INPUT")
{
// command to change GPIO to INPUT
cmdbuilder.Append("CD0\r\n");
// command to read INPUT GPIO
cmdbuilder.Append("GD\r\n");
}
else
{
// command to change GPIO to OUTPUT
cmdbuilder.Append("CD1\r\n");
// Command to write the OUTPUT Value in XML File
cmdbuilder.Append("GD"+ wiznetGpioCfg.GpioD.Value.ToString() + "\r\n");
}
wiznetGpioCfg.GpioD.change = false;
}
// Build Network Command
byte[] cmd_hex = new byte[100]; // not sure how much will use
byte[] tmp_cmd;
byte[] end_cmd = { 0x0d, 0x0a };
int i = 0;
// Required for Network Communications
string command = "MA";
tmp_cmd = Encoding.ASCII.GetBytes(command);
Array.ConstrainedCopy(tmp_cmd, 0, cmd_hex, i, tmp_cmd.Length);
i += tmp_cmd.Length;
tmp_cmd = StringToByteArray(config.Mac.Replace(":", ""));
Array.ConstrainedCopy(tmp_cmd, 0, cmd_hex, i, tmp_cmd.Length);
i += tmp_cmd.Length;
Array.ConstrainedCopy(end_cmd, 0, cmd_hex, i, end_cmd.Length);
i += end_cmd.Length;
command = "PW \r\n";
tmp_cmd = Encoding.ASCII.GetBytes(command);
Array.ConstrainedCopy(tmp_cmd, 0, cmd_hex, i, tmp_cmd.Length);
i += tmp_cmd.Length;
// The actual Command
tmp_cmd = Encoding.ASCII.GetBytes(cmdbuilder.ToString());
Array.ConstrainedCopy(tmp_cmd, 0, cmd_hex, i, tmp_cmd.Length);
i += tmp_cmd.Length;
// Send the command
Connection.Write(cmd_hex, 0, i, CommUtils.ProtocolLogFormats.String, out string logText);
int partial = Connection.Read(cmd_hex, 0, 15, 1000);
// Ack Logic processing and save values for User UI
wiznetGpioCfg.Modify = false;
SaveGpioConfig();
}
}
You might have noticed the LoadGpioConfig and LoadConfig functions. There is also a LoadSerConfig. Those functions merely load the parameters from XML and fill in the classes variables for GPIO, config and serial respectively. For example the Load method of serial class is:
public bool Load(string fileName, out string errMsg)
{
SetToDefault();
try
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(fileName);
XmlElement rootElem = xmlDoc.DocumentElement;
Baud = rootElem.GetChildAsString("Baud");
DataBit = rootElem.GetChildAsString("DataBit");
Parity = rootElem.GetChildAsString("Parity");
Stop = rootElem.GetChildAsString("Stop");
Flow = rootElem.GetChildAsString("Flow");
Modify = rootElem.GetChildAsBool("Modify");
errMsg = "";
return true;
}
catch (Exception ex)
{
errMsg = CommPhrases.LoadKpSettingsError + ":" + Environment.NewLine + ex.Message;
return false;
}
}
and the XML file looks like:
<?xml version="1.0" encoding="utf-8"?>
<KpTestSerialConfig>
<Baud>115200</Baud>
<DataBit>8</DataBit>
<Parity>NONE</Parity>
<Stop>1</Stop>
<Flow>NONE</Flow>
<Modify>false</Modify>
</KpTestSerialConfig>
There is a special method called DecodeCommand in those classes that takes a string of commands returned by WIZnet and parse them to fill in variables of the class.
For the serial class the code is:
public bool DecodeCommand(string cmd)
{
bool success = false;
if (cmd.Contains("BR"))
{
int val = Int32.Parse(cmd.Substring(2));
Baud = BaudRatesNum[val].Item2;
success = true;
}
else if (cmd.Contains("DB"))
{
int val = Int32.Parse(cmd.Substring(2));
DataBit = DataBitNum[val].Item2;
success = true;
}
else if (cmd.Contains("PR"))
{
int val = Int32.Parse(cmd.Substring(2));
Parity = ParityNum[val].Item2;
success = true;
}
else if (cmd.Contains("SB"))
{
int val = Int32.Parse(cmd.Substring(2));
Stop = StopNum[val].Item2;
success = true;
}
else if (cmd.Contains("FL"))
{
int val = Int32.Parse(cmd.Substring(2));
Flow = FlowNum[val].Item2;
success = true;
}
return success;
}
First, the command of the WIZnet response is detected and the value is extracted, then the value is used as an index in a list of tuple to get the string associated with that value.
The list of tupple have been defined such as
BaudRatesNum = new List<(int, string)>
{
(0, "300"),
(1, "600"),
(2, "1200"),
(3, "1800"),
(4, "2400"),
(5, "4800"),
(6, "9600"),
(7, "14400"),
(8, "19200"),
(9, "28800"),
(10, "38400"),
(11, "57600"),
(12, "115200"),
(13, "230400")
};
DataBitNum = new List<(int, string)>
{
(0, "7"),
(1, "8")
};
ParityNum = new List<(int, string)>
{
(0, "NONE"),
(1, "ODD"),
(2, "EVEN")
};
StopNum = new List<(int, string)>
{
(0, "1"),
(1, "2")
};
FlowNum = new List<(int, string)>
{
(0, "NONE"),
(1, "XON/XOFF"),
(2, "RTS/CTS")
};
The Discovery feature is implemented in the class WiznetConfig. A separate UDP communication class is used to handle the UDP socket transmission and reception, though the reception is done in a external callback assigned to a method in WiznetConfig class.
Basically the udp class once started will listen to incoming packets that come into the network port given by PORT_NUMBER definition. Then commands are sent using the broadcast address to port 50001. Here is the code:
class UDPer
{
public delegate bool RxCb(byte[] data);
RxCb ExternalRxCb = null;
const int PORT_NUMBER = 5000;
public void Start()
{
// do nothing
}
public void Stop()
{
try
{
udp.Close();
}
catch
{
// don't care
}
}
public void AssignRxCb(RxCb rx)
{
ExternalRxCb += rx;
}
private readonly UdpClient udp = new UdpClient(PORT_NUMBER);
IAsyncResult ar_ = null;
private void StartListening()
{
ar_ = udp.BeginReceive(Receive, new object());
}
private void Receive(IAsyncResult ar)
{
IPEndPoint ip = new IPEndPoint(IPAddress.Any, PORT_NUMBER);
byte[] bytes = udp.EndReceive(ar, ref ip);
if(ExternalRxCb != null)
{
if (!ExternalRxCb(bytes))
{
string errMsg = "UDP reception Cb error";
ScadaUiUtils.ShowError(errMsg);
}
}
//StartListening();
}
public void Send(byte[] bytes, int dstPort=50001)
{
IPEndPoint ip = new IPEndPoint(IPAddress.Parse("255.255.255.255"), dstPort);
udp.Send(bytes, bytes.Length, ip);
StartListening();
}
}
It works really well, but coding and debugging was somehow complicated because I didn't realize that the form window needs to be disposed before closing it since it was displayed modally using the ShowDialog method.
With this information you are ready to take a look at the rest of the code, if interested in development further.
WIZ750SR Firmware.The firmware was customized using Eclipse IDE and open source tools. A couple of things to mention about it that I found on the road doing this.
1. Don't use an old Boot firmware to update the firmware. The problem is that with release versions (1.2.4 as the time of this writing) it will work since I guess Wiznet folks use Keil compiler which optimized code to be around ~41K. For ARM and Eclipse open source tools, the most optimized code I have handle to get using compiler options -Os is no less than 51K which fails to upload. Recent boot version should allow 100K images when not using backup image section.
2. If you optimize for -Os using Eclipse and ARM open source tools you will sadly found that DHCP don't work at all. It hangs. I spend some time until I found that the portion of code that fails is in the function makeDHCPMSG
void makeDHCPMSG(void)
{
//uint8_t bk_mac[6];
uint8_t* ptmp;
uint8_t i;
//getSHAR(bk_mac);
pDHCPMSG->op = DHCP_BOOTREQUEST;
pDHCPMSG->htype = DHCP_HTYPE10MB;
pDHCPMSG->hlen = DHCP_HLENETHERNET;
pDHCPMSG->hops = DHCP_HOPS;
ptmp = (uint8_t*)(&pDHCPMSG->xid);
*(ptmp+0) = (uint8_t)((DHCP_XID & 0xFF000000) >> 24);
*(ptmp+1) = (uint8_t)((DHCP_XID & 0x00FF0000) >> 16);
*(ptmp+2) = (uint8_t)((DHCP_XID & 0x0000FF00) >> 8);
*(ptmp+3) = (uint8_t)((DHCP_XID & 0x000000FF) >> 0);
pDHCPMSG->secs = DHCP_SECS;
ptmp = (uint8_t*)(&pDHCPMSG->flags);
*(ptmp+0) = (uint8_t)((DHCP_FLAGSBROADCAST & 0xFF00) >> 8);
*(ptmp+1) = (uint8_t)((DHCP_FLAGSBROADCAST & 0x00FF) >> 0);
In order to avoid this issue use optimization level -O3 or -Og. I will try to raise an issue of this in github, but I guess they will not care too much since Wiznet uses Keil tools.
In order to implement the Frequency a few things are added.
- The code to get the pulse count.
- The code that handle new command to send and receive those values to Host and configure device.
The first uses Interrupt routine of GPIO and Dual Timer 1, the timer is setup to interrupt at 1 second. At that time extra care have been taken to quickly store the pulse count, reset it's value. The main task can then retrieve it's value by calling a function.
A set of command to implement a new configuration have been made, the commands are called NA, NB, ... and TA, TB, .. for all the GPIO.
TA command get the frequency value. NA command change between Normal Input and Pulse Input Type.
The new command structure changes to
// Command [K1] : Hidden command, This command erase the configutation data in flash / or EEPROM
typedef enum {SEGCP_MC, SEGCP_VR, SEGCP_MN, SEGCP_IM, SEGCP_OP, SEGCP_DD, SEGCP_CP, SEGCP_PO, SEGCP_DG, SEGCP_KA,
SEGCP_KI, SEGCP_KE, SEGCP_RI, SEGCP_LI, SEGCP_SM, SEGCP_GW, SEGCP_DS, SEGCP_PI, SEGCP_PP, SEGCP_DX,
SEGCP_DP, SEGCP_DI, SEGCP_DW, SEGCP_DH, SEGCP_LP, SEGCP_RP, SEGCP_RH, SEGCP_BR, SEGCP_DB, SEGCP_PR,
SEGCP_SB, SEGCP_FL, SEGCP_IT, SEGCP_PT, SEGCP_PS, SEGCP_PD, SEGCP_TE, SEGCP_SS, SEGCP_NP, SEGCP_SP,
SEGCP_LG, SEGCP_ER, SEGCP_FW, SEGCP_MA, SEGCP_PW, SEGCP_SV, SEGCP_EX, SEGCP_RT, SEGCP_UN, SEGCP_ST,
SEGCP_FR, SEGCP_EC, SEGCP_K1, SEGCP_UE, SEGCP_GA, SEGCP_GB, SEGCP_GC, SEGCP_GD, SEGCP_CA, SEGCP_CB,
SEGCP_CC, SEGCP_CD, SEGCP_SC, SEGCP_S0, SEGCP_S1, SEGCP_RX, SEGCP_FS, SEGCP_FC, SEGCP_FP, SEGCP_FD,
SEGCP_FH, SEGCP_UI, SEGCP_AB, SEGCP_TR, SEGCP_BU,
#ifdef BASE_BOARD_NAME
#if (BASE_BOARD_NAME == WIZ750SR_POE)
SEGCP_NA, SEGCP_NB, SEGCP_NC, SEGCP_ND, SEGCP_TA, SEGCP_TB, SEGCP_TC, SEGCP_TD,
#endif
#endif
SEGCP_UNKNOWN=255
} teSEGCPCMDNUM;
And the section that allow the value to be read is
#ifdef __ALLOW_GPIO_PULSE_TYPE__
// Read configuration of Digital Input
// as Normal or Pulse Measurement Type
case SEGCP_NA:
case SEGCP_NB:
case SEGCP_NC:
case SEGCP_ND:
io_num = (teSEGCPCMDNUM)cmdnum - SEGCP_NA;
dig_type = get_user_dig_type(USER_IO_SEL[io_num]);
sprintf(trep, "%d", dig_type);
break;
// Read the Frequency Value
case SEGCP_TA:
io_num = (teSEGCPCMDNUM)cmdnum - SEGCP_TA;
tmp_int = USER_IO_A << io_num;
io_freq = get_io_frequency(tmp_int);
sprintf(trep, "%lu", io_freq);
break;
case SEGCP_TB:
case SEGCP_TC:
case SEGCP_TD:
ret |= SEGCP_RET_ERR_NOTAVAIL;
break;
#endif
ConclusionThis projects relays only on a WIZ750SR, making a versatile product, a product with a PCB design and enclosure design, it can be use for different industrial and commercial projects, with few modifications even new versions can be created that benefit from PoE technology, thou this work shows an industrial PoE to Serial device based on the WIZnet WIZ750SR module and the integration into existing software such SCADA, only the sky is the limit for your new PoE product.
This projects shows how to build an external application that communicates with the WIZ750SR device using the standard communication protocol by Wiznet, but it also shows that it's possible with minimum effort to add new commands, retaining existing functionality and adding new features such as the pulse counter using GPIO.
Final Thoughts.Interested in one of the bare PCB used in this project? I have a few laying around that I can sell at really minimum price. Interested in similar design with few modifications? For example a PoE RGB LED strip? Just send me a message.
Comments