1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-19 23:22:16 +03:00

Initial UDP support

This commit is contained in:
Ivan Grokhotkov 2015-01-09 02:10:20 +03:00
parent af99a3e388
commit e5e6dbe222
4 changed files with 507 additions and 189 deletions

View File

@ -0,0 +1,190 @@
/*
WiFiUdp.cpp - Library for Arduino Wifi shield.
Copyright (c) 2011-2014 Arduino. All right reserved.
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
*/
#define LWIP_INTERNAL
extern "C"
{
#include "include/wl_definitions.h"
#include "osapi.h"
#include "ets_sys.h"
}
#include "debug.h"
#include "ESP8266WiFi.h"
#include "WiFiUdp.h"
#include "lwip/opt.h"
#include "lwip/udp.h"
#include "lwip/inet.h"
#include "include/UdpContext.h"
/* Constructor */
WiFiUDP::WiFiUDP() : _ctx(0) {}
WiFiUDP::WiFiUDP(const WiFiUDP& other)
{
_ctx = other._ctx;
if (_ctx)
_ctx->ref();
}
WiFiUDP& WiFiUDP::operator=(const WiFiUDP& rhs)
{
_ctx = rhs._ctx;
if (_ctx)
_ctx->ref();
}
WiFiUDP::~WiFiUDP()
{
if (_ctx)
_ctx->unref();
}
/* Start WiFiUDP socket, listening at local port */
uint8_t WiFiUDP::begin(uint16_t port)
{
if (_ctx)
{
_ctx->unref();
}
_ctx = new UdpContext;
ip_addr_t addr;
addr.addr = INADDR_ANY;
return (_ctx->listen(addr, port)) ? 1 : 0;
}
/* return number of bytes available in the current packet,
will return zero if parsePacket hasn't been called yet */
int WiFiUDP::available() {
if (!_ctx)
return 0;
return static_cast<int>(_ctx->getSize());
}
/* Release any resources being used by this WiFiUDP instance */
void WiFiUDP::stop()
{
if (_ctx)
_ctx->disconnect();
_ctx->unref();
_ctx = 0;
}
int WiFiUDP::beginPacket(const char *host, uint16_t port)
{
IPAddress remote_addr;
if (WiFi.hostByName(host, remote_addr))
{
return beginPacket(remote_addr, port);
}
return 0;
}
int WiFiUDP::beginPacket(IPAddress ip, uint16_t port)
{
ip_addr_t addr;
addr.addr = ip;
if (_ctx)
_ctx->unref();
_ctx = new UdpContext;
return (_ctx->connect(addr, port)) ? 1 : 0;
}
int WiFiUDP::endPacket()
{
if (!_ctx)
return 0;
_ctx->send();
}
size_t WiFiUDP::write(uint8_t byte)
{
return write(&byte, 1);
}
size_t WiFiUDP::write(const uint8_t *buffer, size_t size)
{
if (!_ctx)
return 0;
return _ctx->append(reinterpret_cast<const char*>(buffer), size);
}
int WiFiUDP::parsePacket()
{
if (!_ctx)
return 0;
if (!_ctx->next())
return 0;
return _ctx->getSize();
}
int WiFiUDP::read()
{
if (!_ctx)
return -1;
return _ctx->read();
}
int WiFiUDP::read(unsigned char* buffer, size_t len)
{
if (!_ctx)
return 0;
return _ctx->read(reinterpret_cast<char*>(buffer), len);
}
int WiFiUDP::peek()
{
if (!_ctx)
return 0;
return _ctx->peek();
}
void WiFiUDP::flush()
{
if (_ctx)
_ctx->flush();
}
IPAddress WiFiUDP::remoteIP()
{
uint8_t _remoteIp[4] = {0};
uint8_t _remotePort[2] = {0};
// WiFiDrv::getRemoteData(_sock, _remoteIp, _remotePort);
IPAddress ip(_remoteIp);
return ip;
}
uint16_t WiFiUDP::remotePort()
{
uint8_t _remoteIp[4] = {0};
uint8_t _remotePort[2] = {0};
// WiFiDrv::getRemoteData(_sock, _remoteIp, _remotePort);
uint16_t port = (_remotePort[0]<<8)+_remotePort[1];
return port;
}

