Mbed library for ENC28J60 Ethernet modules. Full support for TCP/IP and UDP Server, Client and HTTP server (webserver). DHCP and DNS is included.
Dependents: mBuino_ENC28_MQTT Nucleo_Web_ENC28J60 Nucleo_Web_ENC28J60_ADC Serial_over_Ethernet ... more
Library for ENC28J60 Ethernet modules.
Ported to mbed from Norbert Truchsess's UIPEthernet library for Arduino. Thank you Norbert!
- Full support for persistent (streaming) TCP/IP and UDP connections Client and Server each, ARP, ICMP, DHCP and DNS.
- Works with both Mbed OS 2 and Mbed OS 5.
Usage:
- Import the library into your project.
- Add
#include "UipEthernet.h"
tomain.cpp
- Create one instance of the UipEthernet class initialized with the MAC address you'd like to use and SPI pins of the connected Mbed board.
Example programs:
Import programWebSwitch_ENC28J60
HTTP Server serving a simple webpage which enables to remotely turn a digital output on/off. Compile, download, run and type 'IP_address/secret/' (don't forget the last '/') into your web browser and hit ENTER.
Import programHTTPServer_Echo_ENC28J60
A simple HTTP server echoing received requests. Ethernet connection is over an ENC28J60 board. Usage: Type the server's IP address into you web browser and hit <ENTER>.
Import programTcpServer_ENC28J60
Simple TCP/IP Server using the UIPEthernet library for ENC28J60 Ethernet boards.
Import programTcpClient_ENC28J60
Simple TCP/IP Client using the UIPEthernet library for ENC28J60 Ethernet boards.
Import programUdpServer_ENC28J60
Simple UDP Server using the UIPEthernet library for ENC28J60 Ethernet boards.
Import programUdpClient_ENC28J60
Simple UDP Client using the UIPEthernet library for ENC28J60 Ethernet boards.
Import programMQTT_Hello_ENC28J60
MQTT Client example program. Ethernet connection is via an ENC28J60 module.
DhcpClient.cpp@15:53715cc81c63, 2019-09-07 (annotated)
- Committer:
- hudakz
- Date:
- Sat Sep 07 17:42:42 2019 +0000
- Revision:
- 15:53715cc81c63
- Parent:
- 9:a156d3de5647
- Child:
- 17:1123c3fe86ca
Timeout parameter added for the 'connect' function, SPI speed reduced from 20 to 10 Mb/s, debug messages fixed ...
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
hudakz | 9:a156d3de5647 | 1 | // DHCP Library v0.3 - April 25, 2009 |
hudakz | 9:a156d3de5647 | 2 | // Author: Jordan Terrell - blog.jordanterrell.com |
hudakz | 9:a156d3de5647 | 3 | #include <string.h> |
hudakz | 9:a156d3de5647 | 4 | #include <stdlib.h> |
hudakz | 15:53715cc81c63 | 5 | #include "UipEthernet.h" |
hudakz | 9:a156d3de5647 | 6 | #include "DhcpClient.h" |
hudakz | 9:a156d3de5647 | 7 | #include "utility/util.h" |
hudakz | 9:a156d3de5647 | 8 | |
hudakz | 9:a156d3de5647 | 9 | /** |
hudakz | 9:a156d3de5647 | 10 | * @brief |
hudakz | 9:a156d3de5647 | 11 | * @note |
hudakz | 9:a156d3de5647 | 12 | * @param |
hudakz | 9:a156d3de5647 | 13 | * @retval |
hudakz | 9:a156d3de5647 | 14 | */ |
hudakz | 9:a156d3de5647 | 15 | int DhcpClient::begin(uint8_t* mac, unsigned long timeout, unsigned long responseTimeout) |
hudakz | 9:a156d3de5647 | 16 | { |
hudakz | 9:a156d3de5647 | 17 | _dhcpLeaseTime = 0; |
hudakz | 9:a156d3de5647 | 18 | _dhcpT1 = 0; |
hudakz | 9:a156d3de5647 | 19 | _dhcpT2 = 0; |
hudakz | 9:a156d3de5647 | 20 | _lastCheck = 0; |
hudakz | 9:a156d3de5647 | 21 | _timeout = timeout; |
hudakz | 9:a156d3de5647 | 22 | _responseTimeout = responseTimeout; |
hudakz | 9:a156d3de5647 | 23 | |
hudakz | 9:a156d3de5647 | 24 | // zero out _dhcpMacAddr |
hudakz | 9:a156d3de5647 | 25 | memset(_dhcpMacAddr, 0, 6); |
hudakz | 9:a156d3de5647 | 26 | resetDhcpLease(); |
hudakz | 9:a156d3de5647 | 27 | |
hudakz | 9:a156d3de5647 | 28 | memcpy((void*)_dhcpMacAddr, (void*)mac, 6); |
hudakz | 9:a156d3de5647 | 29 | _dhcp_state = STATE_DHCP_START; |
hudakz | 9:a156d3de5647 | 30 | return requestDhcpLease(); |
hudakz | 9:a156d3de5647 | 31 | } |
hudakz | 9:a156d3de5647 | 32 | |
hudakz | 9:a156d3de5647 | 33 | /** |
hudakz | 9:a156d3de5647 | 34 | * @brief |
hudakz | 9:a156d3de5647 | 35 | * @note |
hudakz | 9:a156d3de5647 | 36 | * @param |
hudakz | 9:a156d3de5647 | 37 | * @retval |
hudakz | 9:a156d3de5647 | 38 | */ |
hudakz | 9:a156d3de5647 | 39 | void DhcpClient::resetDhcpLease() |
hudakz | 9:a156d3de5647 | 40 | { |
hudakz | 9:a156d3de5647 | 41 | // zero out _dhcpSubnetMask, _dhcpGatewayIp, _dhcpLocalIp, _dhcpDhcpServerIp, _dhcpDnsServerIp |
hudakz | 9:a156d3de5647 | 42 | memset(_dhcpLocalIp, 0, 5 * 4); |
hudakz | 9:a156d3de5647 | 43 | } |
hudakz | 9:a156d3de5647 | 44 | |
hudakz | 9:a156d3de5647 | 45 | //return:0 on error, 1 if request is sent and response is received |
hudakz | 9:a156d3de5647 | 46 | int DhcpClient::requestDhcpLease() |
hudakz | 9:a156d3de5647 | 47 | { |
hudakz | 15:53715cc81c63 | 48 | #ifdef UIPETHERNET_DEBUG_UDP |
hudakz | 15:53715cc81c63 | 49 | printf("requestDhcpLease(): begin\r\n"); |
hudakz | 15:53715cc81c63 | 50 | #endif |
hudakz | 9:a156d3de5647 | 51 | uint8_t messageType = 0; |
hudakz | 15:53715cc81c63 | 52 | Timer timer; |
hudakz | 15:53715cc81c63 | 53 | |
hudakz | 15:53715cc81c63 | 54 | timer.reset(); |
hudakz | 15:53715cc81c63 | 55 | timer.start(); |
hudakz | 9:a156d3de5647 | 56 | |
hudakz | 9:a156d3de5647 | 57 | // Pick an initial transaction ID |
hudakz | 15:53715cc81c63 | 58 | srand(time(NULL)+ 1); |
hudakz | 9:a156d3de5647 | 59 | _dhcpTransactionId = (rand() % 2000UL) + 1; |
hudakz | 9:a156d3de5647 | 60 | _dhcpInitialTransactionId = _dhcpTransactionId; |
hudakz | 9:a156d3de5647 | 61 | |
hudakz | 9:a156d3de5647 | 62 | _dhcpUdpSocket.stop(); |
hudakz | 9:a156d3de5647 | 63 | if (_dhcpUdpSocket.begin(DHCP_CLIENT_PORT) == 0) { |
hudakz | 9:a156d3de5647 | 64 | // Couldn't get a socket |
hudakz | 15:53715cc81c63 | 65 | #ifdef UIPETHERNET_DEBUG_UDP |
hudakz | 15:53715cc81c63 | 66 | printf("requestDhcpLease(): Couldn't get a socket\r\n"); |
hudakz | 15:53715cc81c63 | 67 | #endif |
hudakz | 9:a156d3de5647 | 68 | return 0; |
hudakz | 9:a156d3de5647 | 69 | } |
hudakz | 9:a156d3de5647 | 70 | |
hudakz | 9:a156d3de5647 | 71 | presendDhcp(); |
hudakz | 9:a156d3de5647 | 72 | |
hudakz | 9:a156d3de5647 | 73 | volatile int result = 0; |
hudakz | 9:a156d3de5647 | 74 | |
hudakz | 9:a156d3de5647 | 75 | while (_dhcp_state != STATE_DHCP_LEASED) { |
hudakz | 9:a156d3de5647 | 76 | if (_dhcp_state == STATE_DHCP_START) { |
hudakz | 15:53715cc81c63 | 77 | #ifdef UIPETHERNET_DEBUG_UDP |
hudakz | 15:53715cc81c63 | 78 | printf("_dhcp_state: STATE_DHCP_START\r\n"); |
hudakz | 15:53715cc81c63 | 79 | #endif |
hudakz | 9:a156d3de5647 | 80 | _dhcpTransactionId++; |
hudakz | 9:a156d3de5647 | 81 | |
hudakz | 15:53715cc81c63 | 82 | sendDhcpMessage(DHCP_DISCOVER, (timer.read_ms() / 1000 + 1)); |
hudakz | 9:a156d3de5647 | 83 | _dhcp_state = STATE_DHCP_DISCOVER; |
hudakz | 9:a156d3de5647 | 84 | } |
hudakz | 9:a156d3de5647 | 85 | else |
hudakz | 9:a156d3de5647 | 86 | if (_dhcp_state == STATE_DHCP_REREQUEST) { |
hudakz | 15:53715cc81c63 | 87 | #ifdef UIPETHERNET_DEBUG_UDP |
hudakz | 15:53715cc81c63 | 88 | printf("_dhcp_state: STATE_DHCP_REREQUEST\r\n"); |
hudakz | 15:53715cc81c63 | 89 | #endif |
hudakz | 9:a156d3de5647 | 90 | _dhcpTransactionId++; |
hudakz | 15:53715cc81c63 | 91 | sendDhcpMessage(DHCP_REQUEST, (timer.read_ms() / 1000 + 1)); |
hudakz | 9:a156d3de5647 | 92 | _dhcp_state = STATE_DHCP_REQUEST; |
hudakz | 9:a156d3de5647 | 93 | } |
hudakz | 9:a156d3de5647 | 94 | else |
hudakz | 9:a156d3de5647 | 95 | if (_dhcp_state == STATE_DHCP_DISCOVER) { |
hudakz | 15:53715cc81c63 | 96 | #ifdef UIPETHERNET_DEBUG_UDP |
hudakz | 15:53715cc81c63 | 97 | printf("_dhcp_state: STATE_DHCP_DISCOVER\r\n"); |
hudakz | 15:53715cc81c63 | 98 | #endif |
hudakz | 9:a156d3de5647 | 99 | uint32_t respId; |
hudakz | 9:a156d3de5647 | 100 | messageType = parseDhcpResponse(_responseTimeout, respId); |
hudakz | 9:a156d3de5647 | 101 | if (messageType == DHCP_OFFER) { |
hudakz | 9:a156d3de5647 | 102 | // We'll use the transaction ID that the offer came with, |
hudakz | 9:a156d3de5647 | 103 | // rather than the one we were up to |
hudakz | 9:a156d3de5647 | 104 | _dhcpTransactionId = respId; |
hudakz | 15:53715cc81c63 | 105 | sendDhcpMessage(DHCP_REQUEST, (timer.read_ms() / 1000 + 1)); |
hudakz | 9:a156d3de5647 | 106 | _dhcp_state = STATE_DHCP_REQUEST; |
hudakz | 9:a156d3de5647 | 107 | } |
hudakz | 9:a156d3de5647 | 108 | } |
hudakz | 9:a156d3de5647 | 109 | else |
hudakz | 9:a156d3de5647 | 110 | if (_dhcp_state == STATE_DHCP_REQUEST) { |
hudakz | 15:53715cc81c63 | 111 | #ifdef UIPETHERNET_DEBUG_UDP |
hudakz | 15:53715cc81c63 | 112 | printf("_dhcp_state: STATE_DHCP_REQUEST\r\n"); |
hudakz | 15:53715cc81c63 | 113 | #endif |
hudakz | 9:a156d3de5647 | 114 | uint32_t respId; |
hudakz | 9:a156d3de5647 | 115 | messageType = parseDhcpResponse(_responseTimeout, respId); |
hudakz | 9:a156d3de5647 | 116 | if (messageType == DHCP_ACK) { |
hudakz | 15:53715cc81c63 | 117 | #ifdef UIPETHERNET_DEBUG_UDP |
hudakz | 15:53715cc81c63 | 118 | printf("messageType: DHCP_ACK\r\n"); |
hudakz | 15:53715cc81c63 | 119 | #endif |
hudakz | 9:a156d3de5647 | 120 | _dhcp_state = STATE_DHCP_LEASED; |
hudakz | 9:a156d3de5647 | 121 | result = 1; |
hudakz | 9:a156d3de5647 | 122 | |
hudakz | 9:a156d3de5647 | 123 | //use default lease time if we didn't get it |
hudakz | 9:a156d3de5647 | 124 | if (_dhcpLeaseTime == 0) { |
hudakz | 9:a156d3de5647 | 125 | _dhcpLeaseTime = DEFAULT_LEASE; |
hudakz | 9:a156d3de5647 | 126 | } |
hudakz | 9:a156d3de5647 | 127 | |
hudakz | 9:a156d3de5647 | 128 | //calculate T1 & T2 if we didn't get it |
hudakz | 9:a156d3de5647 | 129 | if (_dhcpT1 == 0) { |
hudakz | 9:a156d3de5647 | 130 | //T1 should be 50% of _dhcpLeaseTime |
hudakz | 9:a156d3de5647 | 131 | _dhcpT1 = _dhcpLeaseTime >> 1; |
hudakz | 9:a156d3de5647 | 132 | } |
hudakz | 9:a156d3de5647 | 133 | |
hudakz | 9:a156d3de5647 | 134 | if (_dhcpT2 == 0) { |
hudakz | 9:a156d3de5647 | 135 | //T2 should be 87.5% (7/8ths) of _dhcpLeaseTime |
hudakz | 9:a156d3de5647 | 136 | _dhcpT2 = _dhcpT1 << 1; |
hudakz | 9:a156d3de5647 | 137 | } |
hudakz | 9:a156d3de5647 | 138 | |
hudakz | 9:a156d3de5647 | 139 | _renewInSec = _dhcpT1; |
hudakz | 9:a156d3de5647 | 140 | _rebindInSec = _dhcpT2; |
hudakz | 9:a156d3de5647 | 141 | } |
hudakz | 9:a156d3de5647 | 142 | else |
hudakz | 15:53715cc81c63 | 143 | if (messageType == DHCP_NAK) { |
hudakz | 15:53715cc81c63 | 144 | #ifdef UIPETHERNET_DEBUG_UDP |
hudakz | 15:53715cc81c63 | 145 | printf("messageType: DHCP_NAK\r\n"); |
hudakz | 15:53715cc81c63 | 146 | #endif |
hudakz | 9:a156d3de5647 | 147 | _dhcp_state = STATE_DHCP_START; |
hudakz | 15:53715cc81c63 | 148 | } |
hudakz | 9:a156d3de5647 | 149 | } |
hudakz | 9:a156d3de5647 | 150 | |
hudakz | 9:a156d3de5647 | 151 | if (messageType == 255) { |
hudakz | 15:53715cc81c63 | 152 | #ifdef UIPETHERNET_DEBUG_UDP |
hudakz | 15:53715cc81c63 | 153 | printf("DHCP_NAK: 255\r\n"); |
hudakz | 15:53715cc81c63 | 154 | #endif |
hudakz | 9:a156d3de5647 | 155 | messageType = 0; |
hudakz | 9:a156d3de5647 | 156 | _dhcp_state = STATE_DHCP_START; |
hudakz | 9:a156d3de5647 | 157 | } |
hudakz | 9:a156d3de5647 | 158 | |
hudakz | 15:53715cc81c63 | 159 | if ((result != 1) && ((timer.read_ms() / 1000) > _timeout)) { |
hudakz | 15:53715cc81c63 | 160 | #ifdef UIPETHERNET_DEBUG_UDP |
hudakz | 15:53715cc81c63 | 161 | printf("requestDhcpLease(): timeout\r\n"); |
hudakz | 15:53715cc81c63 | 162 | #endif |
hudakz | 9:a156d3de5647 | 163 | break; |
hudakz | 15:53715cc81c63 | 164 | } |
hudakz | 9:a156d3de5647 | 165 | } |
hudakz | 9:a156d3de5647 | 166 | |
hudakz | 9:a156d3de5647 | 167 | // We're done with the socket now |
hudakz | 9:a156d3de5647 | 168 | _dhcpUdpSocket.stop(); |
hudakz | 9:a156d3de5647 | 169 | _dhcpTransactionId++; |
hudakz | 9:a156d3de5647 | 170 | |
hudakz | 15:53715cc81c63 | 171 | #ifdef UIPETHERNET_DEBUG_UDP |
hudakz | 15:53715cc81c63 | 172 | printf("requestDhcpLease(): %d\r\n", result); |
hudakz | 15:53715cc81c63 | 173 | #endif |
hudakz | 9:a156d3de5647 | 174 | return result; |
hudakz | 9:a156d3de5647 | 175 | } |
hudakz | 9:a156d3de5647 | 176 | |
hudakz | 9:a156d3de5647 | 177 | /** |
hudakz | 9:a156d3de5647 | 178 | * @brief |
hudakz | 9:a156d3de5647 | 179 | * @note |
hudakz | 9:a156d3de5647 | 180 | * @param |
hudakz | 9:a156d3de5647 | 181 | * @retval |
hudakz | 9:a156d3de5647 | 182 | */ |
hudakz | 9:a156d3de5647 | 183 | void DhcpClient::presendDhcp() |
hudakz | 9:a156d3de5647 | 184 | { } |
hudakz | 9:a156d3de5647 | 185 | |
hudakz | 9:a156d3de5647 | 186 | /** |
hudakz | 9:a156d3de5647 | 187 | * @brief |
hudakz | 9:a156d3de5647 | 188 | * @note |
hudakz | 9:a156d3de5647 | 189 | * @param |
hudakz | 9:a156d3de5647 | 190 | * @retval |
hudakz | 9:a156d3de5647 | 191 | */ |
hudakz | 9:a156d3de5647 | 192 | void DhcpClient::sendDhcpMessage(uint8_t messageType, uint16_t secondsElapsed) |
hudakz | 9:a156d3de5647 | 193 | { |
hudakz | 9:a156d3de5647 | 194 | uint8_t buffer[32]; |
hudakz | 9:a156d3de5647 | 195 | memset(buffer, 0, 32); |
hudakz | 9:a156d3de5647 | 196 | |
hudakz | 9:a156d3de5647 | 197 | IpAddress dest_addr(255, 255, 255, 255); // Broadcast address |
hudakz | 9:a156d3de5647 | 198 | |
hudakz | 9:a156d3de5647 | 199 | if (-1 == _dhcpUdpSocket.beginPacket(dest_addr, DHCP_SERVER_PORT)) { |
hudakz | 9:a156d3de5647 | 200 | // FIXME Need to return errors |
hudakz | 9:a156d3de5647 | 201 | return; |
hudakz | 9:a156d3de5647 | 202 | } |
hudakz | 9:a156d3de5647 | 203 | |
hudakz | 9:a156d3de5647 | 204 | buffer[0] = DHCP_BOOTREQUEST; // op |
hudakz | 9:a156d3de5647 | 205 | buffer[1] = DHCP_HTYPE10MB; // htype |
hudakz | 9:a156d3de5647 | 206 | buffer[2] = DHCP_HLENETHERNET; // hlen |
hudakz | 9:a156d3de5647 | 207 | buffer[3] = DHCP_HOPS; // hops |
hudakz | 9:a156d3de5647 | 208 | // xid |
hudakz | 9:a156d3de5647 | 209 | unsigned long xid = htonl(_dhcpTransactionId); |
hudakz | 9:a156d3de5647 | 210 | memcpy(buffer + 4, &(xid), 4); |
hudakz | 9:a156d3de5647 | 211 | |
hudakz | 9:a156d3de5647 | 212 | // 8, 9 - seconds elapsed |
hudakz | 9:a156d3de5647 | 213 | buffer[8] = ((secondsElapsed & 0xff00) >> 8); |
hudakz | 9:a156d3de5647 | 214 | buffer[9] = (secondsElapsed & 0x00ff); |
hudakz | 9:a156d3de5647 | 215 | |
hudakz | 9:a156d3de5647 | 216 | // flags |
hudakz | 9:a156d3de5647 | 217 | unsigned short flags = htons(DHCP_FLAGSBROADCAST); |
hudakz | 9:a156d3de5647 | 218 | memcpy(buffer + 10, &(flags), 2); |
hudakz | 9:a156d3de5647 | 219 | |
hudakz | 9:a156d3de5647 | 220 | // ciaddr: already zeroed |
hudakz | 9:a156d3de5647 | 221 | // yiaddr: already zeroed |
hudakz | 9:a156d3de5647 | 222 | // siaddr: already zeroed |
hudakz | 9:a156d3de5647 | 223 | // giaddr: already zeroed |
hudakz | 15:53715cc81c63 | 224 | //put data in ENC28J60 transmit buffer |
hudakz | 9:a156d3de5647 | 225 | _dhcpUdpSocket.write(buffer, 28); |
hudakz | 9:a156d3de5647 | 226 | |
hudakz | 9:a156d3de5647 | 227 | memset(buffer, 0, 32); // clear local buffer |
hudakz | 9:a156d3de5647 | 228 | memcpy(buffer, _dhcpMacAddr, 6); // chaddr |
hudakz | 15:53715cc81c63 | 229 | //put data in ENC28J60 transmit buffer |
hudakz | 9:a156d3de5647 | 230 | _dhcpUdpSocket.write(buffer, 16); |
hudakz | 9:a156d3de5647 | 231 | |
hudakz | 9:a156d3de5647 | 232 | memset(buffer, 0, 32); // clear local buffer |
hudakz | 9:a156d3de5647 | 233 | // leave zeroed out for sname && file |
hudakz | 15:53715cc81c63 | 234 | // put in ENC28J60 transmit buffer x 6 (192 bytes) |
hudakz | 9:a156d3de5647 | 235 | for (int i = 0; i < 6; i++) { |
hudakz | 9:a156d3de5647 | 236 | _dhcpUdpSocket.write(buffer, 32); |
hudakz | 9:a156d3de5647 | 237 | } |
hudakz | 9:a156d3de5647 | 238 | |
hudakz | 9:a156d3de5647 | 239 | // OPT - Magic Cookie |
hudakz | 9:a156d3de5647 | 240 | buffer[0] = (uint8_t) ((MAGIC_COOKIE >> 24) & 0xFF); |
hudakz | 9:a156d3de5647 | 241 | buffer[1] = (uint8_t) ((MAGIC_COOKIE >> 16) & 0xFF); |
hudakz | 9:a156d3de5647 | 242 | buffer[2] = (uint8_t) ((MAGIC_COOKIE >> 8) & 0xFF); |
hudakz | 9:a156d3de5647 | 243 | buffer[3] = (uint8_t) (MAGIC_COOKIE & 0xFF); |
hudakz | 9:a156d3de5647 | 244 | |
hudakz | 9:a156d3de5647 | 245 | // OPT - message type |
hudakz | 9:a156d3de5647 | 246 | buffer[4] = dhcpMessageType; |
hudakz | 9:a156d3de5647 | 247 | buffer[5] = 0x01; |
hudakz | 9:a156d3de5647 | 248 | buffer[6] = messageType; //DHCP_REQUEST; |
hudakz | 9:a156d3de5647 | 249 | // OPT - client identifier |
hudakz | 9:a156d3de5647 | 250 | buffer[7] = dhcpClientIdentifier; |
hudakz | 9:a156d3de5647 | 251 | buffer[8] = 0x07; |
hudakz | 9:a156d3de5647 | 252 | buffer[9] = 0x01; |
hudakz | 9:a156d3de5647 | 253 | memcpy(buffer + 10, _dhcpMacAddr, 6); |
hudakz | 9:a156d3de5647 | 254 | |
hudakz | 9:a156d3de5647 | 255 | // OPT - host name |
hudakz | 9:a156d3de5647 | 256 | buffer[16] = hostName; |
hudakz | 9:a156d3de5647 | 257 | buffer[17] = strlen(HOST_NAME) + 6; // length of hostname + last 3 bytes of mac address |
hudakz | 9:a156d3de5647 | 258 | strcpy((char*) &(buffer[18]), HOST_NAME); |
hudakz | 9:a156d3de5647 | 259 | |
hudakz | 9:a156d3de5647 | 260 | printByte((char*) &(buffer[24]), _dhcpMacAddr[3]); |
hudakz | 9:a156d3de5647 | 261 | printByte((char*) &(buffer[26]), _dhcpMacAddr[4]); |
hudakz | 9:a156d3de5647 | 262 | printByte((char*) &(buffer[28]), _dhcpMacAddr[5]); |
hudakz | 9:a156d3de5647 | 263 | |
hudakz | 15:53715cc81c63 | 264 | //put data in ENC28J60 transmit buffer |
hudakz | 9:a156d3de5647 | 265 | _dhcpUdpSocket.write(buffer, 30); |
hudakz | 9:a156d3de5647 | 266 | |
hudakz | 9:a156d3de5647 | 267 | if (messageType == DHCP_REQUEST) { |
hudakz | 9:a156d3de5647 | 268 | buffer[0] = dhcpRequestedIPaddr; |
hudakz | 9:a156d3de5647 | 269 | buffer[1] = 0x04; |
hudakz | 9:a156d3de5647 | 270 | buffer[2] = _dhcpLocalIp[0]; |
hudakz | 9:a156d3de5647 | 271 | buffer[3] = _dhcpLocalIp[1]; |
hudakz | 9:a156d3de5647 | 272 | buffer[4] = _dhcpLocalIp[2]; |
hudakz | 9:a156d3de5647 | 273 | buffer[5] = _dhcpLocalIp[3]; |
hudakz | 9:a156d3de5647 | 274 | |
hudakz | 9:a156d3de5647 | 275 | buffer[6] = dhcpServerIdentifier; |
hudakz | 9:a156d3de5647 | 276 | buffer[7] = 0x04; |
hudakz | 9:a156d3de5647 | 277 | |
hudakz | 9:a156d3de5647 | 278 | //buffer[8] = _dhcpDhcpServerIp[0]; |
hudakz | 9:a156d3de5647 | 279 | buffer[8] = _dhcpLocalIp[0]; |
hudakz | 9:a156d3de5647 | 280 | buffer[9] = _dhcpDhcpServerIp[1]; |
hudakz | 9:a156d3de5647 | 281 | buffer[10] = _dhcpDhcpServerIp[2]; |
hudakz | 9:a156d3de5647 | 282 | buffer[11] = _dhcpDhcpServerIp[3]; |
hudakz | 9:a156d3de5647 | 283 | |
hudakz | 15:53715cc81c63 | 284 | //put data in ENC28J60 transmit buffer |
hudakz | 9:a156d3de5647 | 285 | _dhcpUdpSocket.write(buffer, 12); |
hudakz | 9:a156d3de5647 | 286 | } |
hudakz | 9:a156d3de5647 | 287 | |
hudakz | 9:a156d3de5647 | 288 | buffer[0] = dhcpParamRequest; |
hudakz | 9:a156d3de5647 | 289 | buffer[1] = 0x06; |
hudakz | 9:a156d3de5647 | 290 | buffer[2] = subnetMask; |
hudakz | 9:a156d3de5647 | 291 | buffer[3] = routersOnSubnet; |
hudakz | 9:a156d3de5647 | 292 | buffer[4] = dns; |
hudakz | 9:a156d3de5647 | 293 | buffer[5] = domainName; |
hudakz | 9:a156d3de5647 | 294 | buffer[6] = dhcpT1value; |
hudakz | 9:a156d3de5647 | 295 | buffer[7] = dhcpT2value; |
hudakz | 9:a156d3de5647 | 296 | buffer[8] = endOption; |
hudakz | 9:a156d3de5647 | 297 | |
hudakz | 15:53715cc81c63 | 298 | //put data in ENC28J60 transmit buffer |
hudakz | 9:a156d3de5647 | 299 | _dhcpUdpSocket.write(buffer, 9); |
hudakz | 9:a156d3de5647 | 300 | |
hudakz | 9:a156d3de5647 | 301 | _dhcpUdpSocket.endPacket(); |
hudakz | 9:a156d3de5647 | 302 | } |
hudakz | 9:a156d3de5647 | 303 | |
hudakz | 9:a156d3de5647 | 304 | /** |
hudakz | 9:a156d3de5647 | 305 | * @brief |
hudakz | 9:a156d3de5647 | 306 | * @note |
hudakz | 9:a156d3de5647 | 307 | * @param |
hudakz | 9:a156d3de5647 | 308 | * @retval |
hudakz | 9:a156d3de5647 | 309 | */ |
hudakz | 9:a156d3de5647 | 310 | uint8_t DhcpClient::parseDhcpResponse(unsigned long responseTimeout, uint32_t& transactionId) |
hudakz | 9:a156d3de5647 | 311 | { |
hudakz | 9:a156d3de5647 | 312 | volatile uint8_t type = 0; |
hudakz | 9:a156d3de5647 | 313 | uint8_t opt_len = 0; |
hudakz | 15:53715cc81c63 | 314 | Timer timer; |
hudakz | 9:a156d3de5647 | 315 | |
hudakz | 15:53715cc81c63 | 316 | timer.start(); |
hudakz | 9:a156d3de5647 | 317 | |
hudakz | 9:a156d3de5647 | 318 | while (_dhcpUdpSocket.parsePacket() <= 0) { |
hudakz | 15:53715cc81c63 | 319 | if (timer.read() > responseTimeout) { |
hudakz | 9:a156d3de5647 | 320 | return 255; |
hudakz | 9:a156d3de5647 | 321 | } |
hudakz | 9:a156d3de5647 | 322 | |
hudakz | 9:a156d3de5647 | 323 | wait_ms(50); |
hudakz | 9:a156d3de5647 | 324 | } |
hudakz | 9:a156d3de5647 | 325 | |
hudakz | 9:a156d3de5647 | 326 | // start reading in the packet |
hudakz | 9:a156d3de5647 | 327 | RIP_MSG_FIXED fixedMsg; |
hudakz | 9:a156d3de5647 | 328 | _dhcpUdpSocket.read((uint8_t*) &fixedMsg, sizeof(RIP_MSG_FIXED)); |
hudakz | 9:a156d3de5647 | 329 | |
hudakz | 9:a156d3de5647 | 330 | if (fixedMsg.op == DHCP_BOOTREPLY && _dhcpUdpSocket.remotePort() == DHCP_SERVER_PORT) { |
hudakz | 9:a156d3de5647 | 331 | transactionId = ntohl(fixedMsg.xid); |
hudakz | 9:a156d3de5647 | 332 | if |
hudakz | 9:a156d3de5647 | 333 | ( |
hudakz | 9:a156d3de5647 | 334 | memcmp(fixedMsg.chaddr, _dhcpMacAddr, 6) != 0 || |
hudakz | 9:a156d3de5647 | 335 | (transactionId < _dhcpInitialTransactionId) || |
hudakz | 9:a156d3de5647 | 336 | (transactionId > _dhcpTransactionId) |
hudakz | 9:a156d3de5647 | 337 | ) { |
hudakz | 9:a156d3de5647 | 338 | // Need to read the rest of the packet here regardless |
hudakz | 9:a156d3de5647 | 339 | _dhcpUdpSocket.flush(); |
hudakz | 9:a156d3de5647 | 340 | return 0; |
hudakz | 9:a156d3de5647 | 341 | } |
hudakz | 9:a156d3de5647 | 342 | |
hudakz | 9:a156d3de5647 | 343 | memcpy(_dhcpLocalIp, fixedMsg.yiaddr, 4); |
hudakz | 9:a156d3de5647 | 344 | |
hudakz | 9:a156d3de5647 | 345 | // Skip to the option part |
hudakz | 9:a156d3de5647 | 346 | // Doing this a byte at a time so we don't have to put a big buffer |
hudakz | 9:a156d3de5647 | 347 | // on the stack (as we don't have lots of memory lying around) |
hudakz | 9:a156d3de5647 | 348 | for (int i = 0; i < (240 - (int)sizeof(RIP_MSG_FIXED)); i++) { |
hudakz | 9:a156d3de5647 | 349 | _dhcpUdpSocket.read(); // we don't care about the returned byte |
hudakz | 9:a156d3de5647 | 350 | } |
hudakz | 9:a156d3de5647 | 351 | |
hudakz | 9:a156d3de5647 | 352 | while (_dhcpUdpSocket.available() > 0) { |
hudakz | 9:a156d3de5647 | 353 | switch (_dhcpUdpSocket.read()) { |
hudakz | 9:a156d3de5647 | 354 | case endOption: |
hudakz | 9:a156d3de5647 | 355 | break; |
hudakz | 9:a156d3de5647 | 356 | |
hudakz | 9:a156d3de5647 | 357 | case padOption: |
hudakz | 9:a156d3de5647 | 358 | break; |
hudakz | 9:a156d3de5647 | 359 | |
hudakz | 9:a156d3de5647 | 360 | case dhcpMessageType: |
hudakz | 9:a156d3de5647 | 361 | opt_len = _dhcpUdpSocket.read(); |
hudakz | 9:a156d3de5647 | 362 | type = _dhcpUdpSocket.read(); |
hudakz | 9:a156d3de5647 | 363 | break; |
hudakz | 9:a156d3de5647 | 364 | |
hudakz | 9:a156d3de5647 | 365 | case subnetMask: |
hudakz | 9:a156d3de5647 | 366 | opt_len = _dhcpUdpSocket.read(); |
hudakz | 9:a156d3de5647 | 367 | _dhcpUdpSocket.read(_dhcpSubnetMask, 4); |
hudakz | 9:a156d3de5647 | 368 | break; |
hudakz | 9:a156d3de5647 | 369 | |
hudakz | 9:a156d3de5647 | 370 | case routersOnSubnet: |
hudakz | 9:a156d3de5647 | 371 | opt_len = _dhcpUdpSocket.read(); |
hudakz | 9:a156d3de5647 | 372 | _dhcpUdpSocket.read(_dhcpGatewayIp, 4); |
hudakz | 9:a156d3de5647 | 373 | for (int i = 0; i < opt_len - 4; i++) { |
hudakz | 9:a156d3de5647 | 374 | _dhcpUdpSocket.read(); |
hudakz | 9:a156d3de5647 | 375 | } |
hudakz | 9:a156d3de5647 | 376 | break; |
hudakz | 9:a156d3de5647 | 377 | |
hudakz | 9:a156d3de5647 | 378 | case dns: |
hudakz | 9:a156d3de5647 | 379 | opt_len = _dhcpUdpSocket.read(); |
hudakz | 9:a156d3de5647 | 380 | _dhcpUdpSocket.read(_dhcpDnsServerIp, 4); |
hudakz | 9:a156d3de5647 | 381 | for (int i = 0; i < opt_len - 4; i++) { |
hudakz | 9:a156d3de5647 | 382 | _dhcpUdpSocket.read(); |
hudakz | 9:a156d3de5647 | 383 | } |
hudakz | 9:a156d3de5647 | 384 | break; |
hudakz | 9:a156d3de5647 | 385 | |
hudakz | 9:a156d3de5647 | 386 | case dhcpServerIdentifier: |
hudakz | 9:a156d3de5647 | 387 | opt_len = _dhcpUdpSocket.read(); |
hudakz | 9:a156d3de5647 | 388 | if (*((uint32_t*)_dhcpDhcpServerIp) == 0 || IpAddress(_dhcpDhcpServerIp) == _dhcpUdpSocket.remoteIP()) { |
hudakz | 9:a156d3de5647 | 389 | _dhcpUdpSocket.read(_dhcpDhcpServerIp, sizeof(_dhcpDhcpServerIp)); |
hudakz | 9:a156d3de5647 | 390 | } |
hudakz | 9:a156d3de5647 | 391 | else { |
hudakz | 9:a156d3de5647 | 392 | // Skip over the rest of this option |
hudakz | 9:a156d3de5647 | 393 | while (opt_len--) { |
hudakz | 9:a156d3de5647 | 394 | _dhcpUdpSocket.read(); |
hudakz | 9:a156d3de5647 | 395 | } |
hudakz | 9:a156d3de5647 | 396 | } |
hudakz | 9:a156d3de5647 | 397 | break; |
hudakz | 9:a156d3de5647 | 398 | |
hudakz | 9:a156d3de5647 | 399 | case dhcpT1value: |
hudakz | 9:a156d3de5647 | 400 | opt_len = _dhcpUdpSocket.read(); |
hudakz | 9:a156d3de5647 | 401 | _dhcpUdpSocket.read((uint8_t*) &_dhcpT1, sizeof(_dhcpT1)); |
hudakz | 9:a156d3de5647 | 402 | _dhcpT1 = ntohl(_dhcpT1); |
hudakz | 9:a156d3de5647 | 403 | break; |
hudakz | 9:a156d3de5647 | 404 | |
hudakz | 9:a156d3de5647 | 405 | case dhcpT2value: |
hudakz | 9:a156d3de5647 | 406 | opt_len = _dhcpUdpSocket.read(); |
hudakz | 9:a156d3de5647 | 407 | _dhcpUdpSocket.read((uint8_t*) &_dhcpT2, sizeof(_dhcpT2)); |
hudakz | 9:a156d3de5647 | 408 | _dhcpT2 = ntohl(_dhcpT2); |
hudakz | 9:a156d3de5647 | 409 | break; |
hudakz | 9:a156d3de5647 | 410 | |
hudakz | 9:a156d3de5647 | 411 | case dhcpIPaddrLeaseTime: |
hudakz | 9:a156d3de5647 | 412 | opt_len = _dhcpUdpSocket.read(); |
hudakz | 9:a156d3de5647 | 413 | _dhcpUdpSocket.read((uint8_t*) &_dhcpLeaseTime, sizeof(_dhcpLeaseTime)); |
hudakz | 9:a156d3de5647 | 414 | _dhcpLeaseTime = ntohl(_dhcpLeaseTime); |
hudakz | 9:a156d3de5647 | 415 | _renewInSec = _dhcpLeaseTime; |
hudakz | 9:a156d3de5647 | 416 | break; |
hudakz | 9:a156d3de5647 | 417 | |
hudakz | 9:a156d3de5647 | 418 | default: |
hudakz | 9:a156d3de5647 | 419 | opt_len = _dhcpUdpSocket.read(); |
hudakz | 9:a156d3de5647 | 420 | |
hudakz | 9:a156d3de5647 | 421 | // Skip over the rest of this option |
hudakz | 9:a156d3de5647 | 422 | while (opt_len--) { |
hudakz | 9:a156d3de5647 | 423 | _dhcpUdpSocket.read(); |
hudakz | 9:a156d3de5647 | 424 | } |
hudakz | 9:a156d3de5647 | 425 | break; |
hudakz | 9:a156d3de5647 | 426 | } |
hudakz | 9:a156d3de5647 | 427 | } |
hudakz | 9:a156d3de5647 | 428 | } |
hudakz | 9:a156d3de5647 | 429 | |
hudakz | 9:a156d3de5647 | 430 | // Need to skip to end of the packet regardless here |
hudakz | 9:a156d3de5647 | 431 | _dhcpUdpSocket.flush(); |
hudakz | 9:a156d3de5647 | 432 | |
hudakz | 9:a156d3de5647 | 433 | return type; |
hudakz | 9:a156d3de5647 | 434 | } |
hudakz | 9:a156d3de5647 | 435 | |
hudakz | 9:a156d3de5647 | 436 | /* |
hudakz | 9:a156d3de5647 | 437 | returns: |
hudakz | 9:a156d3de5647 | 438 | 0/DHCP_CHECK_NONE: nothing happened |
hudakz | 9:a156d3de5647 | 439 | 1/DHCP_CHECK_RENEW_FAIL: renew failed |
hudakz | 9:a156d3de5647 | 440 | 2/DHCP_CHECK_RENEW_OK: renew success |
hudakz | 9:a156d3de5647 | 441 | 3/DHCP_CHECK_REBIND_FAIL: rebind fail |
hudakz | 9:a156d3de5647 | 442 | 4/DHCP_CHECK_REBIND_OK: rebind success |
hudakz | 9:a156d3de5647 | 443 | */ |
hudakz | 9:a156d3de5647 | 444 | int DhcpClient::checkLease() |
hudakz | 9:a156d3de5647 | 445 | { |
hudakz | 9:a156d3de5647 | 446 | time_t now = time(NULL); |
hudakz | 9:a156d3de5647 | 447 | volatile int rc = DHCP_CHECK_NONE; |
hudakz | 9:a156d3de5647 | 448 | if (_lastCheck != 0) { |
hudakz | 9:a156d3de5647 | 449 | //calc how many ms past the timeout we are |
hudakz | 9:a156d3de5647 | 450 | time_t factor = now - _secTimeout; |
hudakz | 9:a156d3de5647 | 451 | |
hudakz | 9:a156d3de5647 | 452 | //if on or passed the timeout, reduce the counters |
hudakz | 9:a156d3de5647 | 453 | if (factor >= 0) { |
hudakz | 9:a156d3de5647 | 454 | //next timeout should be now plus 1s |
hudakz | 9:a156d3de5647 | 455 | _secTimeout = now + 1; |
hudakz | 9:a156d3de5647 | 456 | |
hudakz | 9:a156d3de5647 | 457 | //reduce the counters by that mouch |
hudakz | 9:a156d3de5647 | 458 | //if we can assume that the cycle time (factor) is fairly constant |
hudakz | 9:a156d3de5647 | 459 | //and if the remainder is less than cycle time * 2 |
hudakz | 9:a156d3de5647 | 460 | //do it early instead of late |
hudakz | 9:a156d3de5647 | 461 | if (_renewInSec < factor * 2) |
hudakz | 9:a156d3de5647 | 462 | _renewInSec = 0; |
hudakz | 9:a156d3de5647 | 463 | else |
hudakz | 9:a156d3de5647 | 464 | _renewInSec -= factor; |
hudakz | 9:a156d3de5647 | 465 | |
hudakz | 9:a156d3de5647 | 466 | if (_rebindInSec < factor * 2) |
hudakz | 9:a156d3de5647 | 467 | _rebindInSec = 0; |
hudakz | 9:a156d3de5647 | 468 | else |
hudakz | 9:a156d3de5647 | 469 | _rebindInSec -= factor; |
hudakz | 9:a156d3de5647 | 470 | } |
hudakz | 9:a156d3de5647 | 471 | |
hudakz | 9:a156d3de5647 | 472 | //if we have a lease but should renew, do it |
hudakz | 9:a156d3de5647 | 473 | if (_dhcp_state == STATE_DHCP_LEASED && _renewInSec <= 0) { |
hudakz | 9:a156d3de5647 | 474 | _dhcp_state = STATE_DHCP_REREQUEST; |
hudakz | 9:a156d3de5647 | 475 | rc = 1 + requestDhcpLease(); |
hudakz | 9:a156d3de5647 | 476 | } |
hudakz | 9:a156d3de5647 | 477 | |
hudakz | 9:a156d3de5647 | 478 | //if we have a lease or is renewing but should bind, do it |
hudakz | 9:a156d3de5647 | 479 | if ((_dhcp_state == STATE_DHCP_LEASED || _dhcp_state == STATE_DHCP_START) && _rebindInSec <= 0) { |
hudakz | 9:a156d3de5647 | 480 | //this should basically restart completely |
hudakz | 9:a156d3de5647 | 481 | _dhcp_state = STATE_DHCP_START; |
hudakz | 9:a156d3de5647 | 482 | resetDhcpLease(); |
hudakz | 9:a156d3de5647 | 483 | rc = 3 + requestDhcpLease(); |
hudakz | 9:a156d3de5647 | 484 | } |
hudakz | 9:a156d3de5647 | 485 | } |
hudakz | 9:a156d3de5647 | 486 | else { |
hudakz | 9:a156d3de5647 | 487 | _secTimeout = now + 1; |
hudakz | 9:a156d3de5647 | 488 | } |
hudakz | 9:a156d3de5647 | 489 | |
hudakz | 9:a156d3de5647 | 490 | _lastCheck = now; |
hudakz | 9:a156d3de5647 | 491 | return rc; |
hudakz | 9:a156d3de5647 | 492 | } |
hudakz | 9:a156d3de5647 | 493 | |
hudakz | 9:a156d3de5647 | 494 | /** |
hudakz | 9:a156d3de5647 | 495 | * @brief |
hudakz | 9:a156d3de5647 | 496 | * @note |
hudakz | 9:a156d3de5647 | 497 | * @param |
hudakz | 9:a156d3de5647 | 498 | * @retval |
hudakz | 9:a156d3de5647 | 499 | */ |
hudakz | 9:a156d3de5647 | 500 | IpAddress DhcpClient::getLocalIp() |
hudakz | 9:a156d3de5647 | 501 | { |
hudakz | 9:a156d3de5647 | 502 | return IpAddress(_dhcpLocalIp); |
hudakz | 9:a156d3de5647 | 503 | } |
hudakz | 9:a156d3de5647 | 504 | |
hudakz | 9:a156d3de5647 | 505 | /** |
hudakz | 9:a156d3de5647 | 506 | * @brief |
hudakz | 9:a156d3de5647 | 507 | * @note |
hudakz | 9:a156d3de5647 | 508 | * @param |
hudakz | 9:a156d3de5647 | 509 | * @retval |
hudakz | 9:a156d3de5647 | 510 | */ |
hudakz | 9:a156d3de5647 | 511 | IpAddress DhcpClient::getSubnetMask() |
hudakz | 9:a156d3de5647 | 512 | { |
hudakz | 9:a156d3de5647 | 513 | return IpAddress(_dhcpSubnetMask); |
hudakz | 9:a156d3de5647 | 514 | } |
hudakz | 9:a156d3de5647 | 515 | |
hudakz | 9:a156d3de5647 | 516 | /** |
hudakz | 9:a156d3de5647 | 517 | * @brief |
hudakz | 9:a156d3de5647 | 518 | * @note |
hudakz | 9:a156d3de5647 | 519 | * @param |
hudakz | 9:a156d3de5647 | 520 | * @retval |
hudakz | 9:a156d3de5647 | 521 | */ |
hudakz | 9:a156d3de5647 | 522 | IpAddress DhcpClient::getGatewayIp() |
hudakz | 9:a156d3de5647 | 523 | { |
hudakz | 9:a156d3de5647 | 524 | return IpAddress(_dhcpGatewayIp); |
hudakz | 9:a156d3de5647 | 525 | } |
hudakz | 9:a156d3de5647 | 526 | |
hudakz | 9:a156d3de5647 | 527 | /** |
hudakz | 9:a156d3de5647 | 528 | * @brief |
hudakz | 9:a156d3de5647 | 529 | * @note |
hudakz | 9:a156d3de5647 | 530 | * @param |
hudakz | 9:a156d3de5647 | 531 | * @retval |
hudakz | 9:a156d3de5647 | 532 | */ |
hudakz | 9:a156d3de5647 | 533 | IpAddress DhcpClient::getDhcpServerIp() |
hudakz | 9:a156d3de5647 | 534 | { |
hudakz | 9:a156d3de5647 | 535 | return IpAddress(_dhcpDhcpServerIp); |
hudakz | 9:a156d3de5647 | 536 | } |
hudakz | 9:a156d3de5647 | 537 | |
hudakz | 9:a156d3de5647 | 538 | /** |
hudakz | 9:a156d3de5647 | 539 | * @brief |
hudakz | 9:a156d3de5647 | 540 | * @note |
hudakz | 9:a156d3de5647 | 541 | * @param |
hudakz | 9:a156d3de5647 | 542 | * @retval |
hudakz | 9:a156d3de5647 | 543 | */ |
hudakz | 9:a156d3de5647 | 544 | IpAddress DhcpClient::getDnsServerIp() |
hudakz | 9:a156d3de5647 | 545 | { |
hudakz | 9:a156d3de5647 | 546 | return IpAddress(_dhcpDnsServerIp); |
hudakz | 9:a156d3de5647 | 547 | } |
hudakz | 9:a156d3de5647 | 548 | |
hudakz | 9:a156d3de5647 | 549 | /** |
hudakz | 9:a156d3de5647 | 550 | * @brief |
hudakz | 9:a156d3de5647 | 551 | * @note |
hudakz | 9:a156d3de5647 | 552 | * @param |
hudakz | 9:a156d3de5647 | 553 | * @retval |
hudakz | 9:a156d3de5647 | 554 | */ |
hudakz | 9:a156d3de5647 | 555 | void DhcpClient::printByte(char* buf, uint8_t n) |
hudakz | 9:a156d3de5647 | 556 | { |
hudakz | 9:a156d3de5647 | 557 | char* str = &buf[1]; |
hudakz | 9:a156d3de5647 | 558 | buf[0] = '0'; |
hudakz | 9:a156d3de5647 | 559 | do { |
hudakz | 9:a156d3de5647 | 560 | unsigned long m = n; |
hudakz | 9:a156d3de5647 | 561 | n /= 16; |
hudakz | 9:a156d3de5647 | 562 | |
hudakz | 9:a156d3de5647 | 563 | char c = m - 16 * n; |
hudakz | 9:a156d3de5647 | 564 | *str-- = c < 10 ? c + '0' : c + 'A' - 10; |
hudakz | 9:a156d3de5647 | 565 | } while (n); |
hudakz | 9:a156d3de5647 | 566 | } |