Hardware components | ||||||
![]() |
| × | 1 | |||
Software apps and online services | ||||||
![]() |
|
In this project I will use W5100S-EVB-Pico for remote ping using Telegram bot. Project can be implemented using any Raspberry Pi Pico board and WIZnet Ethernet HAT too.
In my previous project I explained about W5100S socket-less functions. Please note that these functions are supported by W5100S and W6100 only.
Arduino Libraries usedin this project:
- Arduino-Pico (https://github.com/earlephilhower/arduino-pico)
- Ethernet library for Arduino (https://github.com/WIZnet-ArduinoEthernet/Ethernet/tree/W5100S-EVB-Pico)
- AsyncTelegram 2 (https://github.com/cotestatnt/AsyncTelegram2)
- SSLClient - required by AsyncTelegram (https://github.com/OPEnSLab-OSU/SSLClient)
First, create Telegram bot using this guide.
In AsyncTelegram library there is Ethernet example. As I am newbie, I used it as starting point, W5100S-EVB-Pico was selected as a board.
Second, following items need to be changed in code:
1. Ethernet.init(17); // Pin 17 is CS in W5100S-EVB-Pico
2. Enter proper Telegram bot token
3. Check LED pins
Next, compile and upload. During compile I received following warning, I am not very sure about reason but everything was running normally, so I just ignored it.
WARNING: library SSLClient claims to run on samd, sam, tivac, stm32, esp32 architecture(s) and may be incompatible with your current board which runs on rp2040 architecture(s).
C:\Users\...\Documents\Arduino\libraries\SSLClient\src\SSLClientParameters.cpp: In function 'void std::__throw_length_error(const char*)':
C:\Users\...\Documents\Arduino\libraries\SSLClient\src\SSLClientParameters.cpp:5:67: warning: 'noreturn' function does return
5 | void __attribute__((weak)) __throw_length_error(char const*) {}
|
After uploading to board I could check successful bot operation. It might not be clear, but green LED turns on when button "ON" is pressed.
1. As Ethernet library doesn't have this, I had to add a little code to Ethernet.cpp.
It is pretty straightforward, more information about flow can be found here: https://docs.wiznet.io/img/products/w5100s/application/w5100s_an_slc_v100e.pdf
uint8_t EthernetClass::ping(IPAddress ip)
{
static uint16_t RandomID = 0x1234;
static uint16_t RandomSeqNum = 0x4321;
static uint16_t PingRCR = 0x08;
static uint16_t PingRTR = 0x07d0;
uint8_t i, ret, rep = 0;
//configure ping request
W5100.write(0x005F, SLIR_TIMEOUT | SLIR_PING); //setSLIR
W5100.write(0x004F, PingRCR); //setSLRCR
W5100.write(0x004D,(uint8_t)(PingRTR >> 8)); //setSLRTR
W5100.write(0x004D+1,(uint8_t)(PingRTR)); //setSLRTR
W5100.write(0x005A,(uint8_t)(RandomID >> 8)); //setPINGSEQR
W5100.write(0x005A+1,(uint8_t)(RandomID)); //setPINGSEQR
W5100.write(0x005C,(uint8_t)(RandomSeqNum >> 8)); //setPINGIDR
W5100.write(0x005C+1,(uint8_t)(RandomSeqNum)); //setPINGIDR
for(i = 0; i<=3; i++)
{
//send_ping_request(s, addr);
W5100.write(0x0050,ip.raw_address(),4); //setSLPIPR
W5100.write(0x004C,SLCMD_PING); //setSLCR
delay(500);
while(1)
{
if(W5100.read(0x005F)&SLIR_PING)
{
W5100.write(0x005F, SLIR_PING);
rep++;
break;
}
else if(W5100.read(0x005F)&SLIR_TIMEOUT)
{
W5100.write(0x005F,SLIR_TIMEOUT);//setSLIR(SLIR_TIMEOUT);
break;
}
}
}
return rep;
}
And to Ethernet.h
#define SLIR_TIMEOUT (1<<2)
#define SLIR_PING (1<<0)
#define SLCMD_PING (1<<0)
uint8_t ping(IPAddress ip);
2. I added new command to Telegram bot that would accept IP as an argument and ping it.
Following is the part that I added to loop():
else if(tgReply.startsWith("/ping")){
tgReply.replace("/ping ","");
IPAddress ping_ip;
if (ping_ip.fromString(tgReply)){
char bufL[100];
int8_t ping_ret = Ethernet.ping(ping_ip);
snprintf(bufL, sizeof(bufL), "Ping statistics for %d.%d.%d.%d\nPackets: Sent = 4, Received = %d, Lost = %d (%d%% of loss)\n", ping_ip[0],ping_ip[1],ping_ip[2],ping_ip[3],ping_ret, 4 - ping_ret, (1 - ping_ret/4) * 100);
Serial.print(bufL);
myBot.sendMessage(msg, bufL);
}
else{
myBot.sendMessage(msg, "Check entered IP");
Serial.println("unparsable IP");
}
}
Final result:
Although this is simple project, I hope it can help to understand more about W5100S and how it can be used in various projects.
Thanks for reading till the end. Should you have any questions, do not hesitate to leave it in comment!
#include <SPI.h>
#include <Ethernet.h>
#include <SSLClient.h>
#include <AsyncTelegram2.h>
#include <tg_certificate.h>
const char* token = "";
// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
// Set the static IP address to use if the DHCP fails to assign
IPAddress ip(192, 168, 1, 25);
IPAddress myDns(192, 168, 1, 1);
EthernetClient base_client;
SSLClient client(base_client, TAs, (size_t)TAs_NUM, A0, 1, SSLClient::SSL_ERROR );
AsyncTelegram2 myBot(client);
ReplyKeyboard myReplyKbd; // reply keyboard object helper
InlineKeyboard myInlineKbd, myInlineKbd2; // inline keyboard object helper
bool isKeyboardActive; // store if the reply keyboard is shown
#define LIGHT_ON_CALLBACK "lightON" // callback data sent when "LIGHT ON" button is pressed
#define LIGHT_OFF_CALLBACK "lightOFF" // callback data sent when "LIGHT OFF" button is pressed
#define BUTTON1_CALLBACK "Button1" // callback data sent when "Button1" button is pressed
#define BUTTON2_CALLBACK "Button2" // callback data sent when "Button1" button is pressed
const byte BLINK_LED = 2;
const byte LED = 3;
void button1Pressed(const TBMessage &queryMsg){
Serial.printf("\nButton 1 pressed (callback); \nQueryId: %s\n\n", queryMsg.callbackQueryID);
myBot.endQuery(queryMsg, "You pressed Button 1", true);
}
void button2Pressed(const TBMessage &queryMsg){
Serial.printf("\nButton 2 pressed (callback); \nQueryId: %s\n\n", queryMsg.callbackQueryID);
myBot.endQuery(queryMsg, "You pressed Button 2", false);
}
void setup() {
pinMode(BLINK_LED, OUTPUT);
pinMode(LED, OUTPUT);
pinMode(10, OUTPUT);
SPI.begin();
// You can use Ethernet.init(pin) to configure the CS pin
Ethernet.init(17); // Most Arduino shields
// Open serial communications and wait for port to open:
Serial.begin(115200);
// start the Ethernet connection:
Serial.println("Initialize Ethernet with DHCP:");
if (Ethernet.begin(mac) == 0) {
Serial.println("Failed to configure Ethernet using DHCP");
// Check for Ethernet hardware present
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :(");
}
if (Ethernet.linkStatus() == LinkOFF) {
Serial.println("Ethernet cable is not connected.");
}
// try to congifure using IP address instead of DHCP:
Ethernet.begin(mac, ip, myDns);
} else {
Serial.print(" DHCP assigned IP ");
Serial.println(Ethernet.localIP());
}
// give the Ethernet shield a second to initialize:
delay(1000);
// Set the Telegram bot properies
myBot.setUpdateTime(3000);
myBot.setTelegramToken(token);
// Check if all things are ok
Serial.print("\nTest Telegram connection... ");
myBot.begin() ? Serial.println("OK") : Serial.println("NOK");
// Add reply keyboard
isKeyboardActive = false;
// add a button that send a message with "Simple button" text
myReplyKbd.addButton("Button1");
myReplyKbd.addButton("Button2");
myReplyKbd.addButton("Button3");
// add a new empty button row
myReplyKbd.addRow();
// add another button that send the user position (location)
myReplyKbd.addButton("Send Location", KeyboardButtonLocation);
// add another button that send the user contact
myReplyKbd.addButton("Send contact", KeyboardButtonContact);
// add a new empty button row
myReplyKbd.addRow();
// add a button that send a message with "Hide replyKeyboard" text
// (it will be used to hide the reply keyboard)
myReplyKbd.addButton("/hide_keyboard");
// resize the keyboard to fit only the needed space
myReplyKbd.enableResize();
// Add sample inline keyboard
myInlineKbd.addButton("ON", LIGHT_ON_CALLBACK, KeyboardButtonQuery);
myInlineKbd.addButton("OFF", LIGHT_OFF_CALLBACK, KeyboardButtonQuery);
myInlineKbd.addRow();
myInlineKbd.addButton("GitHub", "https://github.com/cotestatnt/AsyncTelegram2/", KeyboardButtonURL);
myBot.addInlineKeyboard(&myInlineKbd);
// Add another inline keyboard
myInlineKbd2.addButton("Button 1", BUTTON1_CALLBACK, KeyboardButtonQuery, button1Pressed);
myInlineKbd2.addButton("Button 2", BUTTON2_CALLBACK, KeyboardButtonQuery, button2Pressed);
// Add pointer to this keyboard to bot (in order to run callback function)
myBot.addInlineKeyboard(&myInlineKbd2);
// Send a welcome message to specific user
char welcome_msg[128];
snprintf(welcome_msg, 128, "BOT @%s online\n/help all commands avalaible.", myBot.getBotName());
int32_t chat_id = 436865110; // You can discover your own chat id, with "Json Dump Bot"
myBot.sendTo(chat_id, welcome_msg);
}
void loop() {
static uint32_t ledTime = millis();
if (millis() - ledTime > 200) {
ledTime = millis();
digitalWrite(BLINK_LED, !digitalRead(BLINK_LED));
}
// a variable to store telegram message data
TBMessage msg;
// if there is an incoming message...
if (myBot.getNewMessage(msg)) {
// check what kind of message I received
String tgReply;
MessageType msgType = msg.messageType;
switch (msgType) {
case MessageText :
// received a text message
tgReply = msg.text;
Serial.print("\nText message received: ");
Serial.println(tgReply);
// check if is show keyboard command
if (tgReply.equalsIgnoreCase("/reply_keyboard")) {
// the user is asking to show the reply keyboard --> show it
myBot.sendMessage(msg, "This is reply keyboard:", myReplyKbd);
isKeyboardActive = true;
}
else if (tgReply.equalsIgnoreCase("/inline_keyboard")) {
myBot.sendMessage(msg, "This is inline keyboard:", myInlineKbd);
}
else if (tgReply.equalsIgnoreCase("/inline_keyboard2")) {
myBot.sendMessage(msg, "This is inline keyboard 2:", myInlineKbd2);
}
// check if the reply keyboard is active
else if (isKeyboardActive) {
// is active -> manage the text messages sent by pressing the reply keyboard buttons
if (tgReply.equalsIgnoreCase("/hide_keyboard")) {
// sent the "hide keyboard" message --> hide the reply keyboard
myBot.removeReplyKeyboard(msg, "Reply keyboard removed");
isKeyboardActive = false;
} else {
// print every others messages received
myBot.sendMessage(msg, msg.text);
}
}
else if(tgReply.startsWith("/ping")){
tgReply.replace("/ping ","");
IPAddress ping_ip;
if (ping_ip.fromString(tgReply)){
char bufL[100];
int8_t ping_ret = Ethernet.ping(ping_ip);
snprintf(bufL, sizeof(bufL), "Ping statistics for %d.%d.%d.%d\nPackets: Sent = 4, Received = %d, Lost = %d (%d%% of loss)\n", ping_ip[0],ping_ip[1],ping_ip[2],ping_ip[3],ping_ret, 4 - ping_ret, (1 - ping_ret/4) * 100);
Serial.print(bufL);
myBot.sendMessage(msg, bufL);
}
else{
myBot.sendMessage(msg, "Check entered IP");
Serial.println("unparsable IP");
}
}
// the user write anything else and the reply keyboard is not active --> show a hint message
else {
myBot.sendMessage(msg, "Try /reply_keyboard or /inline_keyboard or /inline_keyboard2");
}
break;
case MessageQuery:
// received a callback query message
tgReply = msg.callbackQueryData;
Serial.print("\nCallback query message received: ");
Serial.println(tgReply);
if (tgReply.equalsIgnoreCase(LIGHT_ON_CALLBACK)) {
// pushed "LIGHT ON" button...
Serial.println("\nSet light ON");
digitalWrite(LED, HIGH);
// terminate the callback with an alert message
myBot.endQuery(msg, "Light is on", true);
myBot.forwardMessage(msg, 1345177361);
}
else if (tgReply.equalsIgnoreCase(LIGHT_OFF_CALLBACK)) {
// pushed "LIGHT OFF" button...
Serial.println("\nSet light OFF");
digitalWrite(LED, LOW);
// terminate the callback with a popup message
myBot.endQuery(msg, "Light is off");
}
break;
case MessageLocation:
// received a location message --> send a message with the location coordinates
char bufL[50];
snprintf(bufL, sizeof(bufL), "Longitude: %f\nLatitude: %f\n", msg.location.longitude, msg.location.latitude) ;
myBot.sendMessage(msg, bufL);
Serial.println(bufL);
break;
case MessageContact:
char bufC[50];
snprintf(bufC, sizeof(bufC), "Contact information received: %s - %s\n", msg.contact.firstName, msg.contact.phoneNumber ) ;
// received a contact message --> send a message with the contact information
myBot.sendMessage(msg, bufC);
Serial.println(bufC);
break;
default:
break;
}
}
}
/* Copyright 2018 Paul Stoffregen
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <Arduino.h>
#include "Ethernet.h"
#include "utility/w5100.h"
#include "Dhcp.h"
IPAddress EthernetClass::_dnsServerAddress;
DhcpClass* EthernetClass::_dhcp = NULL;
int EthernetClass::begin(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout)
{
static DhcpClass s_dhcp;
_dhcp = &s_dhcp;
// Initialise the basic info
if (W5100.init() == 0) return 0;
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
W5100.setMACAddress(mac);
W5100.setIPAddress(IPAddress(0,0,0,0).raw_address());
SPI.endTransaction();
// Now try to get our config info from a DHCP server
int ret = _dhcp->beginWithDHCP(mac, timeout, responseTimeout);
if (ret == 1) {
// We've successfully found a DHCP server and got our configuration
// info, so set things accordingly
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
W5100.setIPAddress(_dhcp->getLocalIp().raw_address());
W5100.setGatewayIp(_dhcp->getGatewayIp().raw_address());
W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address());
SPI.endTransaction();
_dnsServerAddress = _dhcp->getDnsServerIp();
socketPortRand(micros());
}
return ret;
}
void EthernetClass::begin(uint8_t *mac, IPAddress ip)
{
// Assume the DNS server will be the machine on the same network as the local IP
// but with last octet being '1'
IPAddress dns = ip;
dns[3] = 1;
begin(mac, ip, dns);
}
void EthernetClass::begin(uint8_t *mac, IPAddress ip, IPAddress dns)
{
// Assume the gateway will be the machine on the same network as the local IP
// but with last octet being '1'
IPAddress gateway = ip;
gateway[3] = 1;
begin(mac, ip, dns, gateway);
}
void EthernetClass::begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway)
{
IPAddress subnet(255, 255, 255, 0);
begin(mac, ip, dns, gateway, subnet);
}
void EthernetClass::begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet)
{
if (W5100.init() == 0) return;
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
W5100.setMACAddress(mac);
#if ARDUINO > 106 || TEENSYDUINO > 121
W5100.setIPAddress(ip._address.bytes);
W5100.setGatewayIp(gateway._address.bytes);
W5100.setSubnetMask(subnet._address.bytes);
#else
W5100.setIPAddress(ip._address);
W5100.setGatewayIp(gateway._address);
W5100.setSubnetMask(subnet._address);
#endif
SPI.endTransaction();
_dnsServerAddress = dns;
}
void EthernetClass::init(uint8_t sspin)
{
W5100.setSS(sspin);
}
EthernetLinkStatus EthernetClass::linkStatus()
{
switch (W5100.getLinkStatus()) {
case UNKNOWN: return Unknown;
case LINK_ON: return LinkON;
case LINK_OFF: return LinkOFF;
default: return Unknown;
}
}
EthernetHardwareStatus EthernetClass::hardwareStatus()
{
switch (W5100.getChip()) {
case 50: return EthernetW5100S;
case 51: return EthernetW5100;
case 52: return EthernetW5200;
case 55: return EthernetW5500;
case 61: return EthernetW6100;
default: return EthernetNoHardware;
}
}
int EthernetClass::maintain()
{
int rc = DHCP_CHECK_NONE;
if (_dhcp != NULL) {
// we have a pointer to dhcp, use it
rc = _dhcp->checkLease();
switch (rc) {
case DHCP_CHECK_NONE:
//nothing done
break;
case DHCP_CHECK_RENEW_OK:
case DHCP_CHECK_REBIND_OK:
//we might have got a new IP.
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
W5100.setIPAddress(_dhcp->getLocalIp().raw_address());
W5100.setGatewayIp(_dhcp->getGatewayIp().raw_address());
W5100.setSubnetMask(_dhcp->getSubnetMask().raw_address());
SPI.endTransaction();
_dnsServerAddress = _dhcp->getDnsServerIp();
break;
default:
//this is actually an error, it will retry though
break;
}
}
return rc;
}
void EthernetClass::MACAddress(uint8_t *mac_address)
{
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
W5100.getMACAddress(mac_address);
SPI.endTransaction();
}
IPAddress EthernetClass::localIP()
{
IPAddress ret;
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
W5100.getIPAddress(ret.raw_address());
SPI.endTransaction();
return ret;
}
IPAddress EthernetClass::subnetMask()
{
IPAddress ret;
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
W5100.getSubnetMask(ret.raw_address());
SPI.endTransaction();
return ret;
}
IPAddress EthernetClass::gatewayIP()
{
IPAddress ret;
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
W5100.getGatewayIp(ret.raw_address());
SPI.endTransaction();
return ret;
}
void EthernetClass::setMACAddress(const uint8_t *mac_address)
{
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
W5100.setMACAddress(mac_address);
SPI.endTransaction();
}
void EthernetClass::setLocalIP(const IPAddress local_ip)
{
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
IPAddress ip = local_ip;
W5100.setIPAddress(ip.raw_address());
SPI.endTransaction();
}
void EthernetClass::setSubnetMask(const IPAddress subnet)
{
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
IPAddress ip = subnet;
W5100.setSubnetMask(ip.raw_address());
SPI.endTransaction();
}
void EthernetClass::setGatewayIP(const IPAddress gateway)
{
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
IPAddress ip = gateway;
W5100.setGatewayIp(ip.raw_address());
SPI.endTransaction();
}
void EthernetClass::setRetransmissionTimeout(uint16_t milliseconds)
{
if (milliseconds > 6553) milliseconds = 6553;
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
W5100.setRetransmissionTime(milliseconds * 10);
SPI.endTransaction();
}
void EthernetClass::setRetransmissionCount(uint8_t num)
{
SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
W5100.setRetransmissionCount(num);
SPI.endTransaction();
}
uint8_t EthernetClass::ping(IPAddress ip)
{
static uint16_t RandomID = 0x1234;
static uint16_t RandomSeqNum = 0x4321;
static uint16_t PingRCR = 0x08;
static uint16_t PingRTR = 0x07d0;
uint8_t i, ret, rep = 0;
//configure ping request
W5100.write(0x005F, SLIR_TIMEOUT | SLIR_PING); //setSLIR
W5100.write(0x004F, PingRCR); //setSLRCR
W5100.write(0x004D,(uint8_t)(PingRTR >> 8)); //setSLRTR
W5100.write(0x004D+1,(uint8_t)(PingRTR)); //setSLRTR
W5100.write(0x005A,(uint8_t)(RandomID >> 8)); //setPINGSEQR
W5100.write(0x005A+1,(uint8_t)(RandomID)); //setPINGSEQR
W5100.write(0x005C,(uint8_t)(RandomSeqNum >> 8)); //setPINGIDR
W5100.write(0x005C+1,(uint8_t)(RandomSeqNum)); //setPINGIDR
for(i = 0; i<=3; i++)
{
//send_ping_request(s, addr);
W5100.write(0x0050,ip.raw_address(),4); //setSLPIPR
W5100.write(0x004C,SLCMD_PING); //setSLCR
delay(500);
while(1)
{
if(W5100.read(0x005F)&SLIR_PING)
{
W5100.write(0x005F, SLIR_PING);
rep++;
break;
}
else if(W5100.read(0x005F)&SLIR_TIMEOUT)
{
W5100.write(0x005F,SLIR_TIMEOUT);//setSLIR(SLIR_TIMEOUT);
break;
}
}
}
return rep;
}
EthernetClass Ethernet;
/* Copyright 2018 Paul Stoffregen
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef ethernet_h_
#define ethernet_h_
// All symbols exposed to Arduino sketches are contained in this header file
//
// Older versions had much of this stuff in EthernetClient.h, EthernetServer.h,
// and socket.h. Including headers in different order could cause trouble, so
// these "friend" classes are now defined in the same header file. socket.h
// was removed to avoid possible conflict with the C library header files.
// Configure the maximum number of sockets to support. W5100 chips can have
// up to 4 sockets. W5200 & W5500 can have up to 8 sockets. Several bytes
// of RAM are used for each socket. Reducing the maximum can save RAM, but
// you are limited to fewer simultaneous connections.
#if defined(RAMEND) && defined(RAMSTART) && ((RAMEND - RAMSTART) <= 2048)
#define MAX_SOCK_NUM 4
#else
#define MAX_SOCK_NUM 8
#endif
// By default, each socket uses 2K buffers inside the Wiznet chip. If
// MAX_SOCK_NUM is set to fewer than the chip's maximum, uncommenting
// this will use larger buffers within the Wiznet chip. Large buffers
// can really help with UDP protocols like Artnet. In theory larger
// buffers should allow faster TCP over high-latency links, but this
// does not always seem to work in practice (maybe Wiznet bugs?)
//#define ETHERNET_LARGE_BUFFERS
//#define USE_SERIAL_DEBUG_PRINT
#ifdef USE_SERIAL_DEBUG_PRINT
#define PRINTLINE_DEF(var) \
PRINTLINE(); \
Serial.println("PRINTLINE_DEF("#var")");
#else
#define PRINTLINE_DEF(var)
#endif
#ifdef USE_SERIAL_DEBUG_PRINT
#define PRINTLINE() \
Serial.print("\r\n"); \
Serial.print(__FILE__); \
Serial.print(" "); \
Serial.println(__LINE__);
#else
#define PRINTLINE()
#endif
#ifdef USE_SERIAL_DEBUG_PRINT
#define PRINTVAR_HEX(var) \
PRINTLINE(); \
Serial.print("PRINTVAR_HEX("#var")"); \
Serial.print(" = 0x"); \
Serial.println(var, HEX);
#else
#define PRINTVAR_HEX(var)
#endif
#ifdef USE_SERIAL_DEBUG_PRINT
#define PRINTVAR_HEXD(var1, var2) \
PRINTLINE(); \
Serial.print("PRINTVAR_HEXD("#var1", "#var2")"); \
Serial.print(" = 0x"); \
Serial.print(var1, HEX); \
Serial.print(" = 0x"); \
Serial.println(var2, HEX);
#else
#define PRINTVAR_HEXD(var1, var2)
#endif
#ifdef USE_SERIAL_DEBUG_PRINT
#define PRINTVAR_HEXT(var1, var2, var3) \
PRINTLINE(); \
Serial.print("PRINTVAR_HEXT("#var1", "#var2", "#var3")"); \
Serial.print(" = 0x"); \
Serial.print(var1, HEX); \
Serial.print(" = 0x"); \
Serial.print(var2, HEX); \
Serial.print(" = 0x"); \
Serial.println(var3, HEX);
#else
#define PRINTVAR_HEXT(var1, var2, var3)
#endif
#ifdef USE_SERIAL_DEBUG_PRINT
#define PRINTVAR(var) \
PRINTLINE(); \
Serial.print("PRINTVAR("#var")"); \
Serial.print(" = "); \
Serial.println(var);
#else
#define PRINTVAR(var)
#endif
#ifdef USE_SERIAL_DEBUG_PRINT
#define PRINTSTR(var) \
PRINTLINE(); \
Serial.println("PRINTVAR_STR("#var")");
#else
#define PRINTSTR(var)
#endif
#include <Arduino.h>
#include "Client.h"
#include "Server.h"
#include "Udp.h"
enum EthernetLinkStatus {
Unknown,
LinkON,
LinkOFF
};
enum EthernetHardwareStatus {
EthernetNoHardware,
EthernetW5100,
EthernetW5200,
EthernetW5500,
EthernetW6100,
EthernetW5100S
};
class EthernetUDP;
class EthernetClient;
class EthernetServer;
class DhcpClass;
class EthernetClass {
private:
static IPAddress _dnsServerAddress;
static DhcpClass* _dhcp;
public:
// Initialise the Ethernet shield to use the provided MAC address and
// gain the rest of the configuration through DHCP.
// Returns 0 if the DHCP configuration failed, and 1 if it succeeded
static int begin(uint8_t *mac, unsigned long timeout = 60000, unsigned long responseTimeout = 4000);
static int maintain();
static EthernetLinkStatus linkStatus();
static EthernetHardwareStatus hardwareStatus();
// Manaul configuration
static void begin(uint8_t *mac, IPAddress ip);
static void begin(uint8_t *mac, IPAddress ip, IPAddress dns);
static void begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway);
static void begin(uint8_t *mac, IPAddress ip, IPAddress dns, IPAddress gateway, IPAddress subnet);
static void init(uint8_t sspin = 10);
#define SLIR_TIMEOUT (1<<2)
#define SLIR_PING (1<<0)
#define SLCMD_PING (1<<0)
uint8_t ping(IPAddress ip);
static void MACAddress(uint8_t *mac_address);
static IPAddress localIP();
static IPAddress subnetMask();
static IPAddress gatewayIP();
static IPAddress dnsServerIP() { return _dnsServerAddress; }
void setMACAddress(const uint8_t *mac_address);
void setLocalIP(const IPAddress local_ip);
void setSubnetMask(const IPAddress subnet);
void setGatewayIP(const IPAddress gateway);
void setDnsServerIP(const IPAddress dns_server) { _dnsServerAddress = dns_server; }
void setRetransmissionTimeout(uint16_t milliseconds);
void setRetransmissionCount(uint8_t num);
friend class EthernetClient;
friend class EthernetServer;
friend class EthernetUDP;
private:
// Opens a socket(TCP or UDP or IP_RAW mode)
static uint8_t socketBegin(uint8_t protocol, uint16_t port);
static uint8_t socketBeginMulticast(uint8_t protocol, IPAddress ip,uint16_t port);
static uint8_t socketStatus(uint8_t s);
// Close socket
static void socketClose(uint8_t s);
// Establish TCP connection (Active connection)
static void socketConnect(uint8_t s, uint8_t * addr, uint16_t port);
// disconnect the connection
static void socketDisconnect(uint8_t s);
// Establish TCP connection (Passive connection)
static uint8_t socketListen(uint8_t s);
// Send data (TCP)
static uint16_t socketSend(uint8_t s, const uint8_t * buf, uint16_t len);
static uint16_t socketSendAvailable(uint8_t s);
// Receive data (TCP)
static int socketRecv(uint8_t s, uint8_t * buf, int16_t len);
static uint16_t socketRecvAvailable(uint8_t s);
static uint8_t socketPeek(uint8_t s);
// sets up a UDP datagram, the data for which will be provided by one
// or more calls to bufferData and then finally sent with sendUDP.
// return true if the datagram was successfully set up, or false if there was an error
static bool socketStartUDP(uint8_t s, uint8_t* addr, uint16_t port);
// copy up to len bytes of data from buf into a UDP datagram to be
// sent later by sendUDP. Allows datagrams to be built up from a series of bufferData calls.
// return Number of bytes successfully buffered
static uint16_t socketBufferData(uint8_t s, uint16_t offset, const uint8_t* buf, uint16_t len);
// Send a UDP datagram built up from a sequence of startUDP followed by one or more
// calls to bufferData.
// return true if the datagram was successfully sent, or false if there was an error
static bool socketSendUDP(uint8_t s);
// Initialize the "random" source port number
static void socketPortRand(uint16_t n);
};
extern EthernetClass Ethernet;
#define UDP_TX_PACKET_MAX_SIZE 24
class EthernetUDP : public UDP {
private:
uint16_t _port; // local port to listen on
IPAddress _remoteIP; // remote IP address for the incoming packet whilst it's being processed
uint16_t _remotePort; // remote port for the incoming packet whilst it's being processed
uint16_t _offset; // offset into the packet being sent
protected:
uint8_t sockindex;
uint16_t _remaining; // remaining bytes of incoming packet yet to be processed
public:
EthernetUDP() : sockindex(MAX_SOCK_NUM) {} // Constructor
virtual uint8_t begin(uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use
virtual uint8_t beginMulticast(IPAddress, uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use
virtual void stop(); // Finish with the UDP socket
// Sending UDP packets
// Start building up a packet to send to the remote host specific in ip and port
// Returns 1 if successful, 0 if there was a problem with the supplied IP address or port
virtual int beginPacket(IPAddress ip, uint16_t port);
// Start building up a packet to send to the remote host specific in host and port
// Returns 1 if successful, 0 if there was a problem resolving the hostname or port
virtual int beginPacket(const char *host, uint16_t port);
// Finish off this packet and send it
// Returns 1 if the packet was sent successfully, 0 if there was an error
virtual int endPacket();
// Write a single byte into the packet
virtual size_t write(uint8_t);
// Write size bytes from buffer into the packet
virtual size_t write(const uint8_t *buffer, size_t size);
using Print::write;
// Start processing the next available incoming packet
// Returns the size of the packet in bytes, or 0 if no packets are available
virtual int parsePacket();
// Number of bytes remaining in the current packet
virtual int available();
// Read a single byte from the current packet
virtual int read();
// Read up to len bytes from the current packet and place them into buffer
// Returns the number of bytes read, or 0 if none are available
virtual int read(unsigned char* buffer, size_t len);
// Read up to len characters from the current packet and place them into buffer
// Returns the number of characters read, or 0 if none are available
virtual int read(char* buffer, size_t len) { return read((unsigned char*)buffer, len); };
// Return the next byte from the current packet without moving on to the next byte
virtual int peek();
virtual void flush(); // Finish reading the current packet
// Return the IP address of the host who sent the current incoming packet
virtual IPAddress remoteIP() { return _remoteIP; };
// Return the port of the host who sent the current incoming packet
virtual uint16_t remotePort() { return _remotePort; };
virtual uint16_t localPort() { return _port; }
};
class EthernetClient : public Client {
public:
EthernetClient() : sockindex(MAX_SOCK_NUM), _timeout(1000) { }
EthernetClient(uint8_t s) : sockindex(s), _timeout(1000) { }
uint8_t status();
virtual int connect(IPAddress ip, uint16_t port);
virtual int connect(const char *host, uint16_t port);
virtual int availableForWrite(void);
virtual size_t write(uint8_t);
virtual size_t write(const uint8_t *buf, size_t size);
virtual int available();
virtual int read();
virtual int read(uint8_t *buf, size_t size);
virtual int peek();
virtual void flush();
virtual void stop();
virtual uint8_t connected();
virtual operator bool() { return sockindex < MAX_SOCK_NUM; }
virtual bool operator==(const bool value) { return bool() == value; }
virtual bool operator!=(const bool value) { return bool() != value; }
virtual bool operator==(const EthernetClient&);
virtual bool operator!=(const EthernetClient& rhs) { return !this->operator==(rhs); }
uint8_t getSocketNumber() const { return sockindex; }
virtual uint16_t localPort();
virtual IPAddress remoteIP();
virtual uint16_t remotePort();
virtual void setConnectionTimeout(uint16_t timeout) { _timeout = timeout; }
friend class EthernetServer;
using Print::write;
private:
uint8_t sockindex; // MAX_SOCK_NUM means client not in use
uint16_t _timeout;
};
class EthernetServer : public Server {
private:
uint16_t _port;
public:
EthernetServer(uint16_t port) : _port(port) { }
EthernetClient available();
EthernetClient accept();
virtual void begin();
virtual size_t write(uint8_t);
virtual size_t write(const uint8_t *buf, size_t size);
virtual operator bool();
using Print::write;
//void statusreport();
// TODO: make private when socket allocation moves to EthernetClass
static uint16_t server_port[MAX_SOCK_NUM];
};
class DhcpClass {
private:
uint32_t _dhcpInitialTransactionId;
uint32_t _dhcpTransactionId;
uint8_t _dhcpMacAddr[6];
#ifdef __arm__
uint8_t _dhcpLocalIp[4] __attribute__((aligned(4)));
uint8_t _dhcpSubnetMask[4] __attribute__((aligned(4)));
uint8_t _dhcpGatewayIp[4] __attribute__((aligned(4)));
uint8_t _dhcpDhcpServerIp[4] __attribute__((aligned(4)));
uint8_t _dhcpDnsServerIp[4] __attribute__((aligned(4)));
#else
uint8_t _dhcpLocalIp[4];
uint8_t _dhcpSubnetMask[4];
uint8_t _dhcpGatewayIp[4];
uint8_t _dhcpDhcpServerIp[4];
uint8_t _dhcpDnsServerIp[4];
#endif
uint32_t _dhcpLeaseTime;
uint32_t _dhcpT1, _dhcpT2;
uint32_t _renewInSec;
uint32_t _rebindInSec;
unsigned long _timeout;
unsigned long _responseTimeout;
unsigned long _lastCheckLeaseMillis;
uint8_t _dhcp_state;
EthernetUDP _dhcpUdpSocket;
int request_DHCP_lease();
void reset_DHCP_lease();
void presend_DHCP();
void send_DHCP_MESSAGE(uint8_t, uint16_t);
void printByte(char *, uint8_t);
uint8_t parseDHCPResponse(unsigned long responseTimeout, uint32_t& transactionId);
public:
IPAddress getLocalIp();
IPAddress getSubnetMask();
IPAddress getGatewayIp();
IPAddress getDhcpServerIp();
IPAddress getDnsServerIp();
int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000);
int checkLease();
};
#endif
Comments
Please log in or sign up to comment.