My goal is to send UDP packets from the microcontroller (nucleo board) to the PC - as simple as that. Update: I reached the goal successfully. I'll explain here how I did it, and the problems I stumbled upon initially. Many thanks to the StackOverflow community and people on LinkedIn ARM group to help me out!
1. SYSTEM OVERVIEW
My system is as follows:
1.1 Microcontroller side
Nucleo_F767ZI board with STM32F767ZI microcontroller. This board has an ethernet connector.
Mbed-OS 5 operating system.
lwip library (wich is part from the Mbed-OS 5 operating system).
1.2 PC side 🖳
Windows 10, 64-bit.
Free ethernet port (internet is through WiFi, that's why this ethernet port is unused).
Wireshark to see if the microcontroller actually sends out UDP packets. Interpretation of them will be done later on in Python. First I want to see packets in Wireshark.
1.3 Connections
Initially I connected the nucleo board directly to my computer, without a router or switch involved. Such a setup requires "static IP configuration". I would configure the PC to have IP address "192.168.1.10" and the microcontroller to have "192.168.1.20". I know this can work, because I've done it before with a PIC microcontroller.
But this situation is a little different. I got a little operating system running on the microcontroller: mbed-os v5. This operating system makes use of Lwip (Light-weight ip stack). Although it should be possible to configure a "static IP address", the software currently doesn't support it. More information can be found here: https://developer.mbed.org/questions/74145/EthernetInterfaceinit-mbed-os-5x-not-wor/. Because there is no way to configure a static IP address on the microcontroller, you need a DHCP server. The microcontroller connects to the network without an IP address, and broadcasts a packet with the question: "What IP address should I use on this network?". If the microcontroller doesn't get an answer, it will broadcast the question a few times, and eventually give up.
If there is a DHCP server connected to the network, it will receive the question and hand over a free IP address. So that is what you need. There are several ways to provide such a DHCP server:
Install a DHCP server on your Windows PC. This is the only way to keep switches and routers out of the picture. The nucleo board is connected directly to the PC. When the microcontroller broadcasts the question, the Windows PC hands over an IP address. After that, communication can start.
Throw a Raspberry PI into the game. The Raspberry PI should run a DHCP server. Connect the three devices (Windows PC, Raspberry PI and Nucleo board) to a switch.
Buy a simple router. Home routers have a DHCP server built-in. Connect the Windows PC and the Nucleo board to the LAN ports of this router.
I decided to go for the third option. The following figure shows the connections to my Netgear R7000 router:
2. ROUTER SETUP
The DHCP server inside the router will hand out IP addresses to all devices that connect to it. My router will hand out the IP address "192.168.1.2" to the first device, "192.168.1.3" to the second, and so forth all the way up to "192.168.1.254". But that's a bit problematic. How can the PC and the microcontroller know each others IP addresses? Perhaps there are more clever solutions, but I've figured out the following approach.
Each DHCP server has a table with "reserved IP addresses". In this table, you can add devices and assign a static IP address to them. So whenever that device asks for an IP address, the DHCP server takes a look in the table and hands over the one you've configured. But how does the DHCP server recognizes a device? It can recognize a device by its MAC-address. Every device has a globally unique MAC-address of 6 bytes.
This is how I've added my Windows PC and my microcontroller to the table:
STEP 1: Connect the computer to one of the LAN ports of the router. Switch the router on.
STEP 2: Make sure that all WiFi on your computer is disconnected. I've experienced lots of issues with this. (*)
STEP 3: Open a browser, and surf to "192.168.1.1". This is my routers home page. It could be different for your router.
STEP 4: Login (my routers default login is "admin" for the username and "password" for the password)
STEP 5: Add the Windows PC and the microcontroller board to the table. You have to look up their MAC-addresses first (**):
2.1 Disconnect all WiFi (*)
This should be a simple step, but Windows makes it hard. Windows can be quite stubborn and reconnect automatically to WiFi networks around, even if you've unchecked the connect automatically box! To force Windows to listen to you, you can use the cmd terminal. Open a cmd terminal with admin privileges. Next, type the following command to see all your WiFi profiles:
> netsh wlan show profiles
Apply the following command to those WiFi profiles that your PC is stubbornly connecting to:
> netsh wlan set profileparameter name="someWifiName" connectionmode=manual
Now you can disconnect from that WiFi network, and Windows won't reconnect automatically anymore.
2.2 Find the MAC address of your PC (**)
This is how I found the MAC-address of my computer. Keep in mind that a computer can have several MAC-addresses. The one for your ethernet port will be different to the one for your wireless connection! Type ipconfig /all in the Windows cmd terminal. I got the following output:
# Note: this is the correct item!
# --------------------------------
Ethernet adapter Local Area Connection:
Media State . . . . . . . . . . . : Media disconnected
Connection-specific DNS Suffix . :
Description . . . . . . . . . . . : Intel(R) Ethernet Connection (2) I219-LM
Physical Address. . . . . . . . . : C8-xx-xx-xx-xx-01 # Replaced some numbers by x for security :-)
DHCP Enabled. . . . . . . . . . . : Yes
Autoconfiguration Enabled . . . . : Yes
Make sure you're looking at the correct item in the list of connections. Ethernet adapter Local Area Connection is the correct one for my computer, because the network card description fits what is expected: Intel(R) Ethernet Connection (2) I219-LM. Before, I was looking at another item in the list, labelled Ethernet adapter Ethernet:
# Note: this is the wrong item!
# ------------------------------
Ethernet adapter Ethernet:
Media State . . . . . . . . . . . : Media disconnected
Connection-specific DNS Suffix . :
Description . . . . . . . . . . . : TAP-Windows Adapter V9
Physical Address. . . . . . . . . : 00-xx-xx-xx-xx-F7
DHCP Enabled. . . . . . . . . . . : No
Autoconfiguration Enabled . . . . : Yes
Mr. Joel C (see his answer below) notified me that the network card from that item is TAP-Windows Adapter V9. Apparently that is a virtual network card. That made me lose a lot of time. Thank you Mr. Joel C for helping me out!
Please make also sure that DHCP Enabled and Autoconfiguration Enabled are turned on!
2.3 Find the MAC address of your Nucleo board (**)
I found the MAC-address of my Nucleo board with the following code:
#include "lwip-interface/EthernetInterface.h"
//1. Make an ethernet object
EthernetInterface eth;
//2. Try to connect
eth.connect(); // <- This line will not work now,
// but at least it will help you to find out your
// own MAC-address.
//3. Print the MAC-address
logger.printf("Controller MAC Address is: %s\r\n", eth.get_mac_address());
The print output I got over my serial port is (some numbers I replaced by x for security):
Controller MAC Address is: 00:xx:xx:xx:xx:40
3. MICROCONTROLLER CODE
This is the code that runs on the microcontroller. My main.cpp file is inspired on the code found on the Mbed-os forum at https://forums.mbed.com/t/udp-receive-with-nucleo-f767zi/1806.
#include <string>
using std::string;
#include "mbed.h"
#include "lwip-interface/EthernetInterface.h"
static Serial logger(USBTX, USBRX);
static DigitalOut led1(LED1);
// IP addresses
#define IP_COMPUTER "192.168.1.10" // Make sure these IP addresses correspond to the
#define IP_NUCLEO "192.168.1.20" // table of 'reserved IP addresses' you have setup in
// your routers DHCP server!
// Ethernet settings
const int PORT_T = 50000;
const int PORT_R = 50001;
EthernetInterface eth;
static void udp_tx_thread_func();
static void udp_rx_thread_func();
static Thread udp_tx_thread;
static Thread udp_rx_thread;
int main()
{
// 1. Initialize the serial logger
logger.baud(115200);
logger.printf("\r\n\r\nApplication started\r\n");
// 2. Initialize and start the UDP connection
eth.connect();
logger.printf("Controller MAC Address is: %s\r\n", eth.get_mac_address());
logger.printf("Controller IP Address is: %s\r\n", eth.get_ip_address());
Thread::wait(200);
udp_tx_thread.start(udp_tx_thread_func);
udp_rx_thread.start(udp_rx_thread_func);
while (true)
{
led1 = !led1;
Thread::wait(500);
}
}
//------------------ Ethernet --------------------------------------------------
static void udp_tx_thread_func()
{
UDPSocket socket(ð);
SocketAddress sock_addr(IP_COMPUTER, PORT_T);
static uint32_t out_buffer[3];
while(true)
{
Thread::wait(100);
// Send 3 values of 32-bit each
out_buffer[0] = 150500;
out_buffer[1] = 255300;
out_buffer[2] = 54;
int ret = socket.sendto(sock_addr, &out_buffer[0], 12); // 3 values of 32-bit equals 12 bytes
//logger.printf("sendto return: %d\r\n", ret);
}
}
static void udp_rx_thread_func()
{
UDPSocket socket(ð);
SocketAddress sock_addr;
int bind = socket.bind(PORT_R);
logger.printf("bind return: %d\n", bind);
char buffer[256];
while(true)
{
//logger.printf("\nWait for packet...\n");
int n = socket.recvfrom(&sock_addr, buffer, sizeof(buffer));
buffer[n] = '\0';
//logger.printf("Packet from \"%s\": %s\n", sock_addr.get_ip_address(), buffer);
Thread::wait(500);
}
}
4. RESULTS
4.1 Wireshark results
In Wireshark I can see the UDP packets flowing in on the Local Area Connection! Huray!
4.2 Python code
The python code to catch the UDP packets looks like this:
import sys
import os
import socket
import dataprocessing.datastruct as datastruct
def main():
# 1. Configure the IP address
# -----------------------------
myAddr = ('192.168.1.10', 50000)
# 2. Create a UDP socket
# -----------------------
sock = None
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(1.5)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(myAddr)
print('UDPsock #: ' + str(sock.getsockname()))
sys.stdout.flush()
except Exception as e:
print('Could not bind to the UDP socket.')
print(e)
sys.stdout.flush()
while (true):
try:
data, address = sock.recvfrom(256) # buffer size is 256 bytes
bytedata = bytearray(data)
# Each 32-bit number is cut in 8-bit pieces. Bring them back together.
value_01 = bytedata[0] + 256*bytedata[1] + 65536*bytedata[2] + 16777216*bytedata[3]
value_02 = bytedata[4] + 256*bytedata[5] + 65536*bytedata[6] + 16777216*bytedata[7]
value_03 = bytedata[8] + 256*bytedata[9] + 65536*bytedata[10] + 16777216*bytedata[11]
print("Value 01: %d", value_01)
print("Value 02: %d", value_02)
print("Value 03: %d", value_03)
except socket.error as err:
print(err)
if __name__== '__main__':
print("")
print("Start UDP catcher")
print("-----------------")
main()
5. LET WIFI AND ETHERNET COEXIST
The UDP packets from the microcontroller flow in on your ethernet port (passing along the router). Meanwhile you might want to connect to some WiFi network for internet access. The problem is that any browser will try to get access through your ethernet port - ignoring the WiFi.
The solution is to make your browsers FIRST attempt to use the WiFi to reach an IP address, next attempt through the Ethernet port. This is done with the "Interface metric" in the Control Panel. Increase this number slightly:
The connection you have labelled Ethernet is actually a TAP connection (eg. a virtual ethernet card). Your actual ethernet connection is labelled Local Area Connection; that is the connection you will need to be configuring and watching with Wireshark, etc.
As to everything else Mbed-OS related, I personally have not dealt with it.
I'm learning network-programming and try to develop a simple socket application that used CSocket class from MFC. And found myself hitting a wall that need some help.
I want the server side listens on a certain port, example 1001. Then from other computer, on the same sub-net, it should successfully telnet to that port. My program works correctly on localhost, but fail on the LAN, although I have opened that port on firewall of listener.
Here is my code from listener:
//CListenSocket is derived from CSocket
CListenSocket myListener;
myListener.Create(1001);
myListener.Listen();
//OnAccept()
//CConnectSocket is also derived from CSocket
CConnectSocket myConnect;
myListener.Accept(myConnect);
I built the release version using VS2008, here is the screenshot of the configuration properties:
So at this stage, when I run the program, netstat -an show this line:
TCP 0.0.0.0:1001 0.0.0.0:0 LISTENING
Then on that machine telnet 127.0.0.1 1001, this line appears.
TCP 127.0.0.1:1001 127.0.0.1:2681 ESTABLISHED
So I think my code is correct. After that I tried from other machine with the same sub-net, and the telnet fail:
Connecting To 192.168.2.199...Could not open connection to the host, on port 1001: Connect failed
Note: that my listener is on 192.168.2.199, and the connector is on 192.168.2.3. Both nodes can successfully ping and sharing file with each other. I also add both Inbound Rule and Outbound Rule for the program on my firewall, here is the properties of the rule:
For more information: On my listener node, there is Apache HTTP server installed on it, so I have the other node telnet to port 80, and it works...
So where did I miss? please help, thank you in advance.
EDITION 1:
Attempt for troubleshooting
So after hitting my head to the table for a while, I quickly make a decision for not use telnet for client anymore. But make a small client program to catch errors:
//CClientSocket is derived from CSocket
CClientSocket clientSocket;
clientSocket.Create();
int iConnect = clientSocket.Connect(ipAddress,1001) //ipAddress is a variable of MFC's text box on GUI.
switch (iConnect)
{
case 0:
{
DWORD errorNumber = ::GetLastError(); //catch error code
CString s_errorNumber;
s_errorNumber.Format("%d",errorNumber); //format to CString for easy echo
MessageBox("Connection fail :"+s_errorNumber)
clientSocket.ShutDown(CAsyncSocket::both);
clientSocket.Close();
break;
case SOCKET_ERROR:
if (::GetLastError() != WSAEWOULDBLOCK)
clientSocket.ShutDown(CAsyncSocket::both);
else
clientSocket.AsyncSelect();
break;
default:
{
MessageBox("Connection Established.");
}
break;
}
And the error number is: 10061. I checked this code on MSDN an it is WSAECONNREFUSED-Connection refused.
Now we know the problem here must be somewhere in the listener's firewall... still hiting my head to the table.
I'm currently working with gloox in order to send XMPP messages from my C++ program. I work in local network with my private prosody XMPP server.
Sending text messages between two client works but not files. I tried the gloox examples (ft_rcv & ft_send) but it did not worked neither (obviously I modified the examples to match my configuration), I always have the same error :
<error type='cancel'><service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
At the beginning I thought it was due to my prosody server but I added the following lines in the conf files :
Component "proxy.jabberserver.local" "proxy65"
proxy65_address = "proxy.jabberserver.local"
proxy65_ports = { 7777 }
I tried different server and different port but I'm currently in a dead end. If someone have an idea it would be great.
Thank you
f->addStreamHost( JID("proxy.jabberserver.local"), "proxy.jabberserver.local", 7777 ); should do the trick. If no - show full XML log.
I have a program that splits a serial device into multiple virtual serial ports and routes all the data to them.
---- /dev/ttyS1.a [data]->
|
[data]-> /dev/ttyS1 ---- /dev/ttyS1.b [data]->
|
---- /dev/ttyS1.c [data]->
My working program (pseudo code for sake of readability and simplicity):
poll(...) {
// Route data from master to vsp
master.read(buf)
for(virtual serial ports as vsp) {
vsp.write(buf)
}
// Route data from vsp to master (if need be)
for(virtual serial ports as vsp) {
if(vsp.needs_to_write()) {
vsp.read(buf)
master.write(buf)
}
}
}
I have one physical serial port device on my machine that continuously feeds data through, which is how I tested if my program initially works, but I would like to write a test to emulate/simulate writing and reading both directions and verifying the data on both ends. Since the data I am receiving from my physical serial port device writes seemingly random data it is hard to verify what is going in is exactly what is being written.
How would I be able to do this? (pseudo code)
1. fork process that feeds a known char sequence into /dev/ttyS2 in a loop
2. use my program to read from the COM `master.read(buf)` and then write to the vsp `vsp.write(buf)`
3. how can I verify that after writing to the buf that the vsp has the correct data?
Any help is appreciated I am confused on how to automate testing this.
Edit 1:
No one can help?
I think you can use com0com to test. You can connect virtual serial ports and then write to one and read from other what it received.
enter link description here
I have established connection to a Siemens S7-300 PLC (simulated via PlcSIM) using the libnodave library. There are no issues connecting and writing data to the PLC. However, I am unable to change the status of the PLC from Start/Stop. I am attempting to use the following libnodave methods for such actions:
int daveStatus = daveStart(dc);
int daveStatus = daveStop(dc);
Both function calls return the same Error: 33794
nodave.c Cites the error as the following:
case 0x8402: return "CPU already in RUN or already in STOP ?";
The use of the daveStart() and daveStop() functions can be viewed in the example testS7online.c:
if(doStop) {
daveStop(dc);
}
if(doRun) {
daveStart(dc);
}
In the examples the start/stop functions are only called when MPI connections to the PLC are made. Does anyone know if the start/stop functions are supported for use with TCP connections? If so, any suggestions as to what may be causing my error?
I have just tried dc.start() and dc.stop() using libnodave 8.4 and NetToPlcSim tool. It worked perfectly. Possibly you don't use NetToPlcSim tool that makes connection to PLCSim via TCP/IP (that is 127.0.0.1 port 102 obviously) hence dc can't even connect. So if your lines don't work, then u must be doing something wrong.