0

I am trying to use an sprintf with an IPAddress and I am not sure why Serial.print() is able to convert IPAddress to a reasonable human readable representation. I mean, presumably it uses an overloaded function that detects what type the parameter is. I want to somehow leverage that functionality ina printf or sprintf.

What I know:

IPAddress ip = WiFi.localIP();
Serial.println(ip);

works great and outputs "192.168.4.1".

char *buf = malloc(128);
sprintf(buf,"%s",String(ip).c_str());

generates buf as "17082560" which isn't wrong, but its not the usual format for an IP address.

I could write a function to output it the way I want, but I figured there must be some relatively simple way to utilize the functionality that Serial.print() provides but somehow channel it to an sprintf() call. Or any other simple way to provide a formatted IP output would be appreciated.

3

Whereas KIIV's answer solves your problem, it does not address some very interesting parts of your original question:

I am not sure why Serial.print() is able to convert IPAddress to a reasonable human readable representation

This works thanks to a couple of abstract classes in the Arduino library called Print and Printable. A Print object is anything that can output text, like a serial port, a network connection or an LCD. A Printable object is anything that can be converted to a text representation and sent through a Print object.

From Print.h:

class Print
{
    // ...
    virtual size_t write(uint8_t) = 0;
    // ...
    size_t print(const String &);
    size_t print(const char[]);
    size_t print(unsigned int, int = DEC);
    size_t print(const Printable&);
    // ...
    size_t println(const String &s);
    // ...
};

In other words, in order to create a Print, you only have to implement write(uint8_t), which should output a single character. Then you get all the print() and println() methods.

From Printable.h:

class Printable
{
  public:
    virtual size_t printTo(Print& p) const = 0;
};

This means you can make any object Printable by implementing printTo(Print&).

From IPAddress.h:

class IPAddress : public Printable {
    // ...
};

This means IPAddress objects are Printable: they know how to print themselves into any Print. You can find the implementation of IPAddress::printTo(Print&) at the end of IPAddress.cpp.

Thus, to answer your question, Serial.print() does not really know how to create a textual representation of the IPAddress, but IPAddress knows how to send itself through Serial, or any other Print object.

I want to somehow leverage that functionality [...] there must be some relatively simple way to utilize the functionality that Serial.print() provides but somehow channel it to an sprintf() call.

The functionality is not provided by Serial.print(): it's the fact that the IPAddress is Printable. In order to leverage that, you have to create a Print object. You cannot use sprintf() for that, but you can create a Print object which behaves like sprintf() and writes into a C string:

// Write into a C string (a char array), a la sprintf().
// Warning: there is no check for buffer overflow.
class StringPrinter : public Print
{
public:
    StringPrinter(char *buffer) : buf(buffer), pos(0) {}
    virtual size_t write(uint8_t c)
    {
        buf[pos++] = c;  // add the character to the string
        buf[pos] = 0;    // null perminator
        return 1;        // one character written
    }
private:
    char *buf;
    size_t pos;
};

which could be used like this:

char buf[16];
StringPrinter(buf).print(ip);

For the sake of simplicity, I would generally prefer the straight sprintf() solution. However, if you are short on flash, the code above is likely to be smaller, as sprintf() is large and you are very likely to be already using Print in your program.

| improve this answer | |
  • Awesome information about the abstract classes. This is kind of what I was dying to know. – Octopus Oct 27 '16 at 22:38
2

In short: hex(17082560) = 0x0104A8C0 where: 0x01 = 1, 0x04 = 4, 0xA8 = 168 and 0xC0 = 192. See the pattern? It's printed as a single uint32_t.

Of course endianness is reversed as platform is little endian but IP Adresses are in network byte order (big endian)

And because you didn't understand this answer, there is also whole code:

uint32_t ip = (uint32_t) WiFi.localIP();
sprintf(buf, "%u.%u.%u.%u", ip & 0xFF, (ip>>8) & 0xFF, (ip>>16) & 0xFF, (ip>>24) & 0xFF);

Maybe even better:

IPAddress ip = WiFi.localIP();
sprintf(buf, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
| improve this answer | |
  • yes, as I said, "which isn't wrong, but its not the usual format for an IP address". this doesn't answer my question. Thanks, though. – Octopus Oct 25 '16 at 20:16
  • @Octopus: I hoped you'll figure this out by yourself. Anyway code added (untested) – KIIV Oct 25 '16 at 20:31
  • right on. now i'm having one of those "why didn't i think of that" moments. perfect! – Octopus Oct 25 '16 at 22:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.