Skip to content

Commit 8e3432f

Browse files
NCM (Network Control Model) is the newest
of three protocols for Ethernet over USB. This implementation in arduino-pico enables an Ethernet connection with a host PC over USB without any extra hardware. Other uses of USB like Serial, Mouse or Keyboard continue to work in parallel. It has been tested on Windows and Linux. MacOS should also work.
1 parent c5a1255 commit 8e3432f

File tree

16 files changed

+681
-0
lines changed

16 files changed

+681
-0
lines changed

include/tusb_config.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
#define CFG_TUD_MSC (1)
7676
#define CFG_TUD_MIDI (1)
7777
#define CFG_TUD_VENDOR (0)
78+
#define CFG_TUD_NCM (1)
7879

7980
#define CFG_TUD_CDC_RX_BUFSIZE (256)
8081
#define CFG_TUD_CDC_TX_BUFSIZE (256)
@@ -84,10 +85,33 @@
8485
// HID buffer size Should be sufficient to hold ID (if any) + Data
8586
#define CFG_TUD_HID_EP_BUFSIZE (64)
8687

88+
8789
// MIDI
8890
#define CFG_TUD_MIDI_RX_BUFSIZE (64)
8991
#define CFG_TUD_MIDI_TX_BUFSIZE (64)
9092

93+
//--------------------------------------------------------------------
94+
// NCM CLASS CONFIGURATION, SEE "ncm.h" FOR PERFORMANCE TUNING
95+
//--------------------------------------------------------------------
96+
#include "lwipopts.h"
97+
// Must be >> MTU
98+
// Can be set to 2048 without impact
99+
#define CFG_TUD_NCM_IN_NTB_MAX_SIZE (2 * TCP_MSS + 100)
100+
101+
// Must be >> MTU
102+
// Can be set to smaller values if wNtbOutMaxDatagrams==1
103+
#define CFG_TUD_NCM_OUT_NTB_MAX_SIZE (2 * TCP_MSS + 100)
104+
105+
// Number of NCM transfer blocks for reception side
106+
#ifndef CFG_TUD_NCM_OUT_NTB_N
107+
#define CFG_TUD_NCM_OUT_NTB_N 1
108+
#endif
109+
110+
// Number of NCM transfer blocks for transmission side
111+
#ifndef CFG_TUD_NCM_IN_NTB_N
112+
#define CFG_TUD_NCM_IN_NTB_N 1
113+
#endif
114+
91115
#ifdef __cplusplus
92116
}
93117
#endif

libraries/lwIP_USB_NCM/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
**/.pio/
2+
**/.vscode/

libraries/lwIP_USB_NCM/.gitkeep

Whitespace-only changes.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.pio
2+
.vscode/.browse.c_cpp.db*
3+
.vscode/c_cpp_properties.json
4+
.vscode/launch.json
5+
.vscode/ipch
6+
*/README
7+

libraries/lwIP_USB_NCM/examples/WiFiClient-NCMEthernet-platformio/include/.gitkeep

Whitespace-only changes.

libraries/lwIP_USB_NCM/examples/WiFiClient-NCMEthernet-platformio/lib/.gitkeep

