Go Down

Topic: Adapt WiFi project to Ethernet Shield (Read 617 times) previous topic - next topic

pdavalos

Greetings!

My first post, I hope to do it rigth.

I have to send sensed data to Elasticsearch database, with JSON protocol. I've found this tutorial

https://www.elastic.co/blog/arduino-based-home-weather-station-on-the-elastic-stack

and it looks great, but I uses a WiFi module, and I need to use the Ethernet Shield. So, I've been trying to adapt the code from WiFi headers and .cpp to Ethernet directives, but something isn't working fine. Some functions of WiFi dynamics don't work just by replacing "WiFiFunction" for "EthernetFunction"  :D

Here is the original code.

1) I added the

#include <Ethernet.h>
#include "EthernetUdp.h"

2) Comented all the "WiFi" parts

3) Added EthernetUDP Udp;

The last error I have is related to NTP server,

Code: [Select]

Serial.println("Transmit NTP Request");
  // get a random server from the pool
  WiFi.hostByName(ntpServerName, ntpServerIP);


Some functions don't work on both WiFi and Ethernet.

Thanks in advance!

Code: [Select]

/*
 *  Simple HTTP get webclient test
 */
#include <ESP8266WiFi.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP085_U.h>
#include <TimeLib.h>
#include <WiFiUdp.h>
Adafruit_BMP085_Unified bmp = Adafruit_BMP085_Unified(10085);
static const char ntpServerName[] = "us.pool.ntp.org"; 
// Setup your wifi SSID and password here.
const char* ssid     = "CanIGetAWiFi";
const char* password = "n0youCan7";
const int timeZone = 0;  // UTC
// Variables needed for NTP
// Elasticsearch needs us to generate timestamps for the data in order to make date histograms in Kibana.
WiFiUDP Udp;
unsigned int localPort = 8888;  // local port to listen for UDP packets
time_t getNtpTime();
void printDigits(int digits);
void sendNTPpacket(IPAddress &address);
// This is the IP address, or DNS name of my Elasticsearch instance.
const char* host = "192.168.1.215";
const int port = 9200;
int motion;
// Variables
float temperature;
String timestamp;
time_t start_time;
uint32_t t_ms;
uint32_t start_mills;
String run_mills;
int milis_chars;
void setup() {
  Serial.begin(115200);
  delay(100);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected"); 
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println("Setting up NTP");
  Udp.begin(localPort);
  Serial.print("Local port: ");
  Serial.println(Udp.localPort());
  Serial.println("waiting for sync");
  setSyncProvider(getNtpTime);
  setSyncInterval(300);
  start_time = now();
  Serial.println("Pressure Sensor Test"); Serial.println("");
  /* Initialise the sensor */
  if(!bmp.begin())
  {
    /* There was a problem detecting the BMP085 ... check your connections */
    Serial.print("Ooops, no BMP180 detected ... Check your wiring!");
    while(1);
  }
}
void loop() {   
  // Measure pressure & temperature from BMP sensor
  // Modified from https://learn.adafruit.com/bmp085/using-the-bmp085-api-v2
  sensors_event_t event;
  bmp.getEvent(&event);
  float pressure = event.pressure;
  float temperature;
  bmp.getTemperature(&temperature);
  // Use WiFiClient class to create TCP connections, connect to the Elasticsearch instance.
  WiFiClient client;
  if (!client.connect(host, port)) {
    Serial.println("connection failed");
    return;
  }
  run_mills = String(millis());
  milis_chars = run_mills.length();
  // To generate a millisecond unix timestamp, we first get the second timestamp, and add to it, the last three characters of the arduino/relative millisecond timestamp
  timestamp = String(now()) + run_mills.charAt(milis_chars-3) + run_mills.charAt(milis_chars-2) + run_mills.charAt(milis_chars-1);
  // With such a simple document, we're just going to use a string to generate the JSON to send to Elasticsearch
  String data = "{pressure: "+String(pressure)+", temperature: "+String(temperature)+", timestamp: "+ timestamp +"}";
  // We can inspect the data being sent over the Serial line, in the Arduino IDE.
  Serial.println(data);
  // We now create a URI for the request
  // This is the index of the Elasticsearch document we're creating
  String url = "/weather/reading";
  //
  client.print(String("POST ") + url + " HTTP/1.1\r\n" +
               // If you're using Shield, you'll need to generate an authentication header
               "Content-Length: " + data.length() + "\r\n" +
               "\r\n" + data);
  // We need this delay in here to give the WiFi Time
  delay(50);
  // Read all the lines of the reply from server and print them to Serial
  while(client.available()){
    String line = client.readStringUntil('\r');
    Serial.print(line);
  }
  Serial.println();
}
/* Copied from https://github.com/PaulStoffregen/Time/blob/master/examples/TimeNTP_ESP8266WiFi/TimeNTP_ESP8266WiFi.ino#L99 */
/*-------- NTP code ----------*/
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
time_t getNtpTime()
{
  IPAddress ntpServerIP; // NTP server's ip address
  while (Udp.parsePacket() > 0) ; // discard any previously received packets
  Serial.println("Transmit NTP Request");
  // get a random server from the pool
  WiFi.hostByName(ntpServerName, ntpServerIP);
  Serial.print(ntpServerName);
  Serial.print(": ");
  Serial.println(ntpServerIP);
  sendNTPpacket(ntpServerIP);
  uint32_t beginWait = millis();
  while (millis() - beginWait < 1500) {
    int size = Udp.parsePacket();
    if (size >= NTP_PACKET_SIZE) {
      Serial.println("Receive NTP Response");
      Udp.read(packetBuffer, NTP_PACKET_SIZE);  // read packet into the buffer
      unsigned long secsSince1900;
      // convert four bytes starting at location 40 to a long integer
      secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
      secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
      secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
      secsSince1900 |= (unsigned long)packetBuffer[43];
      return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
    }
  }
  Serial.println("No NTP Response :-(");
  return 0; // return 0 if unable to get the time
}
// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &address)
{
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12] = 49;
  packetBuffer[13] = 0x4E;
  packetBuffer[14] = 49;
  packetBuffer[15] = 52;
  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