View File

@ -1,181 +0,0 @@
/*
WiFiUdp.cpp - Library for Arduino Wifi shield.
Copyright (c) 2011-2014 Arduino. All right reserved.
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
*/
extern "C" {
#include "utility/debug.h"
#include "utility/wifi_spi.h"
}
#include <string.h>
#include "utility/server_drv.h"
#include "utility/wifi_drv.h"
#include "WiFi.h"
#include "WiFiUdp.h"
#include "WiFiClient.h"
#include "WiFiServer.h"
/* Constructor */
WiFiUDP::WiFiUDP() : _sock(NO_SOCKET_AVAIL) {}
/* Start WiFiUDP socket, listening at local port PORT */
uint8_t WiFiUDP::begin(uint16_t port) {
uint8_t sock = WiFiClass::getSocket();
if (sock != NO_SOCKET_AVAIL)
{
ServerDrv::startServer(port, sock, UDP_MODE);
WiFiClass::_server_port[sock] = port;
_sock = sock;
_port = port;
return 1;
}
return 0;
}
/* return number of bytes available in the current packet,
will return zero if parsePacket hasn't been called yet */
int WiFiUDP::available() {
if (_sock != NO_SOCKET_AVAIL)
{
return ServerDrv::availData(_sock);
}
return 0;
}
/* Release any resources being used by this WiFiUDP instance */
void WiFiUDP::stop()
{
if (_sock == NO_SOCKET_AVAIL)
return;
ServerDrv::stopClient(_sock);
_sock = NO_SOCKET_AVAIL;
}
int WiFiUDP::beginPacket(const char *host, uint16_t port)
{
// Look up the host first
int ret = 0;
IPAddress remote_addr;
if (WiFi.hostByName(host, remote_addr))
{
return beginPacket(remote_addr, port);
}
return ret;
}
int WiFiUDP::beginPacket(IPAddress ip, uint16_t port)
{
if (_sock == NO_SOCKET_AVAIL)
_sock = WiFiClass::getSocket();
if (_sock != NO_SOCKET_AVAIL)
{
ServerDrv::startClient(uint32_t(ip), port, _sock, UDP_MODE);
WiFiClass::_state[_sock] = _sock;
return 1;
}
return 0;
}
int WiFiUDP::endPacket()
{
return ServerDrv::sendUdpData(_sock);
}
size_t WiFiUDP::write(uint8_t byte)
{
return write(&byte, 1);
}
size_t WiFiUDP::write(const uint8_t *buffer, size_t size)
{
ServerDrv::insertDataBuf(_sock, buffer, size);
return size;
}
int WiFiUDP::parsePacket()
{
return available();
}
int WiFiUDP::read()
{
uint8_t b;
if (available())
{
ServerDrv::getData(_sock, &b);
return b;
}else{
return -1;
}
}
int WiFiUDP::read(unsigned char* buffer, size_t len)
{
if (available())
{
uint16_t size = 0;
if (!ServerDrv::getDataBuf(_sock, buffer, &size))
return -1;
// TODO check if the buffer is too smal respect to buffer size
return size;
}else{
return -1;
}
}
int WiFiUDP::peek()
{
uint8_t b;
if (!available())
return -1;
ServerDrv::getData(_sock, &b, 1);
return b;
}
void WiFiUDP::flush()
{
while (available())
read();
}
IPAddress WiFiUDP::remoteIP()
{
uint8_t _remoteIp[4] = {0};
uint8_t _remotePort[2] = {0};
WiFiDrv::getRemoteData(_sock, _remoteIp, _remotePort);
IPAddress ip(_remoteIp);
return ip;
}
uint16_t WiFiUDP::remotePort()
{
uint8_t _remoteIp[4] = {0};
uint8_t _remotePort[2] = {0};
WiFiDrv::getRemoteData(_sock, _remoteIp, _remotePort);
uint16_t port = (_remotePort[0]<<8)+_remotePort[1];
return port;
}