Whitespace-only changes.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
; PlatformIO Project Configuration File
2+
;
3+
; Build options: build flags, source filter
4+
; Upload options: custom upload port, speed and extra flags
5+
; Library options: dependencies, extra library storages
6+
; Advanced options: extra scripting
7+
;
8+
; Please visit documentation for the other options and examples
9+
; https://docs.platformio.org/page/projectconf.html
10+
11+
[platformio]
12+
default_envs = pico
13+
14+
[env:pico]
15+
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
16+
board = rpipico
17+
framework = arduino
18+
board_build.core = earlephilhower
19+
platform_packages =
20+
framework-arduinopico@symlink://path/to/arduino-pico
21+
22+
;build_flags = -DLWIP_DEBUG=1 -DDEBUG_RP2040_PORT=Serial1
23+
24+
[env:pico2]
25+
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
26+
board = rpipico2
27+
framework = arduino
28+
board_build.core = earlephilhower
29+
platform_packages =
30+
framework-arduinopico@symlink://path/to/arduino-pico
31+
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
This sketch establishes a TCP connection to a "quote of the day" service.
3+
It sends a "hello" message, and then prints received data.
4+
*/
5+
6+
#include <Arduino.h>
7+
#include <NCMEthernetlwIP.h>
8+
9+
const char* host = "djxmmx.net";
10+
const uint16_t port = 17;
11+
12+
NCMEthernetlwIP eth;
13+
IPAddress my_static_ip_addr(192, 168, 137, 100);
14+
IPAddress my_static_gateway_and_dns_addr(192, 168, 137, 1);
15+
16+
#define USE_REAL_UART
17+
18+
#if defined(USE_REAL_UART)
19+
#define SER Serial1
20+
#else
21+
#define SER Serial
22+
#endif
23+
24+
void setup() {
25+
// enable Serial1 so it can be used by USE_REAL_UART or by DEBUG_RP2040_PORT
26+
Serial1.end();
27+
Serial1.setTX(16);
28+
Serial1.setRX(17);
29+
Serial1.begin(115200);
30+
31+
pinMode(LED_BUILTIN, OUTPUT);
32+
digitalWrite(LED_BUILTIN, HIGH);
33+
34+
Serial.begin(115200);
35+
delay(3000);
36+
SER.println();
37+
SER.println();
38+
SER.println("Starting NCM Ethernet port");
39+
40+
41+
//optional static config
42+
// eth.config(my_static_ip_addr, my_static_gateway_and_dns_addr, IPAddress(255, 255, 255, 0), my_static_gateway_and_dns_addr);
43+
44+
// Start the Ethernet port
45+
// This starts DHCP in case config() was not called before
46+
bool ok = eth.begin();
47+
delay(1000);
48+
if (!ok) {
49+
while (1) {
50+
SER.println("Failed to initialize NCM Ethernet.");
51+
delay(1000);
52+
}
53+
} else {
54+
SER.println("NCM Ethernet started successfully.");
55+
}
56+
57+
}
58+
59+
void loop() {
60+
static unsigned long next_msg = 0;
61+
static bool led_on = false;
62+
if(millis() > next_msg) {
63+
SER.println(".");
64+
next_msg = millis() + 1000;
65+
digitalWrite(LED_BUILTIN, led_on);
66+
led_on ^=1;
67+
}
68+
69+
static bool connected = false;
70+
if(!eth.connected()) {
71+
connected = false;
72+
return;
73+
} else if(!connected){
74+
SER.println("");
75+
SER.println("Ethernet connected");
76+
SER.println("IP address: ");
77+
SER.println(eth.localIP());
78+
connected = true;
79+
}
80+
81+
static bool wait = false;
82+
83+
SER.printf("connecting to %s:%i\n", host, port);
84+
85+
// Use WiFiClient class to create TCP connections
86+
WiFiClient client;
87+
if (!client.connect(host, port)) {
88+
SER.println("connection failed");
89+
delay(5000);
90+
return;
91+
}
92+
93+
// This will send a string to the server
94+
SER.println("sending data to server");
95+
if (client.connected()) {
96+
client.println("hello from RP2040");
97+
}
98+
99+
// wait for data to be available
100+
unsigned long timeout = millis();
101+
while (client.available() == 0) {
102+
if (millis() - timeout > 5000) {
103+
SER.println(">>> Client Timeout !");
104+
client.stop();
105+
delay(60000);
106+
return;
107+
}
108+
}
109+
110+
// Read all the lines of the reply from server and print them to Serial
111+
SER.println("receiving from remote server");
112+
// not testing 'client.connected()' since we do not need to send data here
113+
while (client.available()) {
114+
char ch = static_cast<char>(client.read());
115+
SER.print(ch);
116+
}
117+
118+
// Close the connection
119+
SER.println();
120+
SER.println("closing connection");
121+
client.stop();
122+
123+
if (wait) {
124+
delay(300000); // execute once every 5 minutes, don't flood remote service
125+
}
126+
wait = true;
127+
}