PaulS

Quote
and it looks great, but I uses a WiFi module, and I need to use the Ethernet Shield.
A GET request doesn't know if there are wires involved, or not.

You are free to look at the WiFi library to see how it implemented hostByName(). It does NOT depend on the communication with the router being handled by radio waves rather than copper.
The art of getting good answers lies in asking good questions.

pdavalos

Hi PaulS, thanks for your answer

I get it, but (for example)

Code: [Select]
WiFi.hostByName(ntpServerName, ntpServerIP)

class Ethernet doesn't have a function like that (I think) and I don't really know how to adapt it to make it work.

Else, the autor of the script states

Code: [Select]
client.print(String("POST ") + url + " HTTP/1.1\r\n" +
               // If you're using Shield, you'll need to generate an authentication header
               "Content-Length: " + data.length() + "\r\n" +
               "\r\n" + data);



Do you know what header is he talking about?

Thank you!


PaulS

Quote
class Ethernet doesn't have a function like that (I think) and I don't really know how to adapt it to make it work.
You need to look at what the Wifi library does to implement that function. What it does does NOT depend on the fact that no wires are involved. It is strictly a domain name lookup by IP address. It is possible to do that with an ethernet shield.

Quote
Do you know what header is he talking about?
No, and I suspect that he doesn't either. Whether the ethernet or WiFi capability is built into the Arduino board, or a separate piece of hardware attached a shield, has NOTHING to do with what the URL to connect to a service looks like.
The art of getting good answers lies in asking good questions.

gdsports

Quote
WiFi.hostByName(ntpServerName, ntpServerIP)
Take a look under the hood (open the source file EthernetUdp.cpp).

Code: [Select]
int EthernetUDP::beginPacket(const char *host, uint16_t port)

Looks like you can just pass the hostname string instead of the IP address to Udp.beginPacket.

Remove the hostByName stuff.


pdavalos

Hi gdsports! Thanks for your answer.

I made it work, at least the communication with the NTP server and the setting for send data (I'm a beginner so, that's an accomplishment to me!)

My problem now is to send that data to Elasticsearch database. Part of the code states:

Code: [Select]

// With such a simple document, we're just going to use a string to generate the JSON to send to Elasticsearch
  String data = "{pressure: "+String(pressure)+", temperature: "+String(temperature)+", timestamp: "+ timestamp +"}";
 
  // We now create a URI for the request
  // This is the index of the Elasticsearch document we're creating
  String url = "/weather/reading";
  //
  client.print(String("POST ") + url + " HTTP/1.1\r\n" +
               // If you're using Shield, you'll need to generate an authentication header
               "Content-Length: " + data.length() + "\r\n" +
               "\r\n" + data);



This part I don't really know where to run to...!

The script is creating a file somewhere? What authetication header is he meanning?

Thank you guys very much!

gdsports

I have never used elastisearch so you will have to depend on the docs. Search for "elastisearch shield" because this reveals Shield is a plugin for Elastisearch. The author is not referring to Arduino board shields. If you have not installed the Shield plugin, I suggest ignoring references to it in the code.

client is a TCP socket connected to elastisearch.

Code: [Select]

  // Use WiFiClient class to create TCP connections, connect to the Elasticsearch instance.
  WiFiClient client;


So client.print(...) is not printing to a file but sending the string to elastisearch.

Go Up