View File

@ -17,21 +17,26 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef wifiudp_h #ifndef WIFIUDP_H
#define wifiudp_h #define WIFIUDP_H
#include <Udp.h> #include <Udp.h>
#define UDP_TX_PACKET_MAX_SIZE 24 #define UDP_TX_PACKET_MAX_SIZE 8192
class UdpContext;
class WiFiUDP : public UDP { class WiFiUDP : public UDP {
private: private:
uint8_t _sock; // socket ID for Wiz5100 UdpContext* _ctx;
uint16_t _port; // local port to listen on
public: public:
WiFiUDP(); // Constructor WiFiUDP(); // 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 WiFiUDP(const WiFiUDP& other);
WiFiUDP& operator=(const WiFiUDP& rhs);
~WiFiUDP();
virtual uint8_t begin(uint16_t port); // 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 virtual void stop(); // Finish with the UDP socket
// Sending UDP packets // Sending UDP packets
@ -74,7 +79,7 @@ public:
// Return the port of the host who sent the current incoming packet // Return the port of the host who sent the current incoming packet
virtual uint16_t remotePort(); virtual uint16_t remotePort();
friend class WiFiDrv;
}; };
#endif #endif //WIFIUDP_H

View File

@ -0,0 +1,304 @@
/*
ClientContext.h - TCP connection handling on top of lwIP
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
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
*/
#ifndef UDPCONTEXT_H
#define UDPCONTEXT_H
class UdpContext;
extern "C" void esp_yield();
extern "C" void esp_schedule();
class UdpContext
{
public:
UdpContext()
: _pcb(0)
, _rx_buf(0)
, _first_buf_taken(false)
, _rx_buf_offset(0)
, _refcnt(0)
, _tx_buf_head(0)
, _tx_buf_cur(0)
, _tx_buf_offset(0)
{
_pcb = udp_new();
}
~UdpContext()
{
udp_remove(_pcb);
_pcb = 0;
if (_tx_buf_head)
{
pbuf_free(_tx_buf_head);
_tx_buf_head = 0;
_tx_buf_cur = 0;
_tx_buf_offset = 0;
}
if (_rx_buf)
{
pbuf_free(_rx_buf);
_rx_buf = 0;
_rx_buf_offset = 0;
}
}
void ref()
{
++_refcnt;
}
void unref()
{
DEBUGV(":ur %d\r\n", _refcnt);
if (--_refcnt == 0)
{
delete this;
}
}
bool connect(ip_addr_t addr, uint16_t port)
{
err_t err = udp_connect(_pcb, &addr, port);
return err == ERR_OK;
}
bool listen(ip_addr_t addr, uint16_t port)
{
udp_recv(_pcb, &_s_recv, (void *) this);
err_t err = udp_bind(_pcb, &addr, port);
return err == ERR_OK;
}
void disconnect()
{
udp_disconnect(_pcb);
}
size_t getSize() const
{
if (!_rx_buf)
return 0;
return _rx_buf->len - _rx_buf_offset;
}
bool next()
{
if (!_rx_buf)
return false;
if (!_first_buf_taken)
{
_first_buf_taken = true;
return true;
}
auto head = _rx_buf;
_rx_buf = _rx_buf->next;
_rx_buf_offset = 0;
if (_rx_buf)
{
pbuf_ref(_rx_buf);
}
pbuf_free(head);
return _rx_buf != 0;
}
char read()
{
if (!_rx_buf || _rx_buf->len == _rx_buf_offset)
return 0;
char c = reinterpret_cast<char*>(_rx_buf->payload)[_rx_buf_offset];
_consume(1);
return c;
}
size_t read(char* dst, size_t size)
{
if (!_rx_buf)
return 0;
size_t max_size = _rx_buf->len - _rx_buf_offset;
size = (size < max_size) ? size : max_size;
DEBUGV(":rd %d, %d, %d\r\n", size, _rx_buf->len, _rx_buf_offset);
os_memcpy(dst, reinterpret_cast<char*>(_rx_buf->payload) + _rx_buf_offset, size);
_consume(size);
return size;
}
char peek()
{
if (!_rx_buf)
return 0;
return reinterpret_cast<char*>(_rx_buf->payload)[_rx_buf_offset];
}
void flush()
{
if (!_rx_buf)
return;
_consume(_rx_buf->len - _rx_buf_offset);
}
size_t append(const char* data, size_t size)
{
if (!_tx_buf_head || _tx_buf_head->tot_len < _tx_buf_offset + size)
{
_reserve(_tx_buf_offset + size);
}
size_t left_to_copy = size;
while(left_to_copy)
{
// size already used in current pbuf
size_t used_cur = _tx_buf_offset - (_tx_buf_head->tot_len - _tx_buf_cur->tot_len);
size_t free_cur = _tx_buf_cur->len - used_cur;
if (free_cur == 0)
{
_tx_buf_cur = _tx_buf_cur->next;
continue;
}
size_t will_copy = (left_to_copy < free_cur) ? left_to_copy : free_cur;
os_memcpy(reinterpret_cast<char*>(_tx_buf_cur->payload) + used_cur, data, will_copy);
_tx_buf_offset += will_copy;
left_to_copy -= will_copy;
data += will_copy;
}
return size;
}
void send()
{
size_t orig_size = _tx_buf_head->tot_len;
size_t data_size = _tx_buf_offset;
size_t size_adjustment = orig_size - data_size;
for (pbuf* p = _tx_buf_head; p; p = p->next)
{
p->tot_len -= size_adjustment;
if (!p->next)
{
p->len = p->tot_len;
}
}
udp_send(_pcb, _tx_buf_head);
for (pbuf* p = _tx_buf_head; p; p = p->next)
{
p->tot_len += size_adjustment;
if (!p->next)
{
p->len = p->tot_len;
}
}
pbuf_free(_tx_buf_head);
_tx_buf_head = 0;
_tx_buf_cur = 0;
_tx_buf_offset = 0;
}
private:
void _reserve(size_t size)
{
const size_t pbuf_unit_size = 1024;
if (!_tx_buf_head)
{
_tx_buf_head = pbuf_alloc(PBUF_TRANSPORT, pbuf_unit_size, PBUF_RAM);
_tx_buf_cur = _tx_buf_head;
_tx_buf_offset = 0;
}
size_t cur_size = _tx_buf_head->tot_len;
if (size < cur_size)
return;
size_t grow_size = size - cur_size;
while(grow_size)
{
pbuf* pb = pbuf_alloc(PBUF_TRANSPORT, pbuf_unit_size, PBUF_RAM);
pbuf_cat(_tx_buf_head, pb);
if (grow_size < pbuf_unit_size)
return;
grow_size -= pbuf_unit_size;
}
}
void _consume(size_t size)
{
_rx_buf_offset += size;
}
void _recv(udp_pcb *upcb, pbuf *pb,
ip_addr_t *addr, u16_t port)
{
if (_rx_buf)
{
// there is some unread data
// chain the new pbuf to the existing one
DEBUGV(":rch %d, %d\r\n", _rx_buf->tot_len, pb->tot_len);
pbuf_cat(_rx_buf, pb);
}
else
{
DEBUGV(":rn %d\r\n", pb->tot_len);
_first_buf_taken = false;
_rx_buf = pb;
_rx_buf_offset = 0;
}
}
static void _s_recv(void *arg,
udp_pcb *upcb, pbuf *p,
ip_addr_t *addr, u16_t port)
{
reinterpret_cast<UdpContext*>(arg)->_recv(upcb, p, addr, port);
}
private:
int _refcnt;
udp_pcb* _pcb;
bool _first_buf_taken;
pbuf* _rx_buf;
size_t _rx_buf_offset;
pbuf* _tx_buf_head;
pbuf* _tx_buf_cur;
size_t _tx_buf_offset;
};
#endif//CLIENTCONTEXT_H