libraries/lwIP_USB_NCM/examples/WiFiClient-NCMEthernet-platformio/test/.gitkeep

Whitespace-only changes.
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
This sketch establishes a TCP connection to a "quote of the day" service.
3+
It sends a "hello" message, and then prints received data.
4+
*/
5+
6+
#include <NCMEthernetlwIP.h>
7+
8+
const char* host = "djxmmx.net";
9+
const uint16_t port = 17;
10+
11+
NCMEthernetlwIP eth;
12+
IPAddress my_static_ip_addr(192, 168, 137, 100);
13+
IPAddress my_static_gateway_and_dns_addr(192, 168, 137, 1);
14+
15+
#define USE_REAL_UART
16+
17+
#if defined(USE_REAL_UART)
18+
#define SER Serial1
19+
#else
20+
#define SER Serial
21+
#endif
22+
23+
void setup() {
24+
// enable Serial1 so it can be used by USE_REAL_UART or by DEBUG_RP2040_PORT
25+
Serial1.end();
26+
Serial1.setTX(16);
27+
Serial1.setRX(17);
28+
Serial1.begin(115200);
29+
30+
pinMode(LED_BUILTIN, OUTPUT);
31+
digitalWrite(LED_BUILTIN, HIGH);
32+
33+
Serial.begin(115200);
34+
delay(3000);
35+
SER.println();
36+
SER.println();
37+
SER.println("Starting NCM Ethernet port");
38+
39+
40+
//optional static config
41+
// eth.config(my_static_ip_addr, my_static_gateway_and_dns_addr, IPAddress(255, 255, 255, 0), my_static_gateway_and_dns_addr);
42+
43+
// Start the Ethernet port
44+
// This starts DHCP in case config() was not called before
45+
bool ok = eth.begin();
46+
delay(1000);
47+
if (!ok) {
48+
while (1) {
49+
SER.println("Failed to initialize NCM Ethernet.");
50+
delay(1000);
51+
}
52+
} else {
53+
SER.println("NCM Ethernet started successfully.");
54+
}
55+
56+
}
57+
58+
void loop() {
59+
static unsigned long next_msg = 0;
60+
static bool led_on = false;
61+
if(millis() > next_msg) {
62+
SER.println(".");
63+
next_msg = millis() + 1000;
64+
digitalWrite(LED_BUILTIN, led_on);
65+
led_on ^=1;
66+
}
67+
68+
static bool connected = false;
69+
if(!eth.connected()) {
70+
connected = false;
71+
return;
72+
} else if(!connected){
73+
SER.println("");
74+
SER.println("Ethernet connected");
75+
SER.println("IP address: ");
76+
SER.println(eth.localIP());
77+
connected = true;
78+
}
79+
80+
static bool wait = false;
81+
82+
SER.printf("connecting to %s:%i\n", host, port);
83+
84+
// Use WiFiClient class to create TCP connections
85+
WiFiClient client;
86+
if (!client.connect(host, port)) {
87+
SER.println("connection failed");
88+
delay(5000);
89+
return;
90+
}
91+
92+
// This will send a string to the server
93+
SER.println("sending data to server");
94+
if (client.connected()) {
95+
client.println("hello from RP2040");
96+
}
97+
98+
// wait for data to be available
99+
unsigned long timeout = millis();
100+
while (client.available() == 0) {
101+
if (millis() - timeout > 5000) {
102+
SER.println(">>> Client Timeout !");
103+
client.stop();
104+
delay(60000);
105+
return;
106+
}
107+
}
108+
109+
// Read all the lines of the reply from server and print them to Serial
110+
SER.println("receiving from remote server");
111+
// not testing 'client.connected()' since we do not need to send data here
112+
while (client.available()) {
113+
char ch = static_cast<char>(client.read());
114+
SER.print(ch);
115+
}
116+
117+
// Close the connection
118+
SER.println();
119+
SER.println("closing connection");
120+
client.stop();
121+
122+
if (wait) {
123+
delay(300000); // execute once every 5 minutes, don't flood remote service
124+
}
125+
wait = true;
126+
}

0 commit comments

Comments
 (0)