Unverified Commit 1f3d5011 authored by Earle F. Philhower, III's avatar Earle F. Philhower, III Committed by GitHub

Support wired network interfaces (W5500, W5100, ENC28J60) (#1703)

Enable use of wired Ethernet modules as first-class LWIP citizens.  All
networking classes like MDNS, WebServer, HTTPClient, WiFiClient, and OTA
can use a wired Ethernet adapter just like built-in WiFi.

Two examples updated to show proper use.

Uses the Async Context support built into the Pico SDK.  When running on the
Pico  it will use the CYW43 async instance.

Uses modified wired Ethernet drivers, thanks Nicholas Humfrey!

Note, the classic, non-LWIP integrated `Ethernet` and related libraries
should still work fine (but not be able to use WebServer/HTTPS/etc.)

Fixes #775
parent 3950b944
......@@ -202,6 +202,7 @@ The installed tools include a version of OpenOCD (in the pqt-openocd directory)
* Bluetooth on the PicoW (Classic and BLE) with Keyboard, Mouse, Joystick, and Virtual Serial
* Generic Arduino USB Serial, Keyboard, Joystick, and Mouse emulation
* WiFi (Pico W)
* Ethernet (Wired W5500, W5100, ENC28J60)
* HTTP client and server (WebServer)
* SSL/TLS/HTTPS
* Over-the-Air (OTA) upgrades
......
......@@ -18,6 +18,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <Arduino.h>
#include <pico/mutex.h>
#include <lwip/pbuf.h>
#include <lwip/udp.h>
......@@ -26,20 +27,61 @@
#include <lwip/raw.h>
#include <lwip/timeouts.h>
#include <pico/cyw43_arch.h>
#include <pico/mutex.h>
#include <sys/lock.h>
#if !defined(ARDUINO_RASPBERRY_PI_PICO_W)
extern void ethernet_arch_lwip_begin() __attribute__((weak));
extern void ethernet_arch_lwip_end() __attribute__((weak));
auto_init_recursive_mutex(__lwipMutex); // Only for non-PicoW case
#endif
class LWIPMutex {
public:
LWIPMutex() {
cyw43_arch_lwip_begin();
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
if (rp2040.isPicoW()) {
cyw43_arch_lwip_begin();
return;
}
#else
if (ethernet_arch_lwip_begin) {
ethernet_arch_lwip_begin();
} else {
recursive_mutex_enter_blocking(&__lwipMutex);
}
#endif
}
~LWIPMutex() {
cyw43_arch_lwip_end();
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
if (rp2040.isPicoW()) {
cyw43_arch_lwip_end();
return;
}
#else
if (ethernet_arch_lwip_end) {
ethernet_arch_lwip_end();
} else {
recursive_mutex_exit(&__lwipMutex);
}
#endif
}
};
extern "C" {
// Avoid calling lwip_init multiple times
extern void __real_lwip_init();
void __wrap_lwip_init() {
static bool initted = false;
if (!initted) {
__real_lwip_init();
initted = true;
}
}
extern u8_t __real_pbuf_header(struct pbuf *p, s16_t header_size);
u8_t __wrap_pbuf_header(struct pbuf *p, s16_t header_size) {
LWIPMutex m;
......
EthernetLWIP (Wired Ethernet) Support
=====================================
Wired Ethernet interfaces are supported for all the internal networking
libraries (``WiFiClient``, ``WiFiClientSecure``, ``WiFiServer``,
``WiFiServerSecure``, ``WiFiUDP``, ``WebServer``, ``Updater``,
``HTTPClient``, etc.).
Using these wired interfaces is very similar to using the Pico-W WiFi
so most examples in the core only require minor modifications to use
a wired interface.
Supported Wired Ethernet Modules
--------------------------------
* Wiznet W5100
* Wiznet W5500
* ENC28J60
Enabling Wired Ethernet
-----------------------
Simply replace the WiFi include at the top with:
.. code:: cpp
#include <W5500lwIP.h> // Or W5100lwIP.h or ENC28J60.h
And add a global Ethernet object of the same type:
.. code:: cpp
Wiznet5500lwIP eth(1); // Parameter is the Chip Select pin
In your ``setup()`` you may adjust the SPI pins you're using to
match your hardware (be sure they are legal for the RP2040!), or
skip this if you're using the default ones:
.. code:: cpp
void setup() {
SPI.setRX(0);
SPI.setCS(1);
SPI.setSCK(2);
SPI.setTX(3);
....
}
And finally replace the ``WiFi.begin()`` and ``WiFi.connected()``
calls with ``eth.begin()`` and ``eth.connected()``:
.. code:: cpp
void setup() {
....
// WiFi.begin(SSID, PASS)
eth.begin();
//while (!WiFi.connected()) {
while (!eth.connected()) {
Serial.print(".");
}
Serial.print("IP address: ");
//Serial.println(WiFi.localIP());
Serial.println(eth.localIP());
....
}
Adjusting LWIP Polling
----------------------
LWIP operates in a polling mode for the wired Ethernet devices. By default it will run
every 20ms, meaning that on average it will take half that time (10ms) before a packet
received in the Ethernet module is received and operated upon by the Pico. This gives
very low CPU utilization but in some cases this latency can affect performance.
Adding a call to ``lwipPollingPeriod(XXX)`` (where ``XXXX`` is the polling period in
milliseconds) can adjust this setting on the fly. Note that if you set it too low, the
Pico may not have enough time to service the Ethernet port before the timer fires again,
leading to a lock up and hang.
Adjusting SPI Speed
-------------------
By default a 4MHz clock will be used to clock data into and out of the Ethernet module.
Depending on the module and your wiring, a higher SPI clock may increase performance (but
too high of a clock will cause communications problems or hangs).
This value may be adjusted using the ``eth.setSPISpeed(hz)`` call **before** starting the
device. (You may also use custom ``SPISettings`` instead via ``eth.setSPISettings(spis)```)
For example, to set the W5500 to use a 30MHZ clock:
.. code:: cpp
#include <W5500lwIP.h>
Wiznet5500lwIP eth(1);
void setup() {
eth.setSPISpeed(30000000);
lwipPollingPeriod(3);
...
eth.begin();
...
}
Example Code
------------
The following examples allow switching between WiFi and Ethernet:
* ``WebServer/AdvancedWebServer``
* ``HTTPClient/BasicHTTPSClient``
Caveats
-------
The same restrictions for ``WiFi`` apply to these Ethernet classes, namely:
* Only core 0 may run any networking related code.
* In FreeRTOS, only the ``setup`` and ``loop`` task can call networking libraries, not any tasks.
Special Thanks
--------------
* LWIPEthernet classes come from the ESP8266 Arduino team
* Individual Ethernet drivers were written by Nicholas Humfrey
......@@ -50,6 +50,7 @@ For the latest version, always check https://github.com/earlephilhower/arduino-p
FreeRTOS SMP (multicore) <freertos>
WiFi (Pico-W Support) <wifi>
Ethernet (Wired) <ethernet>
WiFiClient <wificlient>
WiFiServer <wifiserver>
WiFiUDP <wifiudp>
......
......@@ -152,6 +152,8 @@
-Wl,--wrap=realloc
-Wl,--wrap=free
-Wl,--wrap=lwip_init
-Wl,--wrap=pbuf_header
-Wl,--wrap=pbuf_free
-Wl,--wrap=pbuf_alloc
......
......@@ -6,7 +6,19 @@
*/
#include <Arduino.h>
// Example works with either Wired or WiFi Ethernet, define one of these values to 1, other to 0
#define USE_WIFI 1
#define USE_WIRED 0
#if USE_WIFI
#include <WiFi.h>
#elif USE_WIRED
#include <W5500lwIP.h> // Or W5100lwIP.h or ENC28J60lwIP.h
Wiznet5500lwIP eth(1 /* chip select */); // or Wiznet5100lwIP or ENC28J60lwIP
#endif
#include <HTTPClient.h>
#ifndef STASSID
......@@ -34,8 +46,33 @@ void setup() {
delay(1000);
}
#if USE_WIFI
WiFi.mode(WIFI_STA);
WiFiMulti.addAP(ssid, pass);
#elif USE_WIRED
// Set up SPI pinout to match your HW
SPI.setRX(0);
SPI.setCS(1);
SPI.setSCK(2);
SPI.setTX(3);
// Start the Ethernet port
if (!eth.begin()) {
Serial.println("No wired Ethernet hardware detected. Check pinouts, wiring.");
while (1) {
delay(1000);
}
}
// Wait for connection
while (eth.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.print("IP address: ");
Serial.println(eth.localIP());
#endif
}
const char *jigsaw_cert = R"EOF(
......@@ -74,8 +111,12 @@ N6K5xrmaof185pVCxACPLc/BoKyUwMeC8iXCm00=
static int cnt = 0;
void loop() {
// wait for WiFi connection
if ((WiFiMulti.run() == WL_CONNECTED)) {
#if USE_WIFI
// wait for WiFi connection
if ((WiFiMulti.run() == WL_CONNECTED)) {
#elif USE_WIRED
if (eth.connected()) {
#endif
HTTPClient https;
switch (cnt) {
case 0:
......
......@@ -28,7 +28,17 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Example works with either Wired or WiFi Ethernet, define one of these values to 1, other to 0
#define USE_WIFI 1
#define USE_WIRED 0
#if USE_WIFI
#include <WiFi.h>
#elif USE_WIRED
#include <W5500lwIP.h> // Or W5100lwIP.h or ENC28J60lwIP.h
Wiznet5500lwIP eth(1 /* chip select */); // or Wiznet5100lwIP or ENC28J60lwIP
#endif
#include <WiFiClient.h>
#include <WebServer.h>
#include <LEAmDNS.h>
......@@ -58,13 +68,13 @@ void handleRoot() {
temp.printf("<html>\
<head>\
<meta http-equiv='refresh' content='5'/>\
<title>Pico-W Demo</title>\
<title>" BOARD_NAME " Demo</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<h1>Hello from the Pico W!</h1>\
<h1>Hello from the " BOARD_NAME "!</h1>\
<p>Uptime: %02d:%02d:%02d</p>\
<p>Free Memory: %d</p>\
<p>Page Count: %d</p>\
......@@ -117,6 +127,8 @@ void setup(void) {
pinMode(led, OUTPUT);
digitalWrite(led, 0);
Serial.begin(115200);
#if USE_WIFI
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("");
......@@ -130,8 +142,32 @@ void setup(void) {
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
#elif USE_WIRED
// Set up SPI pinout to match your HW
SPI.setRX(0);
SPI.setCS(1);
SPI.setSCK(2);
SPI.setTX(3);
// Start the Ethernet port
if (!eth.begin()) {
Serial.println("No wired Ethernet hardware detected. Check pinouts, wiring.");
while (1) {
delay(1000);
}
}
// Wait for connection
while (eth.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.print("IP address: ");
Serial.println(eth.localIP());
#endif
if (MDNS.begin("picow")) {
Serial.println("MDNS responder started");
......
......@@ -61,7 +61,7 @@ void loop() {
// This will send a string to the server
Serial.println("sending data to server");
if (client.connected()) {
client.println("hello from ESP8266");
client.println("hello from RP2040");
}
// wait for data to be available
......
#######################################
# Syntax Coloring Map For WiFiNINA
# Syntax Coloring Map For WiFi
#######################################
#######################################
......@@ -7,7 +7,6 @@
#######################################
WiFi KEYWORD1
WiFiPico KEYWORD1
WiFiUdp KEYWORD1
WiFiClient KEYWORD1
WiFiSSLClient KEYWORD1
......@@ -51,6 +50,7 @@ remoteIP KEYWORD2
remotePort KEYWORD2
mode KEYWORD2
addAP KEYWORD2
hostByName KEYWORD2
beginAP KEYWORD2
beginEnterprise KEYWORD2
......
......@@ -9,4 +9,4 @@ url=http://github.com/earlephilhower/arduino-pico
architectures=rp2040
includes=WiFi.h
dot_a_linkage=true
depends=MD5Header
depends=MD5Header,Updater,LWIPEthernet
......@@ -583,9 +583,9 @@ uint8_t WiFiClass::reasonCode() {
@return 1 if aIPAddrString was successfully converted to an IP address,
else 0
*/
// Note that there is now a global FCN for name lookup to use all Ethernet ports, no need to call WiFi.hostByName, just ::hostByName
int WiFiClass::hostByName(const char* aHostname, IPAddress& aResult, int timeout_ms) {
return _wifi.hostByName(aHostname, aResult, timeout_ms);
return ::hostByName(aHostname, aResult, timeout_ms);
}
// TODO
......
......@@ -23,6 +23,7 @@
#pragma once
#include <Arduino.h>
#include <lwIP_CYW43.h>
#include "WiFi.h"
#include <inttypes.h>
......
......@@ -113,7 +113,7 @@ WiFiClient& WiFiClient::operator=(const WiFiClient& other) {
int WiFiClient::connect(const char* host, uint16_t port) {
IPAddress remote_addr;
if (WiFi.hostByName(host, remote_addr, _timeout)) {
if (::hostByName(host, remote_addr, _timeout)) {
return connect(remote_addr, port);
}
return 0;
......@@ -360,7 +360,6 @@ void WiFiClient::stopAllExcept(WiFiClient* except) {
}
}
void WiFiClient::keepAlive(uint16_t idle_sec, uint16_t intv_sec, uint8_t count) {
_client->keepAlive(idle_sec, intv_sec, count);
}
......@@ -380,28 +379,3 @@ uint16_t WiFiClient::getKeepAliveInterval() const {
uint8_t WiFiClient::getKeepAliveCount() const {
return _client->getKeepAliveCount();
}
//bool WiFiClient::hasPeekBufferAPI () const
//{
// return true;
//}
// return a pointer to available data buffer (size = peekAvailable())
// semantic forbids any kind of read() before calling peekConsume()
//const char* WiFiClient::peekBuffer ()
//{
// return _client? _client->peekBuffer(): nullptr;
//}
// return number of byte accessible by peekBuffer()
//size_t WiFiClient::peekAvailable ()
//{
// return _client? _client->peekAvailable(): 0;
//}
// consume bytes after use (see peekBuffer)
//void WiFiClient::peekConsume (size_t consume)
//{
// if (_client)
// _client->peekConsume(consume);
//}
......@@ -240,7 +240,7 @@ int WiFiClientSecureCtx::connect(IPAddress ip, uint16_t port) {
int WiFiClientSecureCtx::connect(const char* name, uint16_t port) {
IPAddress remote_addr;
if (!WiFi.hostByName(name, remote_addr)) {
if (!::hostByName(name, remote_addr)) {
DEBUG_BSSL("connect: Name lookup failure\n");
return 0;
}
......@@ -1457,7 +1457,7 @@ int WiFiClientSecureCtx::getLastSSLError(char *dest, size_t len) {
bool WiFiClientSecure::probeMaxFragmentLength(const char* name, uint16_t port, uint16_t len) {
IPAddress remote_addr;
if (!WiFi.hostByName(name, remote_addr)) {
if (!::hostByName(name, remote_addr)) {
DEBUG_BSSL("probeMaxFragmentLength: Can't resolve host\n");
return false;
}
......
......@@ -25,6 +25,7 @@
#include <vector>
#include "WiFiClient.h"
#include <LwipEthernet.h>
#include <bearssl/bearssl.h>
#include "BearSSLHelpers.h"
#include "CertStoreBearSSL.h"
......@@ -247,7 +248,7 @@ public:
}
int connect(const char *host, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key) {
IPAddress ip;
if (WiFi.hostByName(host, ip, _timeout)) {
if (::hostByName(host, ip, _timeout)) {
return connect(ip, port, rootCABuff, cli_cert, cli_key);
} else {
return 0;
......
......@@ -58,7 +58,7 @@ public:
void begin(const char *server, int timeout = 3600) {
IPAddress addr;
if (WiFi.hostByName(server, addr)) {
if (::hostByName(server, addr)) {
begin(addr, timeout);
}
_running = true;
......@@ -66,8 +66,8 @@ public:
void begin(const char *s1, const char *s2, int timeout = 3600) {
IPAddress a1, a2;
if (WiFi.hostByName(s1, a1)) {
if (WiFi.hostByName(s2, a2)) {
if (::hostByName(s1, a1)) {
if (::hostByName(s2, a2)) {
begin(a1, a2, timeout);
} else {
begin(a1, timeout);
......
......@@ -133,7 +133,7 @@ void WiFiUDP::stop() {
int WiFiUDP::beginPacket(const char *host, uint16_t port) {
IPAddress remote_addr;
if (WiFi.hostByName(host, remote_addr)) {
if (::hostByName(host, remote_addr)) {
return beginPacket(remote_addr, port);
}
return 0;
......
......@@ -37,9 +37,9 @@ bool getDefaultPrivateGlobalSyncValue();
template <typename T>
inline void esp_delay(const uint32_t timeout_ms, T&& blocked, const uint32_t intvl_ms) {
const auto start_ms = millis();
(void) intvl_ms;
while ((((uint32_t)millis() - start_ms) < timeout_ms) && blocked()) {
sys_check_timeouts();
delay(intvl_ms);
delay(1);
}
}
......
......@@ -66,6 +66,27 @@ public:
*/
uint16_t readFrame(uint8_t* buffer, uint16_t bufsize);
// ------------- Dummy handler, actually run in the SDK async context -------------
void handlePackets() {
}
// ------------- Dummy handler for linkage, but never called at runtime -------------
uint16_t readFrameSize() {
return 0;
}
// ------------- Dummy handler for linkage, but never called at runtime -------------
void discardFrame(uint16_t ign) {
(void) ign;
}
// ------------- Dummy handler for linkage, but never called at runtime -------------
uint16_t readFrameData(uint8_t *ign1, uint16_t ign2) {
(void) ign1;
(void) ign2;
return 0;
}
bool interruptIsPossible() {
return true;
}
......@@ -94,6 +115,10 @@ public:
_timeout = timeout;
}
constexpr bool needsSPI() const {
return false;
}
// LWIP netif for the IRQ packet processing
static netif *_netif;
protected:
......
#######################################
# Syntax Coloring Map
#######################################
#######################################
# Library (KEYWORD1)
#######################################
#######################################
# Methods and Functions (KEYWORD2)
#######################################
lwipPollingPeriod KEYWORD2
setSPISpeed KEYWORD2
setSPISettings KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
......@@ -6,5 +6,6 @@ sentence=Helper for ethernet drivers
paragraph=Example repository for Ethernet drivers
category=Communication
url=https://github.com/esp8266/Arduino
architectures=esp8266,rp2040
architectures=rp2040
dot_a_linkage=true
depends=WiFi,Updater
/*
LwipEthernet.cpp
Handles the async context for wired Ethernet
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <LwipEthernet.h>
//#include <SPI.h>
#include <lwip/timeouts.h>
#include <lwip/dns.h>
#include <pico/mutex.h>
#include <pico/cyw43_arch.h>
#include <pico/async_context_threadsafe_background.h>
#include <functional>
#include <map>
bool __ethernetContextInitted = false;
// Async context that pumps the ethernet controllers
static async_context_threadsafe_background_t lwip_ethernet_async_context_threadsafe_background;
static async_when_pending_worker_t always_pending_update_timeout_worker;
static async_at_time_worker_t ethernet_timeout_worker;
static async_context_t *_context = nullptr;
// Theoretically support multiple interfaces
static std::map<int, std::function<void(void)>> _handlePacketList;
void ethernet_arch_lwip_begin() {
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
if (rp2040.isPicoW()) {
cyw43_arch_lwip_begin();
return;
}
#endif
async_context_acquire_lock_blocking(&lwip_ethernet_async_context_threadsafe_background.core);
}
void ethernet_arch_lwip_end() {
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
if (rp2040.isPicoW()) {
cyw43_arch_lwip_end();
return;
}
#endif
async_context_release_lock(&lwip_ethernet_async_context_threadsafe_background.core);
}
int __addEthernetPacketHandler(std::function<void(void)> _packetHandler) {
static int id = 0xdead;
ethernet_arch_lwip_begin();
_handlePacketList.insert({id, _packetHandler});
ethernet_arch_lwip_end();
return id++;
}
void __removeEthernetPacketHandler(int id) {
ethernet_arch_lwip_begin();
_handlePacketList.erase(id);
ethernet_arch_lwip_end();
}
//#ifndef ETHERNET_SPI_CLOCK_DIV
//#define ETHERNET_SPI_CLOCK_DIV SPI_CLOCK_DIV4 // 4MHz (SPI.h)
//#endif
static volatile bool _dns_lookup_pending = false;
static void _dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg) {
(void) name;
if (!_dns_lookup_pending) {
return;
}
if (ipaddr) {
*(IPAddress *)callback_arg = IPAddress(ipaddr);
}
_dns_lookup_pending = false; // resume hostByName
}
int hostByName(const char* aHostname, IPAddress& aResult, int timeout_ms) {
ip_addr_t addr;
aResult = static_cast<uint32_t>(0xffffffff);
if (aResult.fromString(aHostname)) {
// Host name is a IP address use it!
return 1;
}
#if LWIP_IPV4 && LWIP_IPV6
err_t err = dns_gethostbyname_addrtype(aHostname, &addr, &_dns_found_callback, &aResult, LWIP_DNS_ADDRTYPE_DEFAULT);
#else
err_t err = dns_gethostbyname(aHostname, &addr, &_dns_found_callback, &aResult);
#endif
if (err == ERR_OK) {
aResult = IPAddress(&addr);
} else if (err == ERR_INPROGRESS) {
_dns_lookup_pending = true;
uint32_t now = millis();
while ((millis() - now < (uint32_t)timeout_ms) && _dns_lookup_pending) {
delay(1);
}
_dns_lookup_pending = false;
if (aResult.isSet()) {
err = ERR_OK;
}
}
if (err == ERR_OK) {
return 1;
}
return 0;
}
static async_context_t *lwip_ethernet_init_default_async_context(void) {
async_context_threadsafe_background_config_t config = async_context_threadsafe_background_default_config();
if (async_context_threadsafe_background_init(&lwip_ethernet_async_context_threadsafe_background, &config)) {
return &lwip_ethernet_async_context_threadsafe_background.core;
}
return NULL;
}
// This will only be called under the protection of the async context mutex, so no re-entrancy checks needed
static void ethernet_timeout_reached(__unused async_context_t *context, __unused async_at_time_worker_t *worker) {
assert(worker == &ethernet_timeout_worker);
for (auto handlePacket : _handlePacketList) {
handlePacket.second();
}
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
if (!rp2040.isPicoW()) {
sys_check_timeouts();
}
#else
sys_check_timeouts();
#endif
}
static uint32_t _pollingPeriod = 20;
static void update_next_timeout(async_context_t *context, async_when_pending_worker_t *worker) {
assert(worker == &always_pending_update_timeout_worker);
worker->work_pending = true;
async_context_add_at_time_worker_in_ms(context, &ethernet_timeout_worker, _pollingPeriod);
}
void __startEthernetContext() {
if (__ethernetContextInitted) {
return;
}
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
if (rp2040.isPicoW()) {
_context = cyw43_arch_async_context();
} else {
_context = lwip_ethernet_init_default_async_context();
}
#else
_context = lwip_ethernet_init_default_async_context();
#endif
always_pending_update_timeout_worker.work_pending = true;
always_pending_update_timeout_worker.do_work = update_next_timeout;
ethernet_timeout_worker.do_work = ethernet_timeout_reached;
async_context_add_when_pending_worker(_context, &always_pending_update_timeout_worker);
__ethernetContextInitted = true;
}
void SPI4EthInit() {
//SPI.begin();
// SPI.setClockDivider(ETHERNET_SPI_CLOCK_DIV);
// SPI.setBitOrder(MSBFIRST);
// SPI.setDataMode(SPI_MODE0);
void lwipPollingPeriod(int ms) {
if (ms > 0) {
// No need for mutexes, this is an atomic 32b write
_pollingPeriod = ms;
}
}
......@@ -20,53 +20,22 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
//#include <ESP8266WiFi.h> // tcp API
//#include <debug.h>
#include <Arduino.h>
#include <functional>
#include <lwIP_CYW43.h>
//#include <W5100lwIP.h>
//#include <W5500lwIP.h>
//#include <ENC28J60lwIP.h>
void ethernet_arch_lwip_begin() __attribute__((weak));
void ethernet_arch_lwip_end() __attribute__((weak));
// One of them is to be declared in the main sketch
// and passed to ethInitDHCP() or ethInitStatic():
// Wiznet5500lwIP eth(CSPIN);
// Wiznet5100lwIP eth(CSPIN);
// ENC28J60lwIP eth(CSPIN);
// Internal Ethernet helper functions
void __startEthernetContext();
void SPI4EthInit();
int __addEthernetPacketHandler(std::function<void(void)> _packetHandler);
void __removeEthernetPacketHandler(int id);
template<class EthImpl>
bool ethInitDHCP(EthImpl& eth) {
SPI4EthInit();
// Used by WiFiClient to get DNS lookup
int hostByName(const char *aHostname, IPAddress &aResult, int timeout_ms = 5000);
if (!eth.begin()) {
// hardware not responding
// DEBUGV("ethInitDHCP: hardware not responding\n");
return false;
}
return true;
}
template<class EthImpl>
bool ethInitStatic(EthImpl& eth, IPAddress IP, IPAddress gateway, IPAddress netmask, IPAddress dns1,
IPAddress dns2 = IPADDR_NONE) {
SPI4EthInit();
if (!eth.config(IP, gateway, netmask, dns1, dns2)) {
// invalid arguments
// DEBUGV("ethInitStatic: invalid arguments\n");
return false;
}
if (!eth.begin()) {
// hardware not responding
// DEBUGV("ethInitStatic: hardware not responding\n");
return false;
}
return true;
}
// Set the LWIP polling time (default 50ms). Lower polling times == lower latency but higher CPU usage
void lwipPollingPeriod(int ms);
......@@ -40,17 +40,17 @@
#include <lwip/inet_chksum.h>
#include <lwip/apps/sntp.h>
//#include <user_interface.h> // wifi_get_macaddr()
#include "SPI.h"
//#include "Schedule.h"
#include "LwipIntf.h"
#include "LwipEthernet.h"
#include "wl_definitions.h"
#ifndef DEFAULT_MTU
#define DEFAULT_MTU 1500
#endif
extern "C" void cyw43_hal_generate_laa_mac(__unused int idx, uint8_t buf[6]);
template<class RawDev>
......@@ -58,6 +58,7 @@ class LwipIntfDev: public LwipIntf, public RawDev {
public:
LwipIntfDev(int8_t cs = SS, SPIClass& spi = SPI, int8_t intr = -1) :
RawDev(cs, spi, intr), _mtu(DEFAULT_MTU), _intrPin(intr), _started(false), _default(false) {
_spiUnit = spi;
memset(&_netif, 0, sizeof(_netif));
}
......@@ -109,6 +110,14 @@ public:
int hostByName(const char* aHostname, IPAddress& aResult, int timeout);
inline void setSPISpeed(int mhz) {
setSPISettings(SPISettings(mhz, MSBFIRST, SPI_MODE0));
}
void setSPISettings(SPISettings s) {
_spiSettings = s;
}
// ESP8266WiFi API compatibility
wl_status_t status();
......@@ -121,12 +130,13 @@ protected:
static err_t netif_init_s(netif* netif);
static err_t linkoutput_s(netif* netif, struct pbuf* p);
static void netif_status_callback_s(netif* netif);
public:
// called on a regular basis or on interrupt
err_t handlePackets();
protected:
// members
SPIClass &_spiUnit = SPI;
SPISettings _spiSettings = SPISettings(4000000, MSBFIRST, SPI_MODE0);
netif _netif;
uint16_t _mtu;
......@@ -141,65 +151,14 @@ protected:
volatile int _ping_ttl;
static u8_t _pingCB(void *arg, struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *addr);
// DNS lookup callback
bool _dns_lookup_pending = false;
typedef struct {
IPAddress *ip;
LwipIntfDev<RawDev> *wifi;
} _dns_cb_t;
static void _dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg);
// Packet handler number
int _phID = -1;
};
template<class RawDev>
void LwipIntfDev<RawDev>::_dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg) {
(void) name;
_dns_cb_t *cb = (_dns_cb_t *)callback_arg;
if (!cb->wifi->_dns_lookup_pending) {
return;
}
if (ipaddr) {
*(cb->ip) = IPAddress(ipaddr);
}
cb->wifi->_dns_lookup_pending = false; // resume hostByName
}
template<class RawDev>
int LwipIntfDev<RawDev>::hostByName(const char* aHostname, IPAddress& aResult, int timeout_ms) {
ip_addr_t addr;
aResult = static_cast<uint32_t>(0xffffffff);
if (aResult.fromString(aHostname)) {
// Host name is a IP address use it!
return 1;
}
_dns_cb_t cb = { &aResult, this };
#if LWIP_IPV4 && LWIP_IPV6
err_t err = dns_gethostbyname_addrtype(aHostname, &addr, &_dns_found_callback, &cb, LWIP_DNS_ADDRTYPE_DEFAULT);
#else
err_t err = dns_gethostbyname(aHostname, &addr, &_dns_found_callback, &cb);
#endif
if (err == ERR_OK) {
aResult = IPAddress(&addr);
} else if (err == ERR_INPROGRESS) {
_dns_lookup_pending = true;
uint32_t now = millis();
while ((millis() - now < (uint32_t)timeout_ms) && _dns_lookup_pending) {
sys_check_timeouts();
delay(10);
}
_dns_lookup_pending = false;
if (aResult.isSet()) {
err = ERR_OK;
}
}
if (err == ERR_OK) {
return 1;
}
return 0;
return ::hostByName(aHostname, aResult, timeout_ms);
}
template<class RawDev>
......@@ -293,7 +252,16 @@ bool LwipIntfDev<RawDev>::config(const IPAddress& localIP, const IPAddress& gate
}
extern char wifi_station_hostname[];
template<class RawDev>
boolean LwipIntfDev<RawDev>::begin(const uint8_t* macAddress, const uint16_t mtu) {
bool LwipIntfDev<RawDev>::begin(const uint8_t* macAddress, const uint16_t mtu) {
lwip_init();
__startEthernetContext();
if (RawDev::needsSPI()) {
_spiUnit.begin();
// Set SPI clocks/etc. per request, doesn't seem to be direct way other than a fake transaction
_spiUnit.beginTransaction(_spiSettings);
_spiUnit.endTransaction();
}
if (mtu) {
_mtu = mtu;
}
......@@ -343,6 +311,8 @@ boolean LwipIntfDev<RawDev>::begin(const uint8_t* macAddress, const uint16_t mtu
return false;
}
_phID = __addEthernetPacketHandler(std::bind(&LwipIntfDev<RawDev>::handlePackets, this));
if (localIP().v4() == 0) {
// IP not set, starting DHCP
_netif.flags |= NETIF_FLAG_UP;
......@@ -383,24 +353,15 @@ boolean LwipIntfDev<RawDev>::begin(const uint8_t* macAddress, const uint16_t mtu
}
}
#if 0
if (_intrPin < 0
&& !schedule_recurrent_function_us(
[&]() {
this->handlePackets();
return true;
},
100)) {
netif_remove(&_netif);
return false;
}
#endif
return true;
}
template<class RawDev>
void LwipIntfDev<RawDev>::end() {
__removeEthernetPacketHandler(_phID);
RawDev::end();
netif_remove(&_netif);
memset(&_netif, 0, sizeof(_netif));
_started = false;
......@@ -415,7 +376,7 @@ wl_status_t LwipIntfDev<RawDev>::status() {
template<class RawDev>
err_t LwipIntfDev<RawDev>::linkoutput_s(netif* netif, struct pbuf* pbuf) {
LwipIntfDev* lid = (LwipIntfDev*)netif->state;
ethernet_arch_lwip_begin();
uint16_t len = lid->sendFrame((const uint8_t*)pbuf->payload, pbuf->len);
#if PHY_HAS_CAPTURE
......@@ -424,7 +385,7 @@ err_t LwipIntfDev<RawDev>::linkoutput_s(netif* netif, struct pbuf* pbuf) {
/*success*/ len == pbuf->len);
}
#endif
ethernet_arch_lwip_end();
return len == pbuf->len ? ERR_OK : ERR_MEM;
}
......
/*
This sketch establishes a TCP connection to a "quote of the day" service.
It sends a "hello" message, and then prints received data.
*/
#include <ENC28J60lwIP.h>
const char* host = "djxmmx.net";
const uint16_t port = 17;
ENC28J60lwIP eth(1 /* chip select */);
void setup() {
// Set up SPI pinout to match your HW
SPI.setRX(0);
SPI.setCS(1);
SPI.setSCK(2);
SPI.setTX(3);
Serial.begin(115200);
delay(5000);
Serial.println();
Serial.println();
Serial.println("Starting Ethernet port");
// Start the Ethernet port
if (!eth.begin()) {
Serial.println("No wired Ethernet hardware detected. Check pinouts, wiring.");
while (1) {
delay(1000);
}
}
while (!eth.connected()) {
Serial.print(".");
delay(500);
}
Serial.println("");
Serial.println("Ethernet connected");
Serial.println("IP address: ");
Serial.println(eth.localIP());
}
void loop() {
static bool wait = false;
Serial.print("connecting to ");
Serial.print(host);
Serial.print(':');
Serial.println(port);
// Use WiFiClient class to create TCP connections
WiFiClient client;
if (!client.connect(host, port)) {
Serial.println("connection failed");
delay(5000);
return;
}
// This will send a string to the server
Serial.println("sending data to server");
if (client.connected()) {
client.println("hello from RP2040");
}
// wait for data to be available
unsigned long timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 5000) {
Serial.println(">>> Client Timeout !");
client.stop();
delay(60000);
return;
}
}
// Read all the lines of the reply from server and print them to Serial
Serial.println("receiving from remote server");
// not testing 'client.connected()' since we do not need to send data here
while (client.available()) {
char ch = static_cast<char>(client.read());
Serial.print(ch);
}
// Close the connection
Serial.println();
Serial.println("closing connection");
client.stop();
if (wait) {
delay(300000); // execute once every 5 minutes, don't flood remote service
}
wait = true;
}
#######################################
# Syntax Coloring Map
#######################################
#######################################
# Library (KEYWORD1)
#######################################
ENC28J60lwIP KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
#######################################
# Constants (LITERAL1)
#######################################
name=lwIP_enc28j60
version=1
author=Nicholas Humfrey
maintainer=esp8266/Arduino
sentence=Ethernet driver
paragraph=ENC28J60 ethernet drivers for lwIP and esp8266 Arduino from https://github.com/njh/EtherSia/tree/master/src/enc28j60.cpp
category=Communication
url=https://github.com/esp8266/Arduino
architectures=rp2040
dot_a_linkage=true
#pragma once
#include <LwipIntfDev.h>
#include <utility/enc28j60.h>
#include <LwipEthernet.h>
#include <WiFi.h>
using ENC28J60lwIP = LwipIntfDev<ENC28J60>;
This diff is collapsed.
/**
Header file for direct Ethernet frame access to the ENC28J60 controller
@file enc28j60.h
*/
/*
Copyright (c) 2012-2013, Thingsquare, http://www.thingsquare.com/.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// original sources: https://github.com/njh/EtherSia/tree/master/src/enc28j60.h
#ifndef ENC28J60_H
#define ENC28J60_H
#include <SPI.h>
/**
Send and receive Ethernet frames directly using a ENC28J60 controller.
*/
class ENC28J60 {
public:
/**
Constructor that uses the default hardware SPI pins
@param cs the Arduino Chip Select / Slave Select pin (default 10 on Uno)
*/
ENC28J60(int8_t cs = SS, SPIClass& spi = SPI, int8_t intr = -1);
/**
Initialise the Ethernet controller
Must be called before sending or receiving Ethernet frames
@param address the local MAC address for the Ethernet interface
@return Returns true if setting up the Ethernet interface was successful
*/
bool begin(const uint8_t* address, netif *net);
/**
Send an Ethernet frame
@param data a pointer to the data to send
@param datalen the length of the data in the packet
@return the number of bytes transmitted
*/
virtual uint16_t sendFrame(const uint8_t* data, uint16_t datalen);
/**
Read an Ethernet frame
@param buffer a pointer to a buffer to write the packet to
@param bufsize the available space in the buffer
@return the length of the received packet
or 0 if no packet was received
*/
virtual uint16_t readFrame(uint8_t* buffer, uint16_t bufsize);
/**
Check physical link
@return true when physical link is up
*/
bool isLinked();
/**
Report whether ::isLinked() API is implemented
@return true when ::isLinked() API is implemented
*/
constexpr bool isLinkDetectable() const {
return true;
}
constexpr bool needsSPI() const {
return true;
}
netif *_netif;
protected:
static constexpr bool interruptIsPossible() {
return false;
}
/**
Read an Ethernet frame size
@return the length of data do receive
or 0 if no frame was received
*/
uint16_t readFrameSize();
/**
discard an Ethernet frame
@param framesize readFrameSize()'s result
*/
void discardFrame(uint16_t framesize);
/**
Read an Ethernet frame data
readFrameSize() must be called first,
its result must be passed into framesize parameter
@param buffer a pointer to a buffer to write the frame to
@param framesize readFrameSize()'s result
@return the length of the received frame
or 0 if a problem occurred
*/
uint16_t readFrameData(uint8_t* frame, uint16_t framesize);
private:
uint8_t is_mac_mii_reg(uint8_t reg);
uint8_t readreg(uint8_t reg);
void writereg(uint8_t reg, uint8_t data);
void setregbitfield(uint8_t reg, uint8_t mask);
void clearregbitfield(uint8_t reg, uint8_t mask);
void setregbank(uint8_t new_bank);
void writedata(const uint8_t* data, int datalen);
void writedatabyte(uint8_t byte);
int readdata(uint8_t* buf, int len);
uint8_t readdatabyte(void);
void softreset(void);
uint8_t readrev(void);
bool reset(void);
void enc28j60_arch_spi_init(void);
uint8_t enc28j60_arch_spi_write(uint8_t data);
uint8_t enc28j60_arch_spi_read(void);
void enc28j60_arch_spi_select(void);
void enc28j60_arch_spi_deselect(void);
// Previously defined in contiki/core/sys/clock.h
void clock_delay_usec(uint16_t dt);
uint16_t phyread(uint8_t reg);
uint8_t _bank;
int8_t _cs;
SPIClass& _spi;
const uint8_t* _localMac;
/* readFrame*() state */
uint16_t _next, _len;
};
#endif /* ENC28J60_H */
/*
This sketch establishes a TCP connection to a "quote of the day" service.
It sends a "hello" message, and then prints received data.
*/
#include <W5100lwIP.h>
const char* host = "djxmmx.net";
const uint16_t port = 17;
Wiznet5100lwIP eth(1 /* chip select */);
void setup() {
// Set up SPI pinout to match your HW
SPI.setRX(0);
SPI.setCS(1);
SPI.setSCK(2);
SPI.setTX(3);
Serial.begin(115200);
delay(5000);
Serial.println();
Serial.println();
Serial.println("Starting Ethernet port");
// Start the Ethernet port
if (!eth.begin()) {
Serial.println("No wired Ethernet hardware detected. Check pinouts, wiring.");
while (1) {
delay(1000);
}
}
while (!eth.connected()) {
Serial.print(".");
delay(500);
}
Serial.println("");
Serial.println("Ethernet connected");
Serial.println("IP address: ");
Serial.println(eth.localIP());
}
void loop() {
static bool wait = false;
Serial.print("connecting to ");
Serial.print(host);
Serial.print(':');
Serial.println(port);
// Use WiFiClient class to create TCP connections
WiFiClient client;
if (!client.connect(host, port)) {
Serial.println("connection failed");
delay(5000);
return;
}
// This will send a string to the server
Serial.println("sending data to server");
if (client.connected()) {
client.println("hello from RP2040");
}
// wait for data to be available
unsigned long timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 5000) {
Serial.println(">>> Client Timeout !");
client.stop();
delay(60000);
return;
}
}
// Read all the lines of the reply from server and print them to Serial
Serial.println("receiving from remote server");
// not testing 'client.connected()' since we do not need to send data here
while (client.available()) {
char ch = static_cast<char>(client.read());
Serial.print(ch);
}
// Close the connection
Serial.println();
Serial.println("closing connection");
client.stop();
if (wait) {
delay(300000); // execute once every 5 minutes, don't flood remote service
}
wait = true;
}
#######################################
# Syntax Coloring Map
#######################################
#######################################
# Library (KEYWORD1)
#######################################
W5100lwIP KEYWORD1
Wiznet5100lwIP KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
#######################################
# Constants (LITERAL1)
#######################################
name=lwIP_w5100
version=1
author=Nicholas Humfrey
maintainer=esp8266/Arduino
sentence=Ethernet driver
paragraph=Wiznet5100 ethernet drivers for lwIP and esp8266 Arduino from https://github.com/njh/W5100MacRaw
category=Communication
url=https://github.com/esp8266/Arduino
architectures=rp2040
dot_a_linkage=true
#pragma once
#include <LwipIntfDev.h>
#include <utility/w5100.h>
#include <LwipEthernet.h>
#include <WiFi.h>
using Wiznet5100lwIP = LwipIntfDev<Wiznet5100>;
/*
Copyright (c) 2013, WIZnet Co., Ltd.
Copyright (c) 2016, Nicholas Humfrey
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// original sources: https://github.com/njh/W5100MacRaw
#include <SPI.h>
#include "w5100.h"
uint8_t Wiznet5100::wizchip_read(uint16_t address) {
uint8_t ret;
wizchip_cs_select();
_spi.transfer(0x0F);
_spi.transfer((address & 0xFF00) >> 8);
_spi.transfer((address & 0x00FF) >> 0);
ret = _spi.transfer(0);
wizchip_cs_deselect();
return ret;
}
uint16_t Wiznet5100::wizchip_read_word(uint16_t address) {
return ((uint16_t)wizchip_read(address) << 8) + wizchip_read(address + 1);
}
void Wiznet5100::wizchip_read_buf(uint16_t address, uint8_t* pBuf, uint16_t len) {
for (uint16_t i = 0; i < len; i++) {
pBuf[i] = wizchip_read(address + i);
}
}
void Wiznet5100::wizchip_write(uint16_t address, uint8_t wb) {
wizchip_cs_select();
_spi.transfer(0xF0);
_spi.transfer((address & 0xFF00) >> 8);
_spi.transfer((address & 0x00FF) >> 0);
_spi.transfer(wb); // Data write (write 1byte data)
wizchip_cs_deselect();
}
void Wiznet5100::wizchip_write_word(uint16_t address, uint16_t word) {
wizchip_write(address, (uint8_t)(word >> 8));
wizchip_write(address + 1, (uint8_t)word);
}
void Wiznet5100::wizchip_write_buf(uint16_t address, const uint8_t* pBuf, uint16_t len) {
for (uint16_t i = 0; i < len; i++) {
wizchip_write(address + i, pBuf[i]);
}
}
void Wiznet5100::setSn_CR(uint8_t cr) {
// Write the command to the Command Register
wizchip_write(Sn_CR, cr);
// Now wait for the command to complete
while (wizchip_read(Sn_CR))
;
}
uint16_t Wiznet5100::getSn_TX_FSR() {
uint16_t val = 0, val1 = 0;
do {
val1 = wizchip_read_word(Sn_TX_FSR);
if (val1 != 0) {
val = wizchip_read_word(Sn_TX_FSR);
}
} while (val != val1);
return val;
}
uint16_t Wiznet5100::getSn_RX_RSR() {
uint16_t val = 0, val1 = 0;
do {
val1 = wizchip_read_word(Sn_RX_RSR);
if (val1 != 0) {
val = wizchip_read_word(Sn_RX_RSR);
}
} while (val != val1);
return val;
}
void Wiznet5100::wizchip_send_data(const uint8_t* wizdata, uint16_t len) {
uint16_t ptr;
uint16_t size;
uint16_t dst_mask;
uint16_t dst_ptr;
ptr = getSn_TX_WR();
dst_mask = ptr & TxBufferMask;
dst_ptr = TxBufferAddress + dst_mask;
if (dst_mask + len > TxBufferLength) {
size = TxBufferLength - dst_mask;
wizchip_write_buf(dst_ptr, wizdata, size);
wizdata += size;
size = len - size;
dst_ptr = TxBufferAddress;
wizchip_write_buf(dst_ptr, wizdata, size);
} else {
wizchip_write_buf(dst_ptr, wizdata, len);
}
ptr += len;
setSn_TX_WR(ptr);
}
void Wiznet5100::wizchip_recv_data(uint8_t* wizdata, uint16_t len) {
uint16_t ptr;
uint16_t size;
uint16_t src_mask;
uint16_t src_ptr;
ptr = getSn_RX_RD();
src_mask = ptr & RxBufferMask;
src_ptr = RxBufferAddress + src_mask;
if ((src_mask + len) > RxBufferLength) {
size = RxBufferLength - src_mask;
wizchip_read_buf(src_ptr, wizdata, size);
wizdata += size;
size = len - size;
src_ptr = RxBufferAddress;
wizchip_read_buf(src_ptr, wizdata, size);
} else {
wizchip_read_buf(src_ptr, wizdata, len);
}
ptr += len;
setSn_RX_RD(ptr);
}
void Wiznet5100::wizchip_recv_ignore(uint16_t len) {
uint16_t ptr;
ptr = getSn_RX_RD();
ptr += len;
setSn_RX_RD(ptr);
}
void Wiznet5100::wizchip_sw_reset() {
setMR(MR_RST);
getMR(); // for delay
setSHAR(_mac_address);
}
Wiznet5100::Wiznet5100(int8_t cs, SPIClass& spi, int8_t intr) : _spi(spi), _cs(cs) {
(void)intr;
}
bool Wiznet5100::begin(const uint8_t* mac_address, netif *net) {
memcpy(_mac_address, mac_address, 6);
_netif = net;
pinMode(_cs, OUTPUT);
wizchip_cs_deselect();
#if 0
_spi.begin();
_spi.setClockDivider(SPI_CLOCK_DIV4); // 4 MHz?
_spi.setBitOrder(MSBFIRST);
_spi.setDataMode(SPI_MODE0);
#endif
wizchip_sw_reset();
// Set the size of the Rx and Tx buffers
wizchip_write(RMSR, RxBufferSize);
wizchip_write(TMSR, TxBufferSize);
// Set our local MAC address
setSHAR(_mac_address);
// Open Socket 0 in MACRaw mode
setSn_MR(Sn_MR_MACRAW);
setSn_CR(Sn_CR_OPEN);
if (getSn_SR() != SOCK_MACRAW) {
// Failed to put socket 0 into MACRaw mode
return false;
}
// Success
return true;
}
void Wiznet5100::end() {
setSn_CR(Sn_CR_CLOSE);
// clear all interrupt of the socket
setSn_IR(0xFF);
// Wait for socket to change to closed
while (getSn_SR() != SOCK_CLOSED)
;
}
uint16_t Wiznet5100::readFrame(uint8_t* buffer, uint16_t bufsize) {
uint16_t data_len = readFrameSize();
if (data_len == 0) {
return 0;
}
if (data_len > bufsize) {
// Packet is bigger than buffer - drop the packet
discardFrame(data_len);
return 0;
}
return readFrameData(buffer, data_len);
}
uint16_t Wiznet5100::readFrameSize() {
uint16_t len = getSn_RX_RSR();
if (len == 0) {
return 0;
}
uint8_t head[2];
uint16_t data_len = 0;
wizchip_recv_data(head, 2);
setSn_CR(Sn_CR_RECV);
data_len = head[0];
data_len = (data_len << 8) + head[1];
data_len -= 2;
return data_len;
}
void Wiznet5100::discardFrame(uint16_t framesize) {
wizchip_recv_ignore(framesize);
setSn_CR(Sn_CR_RECV);
}
uint16_t Wiznet5100::readFrameData(uint8_t* buffer, uint16_t framesize) {
wizchip_recv_data(buffer, framesize);
setSn_CR(Sn_CR_RECV);
#if 1
// let lwIP deal with mac address filtering
return framesize;
#else
// W5100 doesn't have any built-in MAC address filtering
if ((buffer[0] & 0x01) || memcmp(&buffer[0], _mac_address, 6) == 0) {
// Addressed to an Ethernet multicast address or our unicast address
return framesize;
} else {
return 0;
}
#endif
}
uint16_t Wiznet5100::sendFrame(const uint8_t* buf, uint16_t len) {
// Wait for space in the transmit buffer
while (1) {
uint16_t freesize = getSn_TX_FSR();
if (getSn_SR() == SOCK_CLOSED) {
return -1;
}
if (len <= freesize) {
break;
}
};
wizchip_send_data(buf, len);
setSn_CR(Sn_CR_SEND);
while (1) {
uint8_t tmp = getSn_IR();
if (tmp & Sn_IR_SENDOK) {
setSn_IR(Sn_IR_SENDOK);
// Packet sent ok
break;
} else if (tmp & Sn_IR_TIMEOUT) {
setSn_IR(Sn_IR_TIMEOUT);
// There was a timeout
return -1;
}
}
return len;
}
This diff is collapsed.
/*
This sketch establishes a TCP connection to a "quote of the day" service.
It sends a "hello" message, and then prints received data.
*/
#include <W5500lwIP.h>
const char* host = "djxmmx.net";
const uint16_t port = 17;
Wiznet5500lwIP eth(1 /* chip select */);
void setup() {
// Set up SPI pinout to match your HW
SPI.setRX(0);
SPI.setCS(1);
SPI.setSCK(2);
SPI.setTX(3);
Serial.begin(115200);
delay(5000);
Serial.println();
Serial.println();
Serial.println("Starting Ethernet port");
// Start the Ethernet port
if (!eth.begin()) {
Serial.println("No wired Ethernet hardware detected. Check pinouts, wiring.");
while (1) {
delay(1000);
}
}
while (!eth.connected()) {
Serial.print(".");
delay(500);
}
Serial.println("");
Serial.println("Ethernet connected");
Serial.println("IP address: ");
Serial.println(eth.localIP());
}
void loop() {
static bool wait = false;
Serial.print("connecting to ");
Serial.print(host);
Serial.print(':');
Serial.println(port);
// Use WiFiClient class to create TCP connections
WiFiClient client;
if (!client.connect(host, port)) {
Serial.println("connection failed");
delay(5000);
return;
}
// This will send a string to the server
Serial.println("sending data to server");
if (client.connected()) {
client.println("hello from RP2040");
}
// wait for data to be available
unsigned long timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 5000) {
Serial.println(">>> Client Timeout !");
client.stop();
delay(60000);
return;
}
}
// Read all the lines of the reply from server and print them to Serial
Serial.println("receiving from remote server");
// not testing 'client.connected()' since we do not need to send data here
while (client.available()) {
char ch = static_cast<char>(client.read());
Serial.print(ch);
}
// Close the connection
Serial.println();
Serial.println("closing connection");
client.stop();
if (wait) {
delay(300000); // execute once every 5 minutes, don't flood remote service
}
wait = true;
}
#######################################
# Syntax Coloring Map
#######################################
#######################################
# Library (KEYWORD1)
#######################################
W5500lwIP KEYWORD1
Wiznet5500lwIP KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
#######################################
# Constants (LITERAL1)
#######################################
name=lwIP_w5500
version=1
author=Nicholas Humfrey
maintainer=esp8266/Arduino
sentence=Ethernet driver
paragraph=Wiznet5500 ethernet drivers for lwIP and esp8266 Arduino from https://github.com/njh/W5500MacRaw
category=Communication
url=https://github.com/esp8266/Arduino
architectures=rp2040
dot_a_linkage=true
#pragma once
#include <LwipIntfDev.h>
#include <utility/w5500.h>
#include <LwipEthernet.h>
#include <WiFi.h>
using Wiznet5500lwIP = LwipIntfDev<Wiznet5500>;
/*
Copyright (c) 2013, WIZnet Co., Ltd.
Copyright (c) 2016, Nicholas Humfrey
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// original sources: https://github.com/njh/W5500MacRaw
#include <SPI.h>
#include "w5500.h"
uint8_t Wiznet5500::wizchip_read(uint8_t block, uint16_t address) {
uint8_t ret;
wizchip_cs_select();
block |= AccessModeRead;
wizchip_spi_write_byte((address & 0xFF00) >> 8);
wizchip_spi_write_byte((address & 0x00FF) >> 0);
wizchip_spi_write_byte(block);
ret = wizchip_spi_read_byte();
wizchip_cs_deselect();
return ret;
}
uint16_t Wiznet5500::wizchip_read_word(uint8_t block, uint16_t address) {
return ((uint16_t)wizchip_read(block, address) << 8) + wizchip_read(block, address + 1);
}
void Wiznet5500::wizchip_read_buf(uint8_t block, uint16_t address, uint8_t* pBuf, uint16_t len) {
uint16_t i;
wizchip_cs_select();
block |= AccessModeRead;
wizchip_spi_write_byte((address & 0xFF00) >> 8);
wizchip_spi_write_byte((address & 0x00FF) >> 0);
wizchip_spi_write_byte(block);
for (i = 0; i < len; i++) {
pBuf[i] = wizchip_spi_read_byte();
}
wizchip_cs_deselect();
}
void Wiznet5500::wizchip_write(uint8_t block, uint16_t address, uint8_t wb) {
wizchip_cs_select();
block |= AccessModeWrite;
wizchip_spi_write_byte((address & 0xFF00) >> 8);
wizchip_spi_write_byte((address & 0x00FF) >> 0);
wizchip_spi_write_byte(block);
wizchip_spi_write_byte(wb);
wizchip_cs_deselect();
}
void Wiznet5500::wizchip_write_word(uint8_t block, uint16_t address, uint16_t word) {
wizchip_write(block, address, (uint8_t)(word >> 8));
wizchip_write(block, address + 1, (uint8_t)word);
}
void Wiznet5500::wizchip_write_buf(uint8_t block, uint16_t address, const uint8_t* pBuf,
uint16_t len) {
uint16_t i;
wizchip_cs_select();
block |= AccessModeWrite;
wizchip_spi_write_byte((address & 0xFF00) >> 8);
wizchip_spi_write_byte((address & 0x00FF) >> 0);
wizchip_spi_write_byte(block);
for (i = 0; i < len; i++) {
wizchip_spi_write_byte(pBuf[i]);
}
wizchip_cs_deselect();
}
void Wiznet5500::setSn_CR(uint8_t cr) {
// Write the command to the Command Register
wizchip_write(BlockSelectSReg, Sn_CR, cr);
// Now wait for the command to complete
while (wizchip_read(BlockSelectSReg, Sn_CR))
;
}
uint16_t Wiznet5500::getSn_TX_FSR() {
uint16_t val = 0, val1 = 0;
do {
val1 = wizchip_read_word(BlockSelectSReg, Sn_TX_FSR);
if (val1 != 0) {
val = wizchip_read_word(BlockSelectSReg, Sn_TX_FSR);
}
} while (val != val1);
return val;
}
uint16_t Wiznet5500::getSn_RX_RSR() {
uint16_t val = 0, val1 = 0;
do {
val1 = wizchip_read_word(BlockSelectSReg, Sn_RX_RSR);
if (val1 != 0) {
val = wizchip_read_word(BlockSelectSReg, Sn_RX_RSR);
}
} while (val != val1);
return val;
}
void Wiznet5500::wizchip_send_data(const uint8_t* wizdata, uint16_t len) {
uint16_t ptr = 0;
if (len == 0) {
return;
}
ptr = getSn_TX_WR();
wizchip_write_buf(BlockSelectTxBuf, ptr, wizdata, len);
ptr += len;
setSn_TX_WR(ptr);
}
void Wiznet5500::wizchip_recv_data(uint8_t* wizdata, uint16_t len) {
uint16_t ptr;
if (len == 0) {
return;
}
ptr = getSn_RX_RD();
wizchip_read_buf(BlockSelectRxBuf, ptr, wizdata, len);
ptr += len;
setSn_RX_RD(ptr);
}
void Wiznet5500::wizchip_recv_ignore(uint16_t len) {
uint16_t ptr;
ptr = getSn_RX_RD();
ptr += len;
setSn_RX_RD(ptr);
}
void Wiznet5500::wizchip_sw_reset() {
setMR(MR_RST);
getMR(); // for delay
setSHAR(_mac_address);
}
int8_t Wiznet5500::wizphy_getphylink() {
int8_t tmp;
if (getPHYCFGR() & PHYCFGR_LNK_ON) {
tmp = PHY_LINK_ON;
} else {
tmp = PHY_LINK_OFF;
}
return tmp;
}
int8_t Wiznet5500::wizphy_getphypmode() {
int8_t tmp = 0;
if (getPHYCFGR() & PHYCFGR_OPMDC_PDOWN) {
tmp = PHY_POWER_DOWN;
} else {
tmp = PHY_POWER_NORM;
}
return tmp;
}
void Wiznet5500::wizphy_reset() {
uint8_t tmp = getPHYCFGR();
tmp &= PHYCFGR_RST;
setPHYCFGR(tmp);
tmp = getPHYCFGR();
tmp |= ~PHYCFGR_RST;
setPHYCFGR(tmp);
}
int8_t Wiznet5500::wizphy_setphypmode(uint8_t pmode) {
uint8_t tmp = 0;
tmp = getPHYCFGR();
if ((tmp & PHYCFGR_OPMD) == 0) {
return -1;
}
tmp &= ~PHYCFGR_OPMDC_ALLA;
if (pmode == PHY_POWER_DOWN) {
tmp |= PHYCFGR_OPMDC_PDOWN;
} else {
tmp |= PHYCFGR_OPMDC_ALLA;
}
setPHYCFGR(tmp);
wizphy_reset();
tmp = getPHYCFGR();
if (pmode == PHY_POWER_DOWN) {
if (tmp & PHYCFGR_OPMDC_PDOWN) {
return 0;
}
} else {
if (tmp & PHYCFGR_OPMDC_ALLA) {
return 0;
}
}
return -1;
}
Wiznet5500::Wiznet5500(int8_t cs, SPIClass& spi, int8_t intr) : _spi(spi), _cs(cs) {
(void)intr;
}
bool Wiznet5500::begin(const uint8_t* mac_address, netif *net) {
_netif = net;
memcpy(_mac_address, mac_address, 6);
pinMode(_cs, OUTPUT);
wizchip_cs_deselect();
#if 0
_spi.begin();
_spi.setClockDivider(SPI_CLOCK_DIV4); // 4 MHz?
_spi.setBitOrder(MSBFIRST);
_spi.setDataMode(SPI_MODE0);
#endif
wizchip_sw_reset();
// Use the full 16Kb of RAM for Socket 0
setSn_RXBUF_SIZE(16);
setSn_TXBUF_SIZE(16);
// Set our local MAC address
setSHAR(_mac_address);
// Open Socket 0 in MACRaw mode
setSn_MR(Sn_MR_MACRAW);
setSn_CR(Sn_CR_OPEN);
if (getSn_SR() != SOCK_MACRAW) {
// Failed to put socket 0 into MACRaw mode
return false;
}
// Success
return true;
}
void Wiznet5500::end() {
setSn_CR(Sn_CR_CLOSE);
// clear all interrupt of the socket
setSn_IR(0xFF);
// Wait for socket to change to closed
while (getSn_SR() != SOCK_CLOSED)
;
}
uint16_t Wiznet5500::readFrame(uint8_t* buffer, uint16_t bufsize) {
uint16_t data_len = readFrameSize();
if (data_len == 0) {
return 0;
}
if (data_len > bufsize) {
// Packet is bigger than buffer - drop the packet
discardFrame(data_len);
return 0;
}
return readFrameData(buffer, data_len);
}
uint16_t Wiznet5500::readFrameSize() {
uint16_t len = getSn_RX_RSR();
if (len == 0) {
return 0;
}
uint8_t head[2];
uint16_t data_len = 0;
wizchip_recv_data(head, 2);
setSn_CR(Sn_CR_RECV);
data_len = head[0];
data_len = (data_len << 8) + head[1];
data_len -= 2;
return data_len;
}
void Wiznet5500::discardFrame(uint16_t framesize) {
wizchip_recv_ignore(framesize);
setSn_CR(Sn_CR_RECV);
}
uint16_t Wiznet5500::readFrameData(uint8_t* buffer, uint16_t framesize) {
wizchip_recv_data(buffer, framesize);
setSn_CR(Sn_CR_RECV);
#if 1
// let lwIP deal with mac address filtering
return framesize;
#else
// Had problems with W5500 MAC address filtering (the Sn_MR_MFEN option)
// Do it in software instead:
if ((buffer[0] & 0x01) || memcmp(&buffer[0], _mac_address, 6) == 0) {
// Addressed to an Ethernet multicast address or our unicast address
return framesize;
} else {
return 0;
}
#endif
}
uint16_t Wiznet5500::sendFrame(const uint8_t* buf, uint16_t len) {
// Wait for space in the transmit buffer
while (1) {
uint16_t freesize = getSn_TX_FSR();
if (getSn_SR() == SOCK_CLOSED) {
return -1;
}
if (len <= freesize) {
break;
}
};
wizchip_send_data(buf, len);
setSn_CR(Sn_CR_SEND);
while (1) {
uint8_t tmp = getSn_IR();
if (tmp & Sn_IR_SENDOK) {
setSn_IR(Sn_IR_SENDOK);
// Packet sent ok
break;
} else if (tmp & Sn_IR_TIMEOUT) {
setSn_IR(Sn_IR_TIMEOUT);
// There was a timeout
return -1;
}
}
return len;
}
This diff is collapsed.
......@@ -12,7 +12,8 @@ for dir in ./cores/rp2040 ./libraries/EEPROM ./libraries/I2S ./libraries/SingleF
./libraries/Joystick ./libraries/Keyboard ./libraries/Mouse \
./libraries/JoystickBT ./libraries/KeyboardBT ./variants ./libraries/BTstackLib \
./libraries/MouseBT ./libraries/SerialBT ./libraries/HID_Bluetooth \
./libraries/JoystickBLE ./libraries/KeyboardBLE ./libraries/MouseBLE ; do
./libraries/JoystickBLE ./libraries/KeyboardBLE ./libraries/MouseBLE \
./libraries/lwIP_w5500 ./libraries/lwIP_w5100 ./libraries/lwIP_enc28j60; do
find $dir -type f \( -name "*.c" -o -name "*.h" -o -name "*.cpp" \) -a \! -path '*api*' -exec astyle --suffix=none --options=./tests/astyle_core.conf \{\} \;
find $dir -type f -name "*.ino" -exec astyle --suffix=none --options=./tests/astyle_examples.conf \{\} \;
done
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment