This post aims to get you started with the Industruino Ethernet module with a plug&play example sketch. It uses a common HTTP GET request that can be easily modified for any API, or any specific TCP communication. The sketch requires 3 libraries:
- Ethernet2 this needs to be the Industruino version, as the SPI speed needs to be set to 4MHz
- UC1701 for the Industruino LCD
- Adafruit_SleepyDog for watchdog reset functionality
The sketch does the following:
- Enable the watchdog timer with an interval of 20 seconds: if the code hangs for some reason so the watchdog does not get reset within 20 seconds, the unit reboots
- MAC address: the Industruino D21G has a unique MAC address stored in the EEPROM of the RTC, this 8-byte MAC is retrieved and formatted to 6 bytes for IPv4
- Check ETH module: the Ethernet module has 8KB non-volatile FRAM memory; we use this to check whether the module is connected by writing/reading a flag at address 0 of the FRAM
- Start Ethernet: we can use a fixed IP address, or request via DHCP, using the parameter USE_DHCP at the top of the sketch
- Connect to server: the sketch loop makes a connection to the TCP_SERVER at port TCP_SERVER_PORT
- HTTP GET request: if a connection is established, the sketch retrieves www.arduino.cc/latest.txt and looks for the '200 OK' reply
- If the connection to the server fails, Ethernet is restarted
- At the start of the loop, the watchdog timer is reset
In case the ETH module is not connected, the sketch stops, which triggers a watchdog reset after 20 seconds.
In case USE_DHCP is set to 1 but no IP address is assigned by a DHCP server, the sketch also stops, and the watchdog will trigger a reset.
Open the Serial Monitor to see diagnostics, and most info is also displayed on the LCD.
Below is the code, also on github.
/*
Industruino Ethernet example sketch for:
> Industruino D21G PROTO or IND.I/O
> Industruino Ethernet module
Libraries needed:
> Ethernet2: Industruino version at https://github.com/Industruino/Ethernet2
> UC1701: https://github.com/Industruino/UC1701
> Adafruit_SleepyDog: https://github.com/adafruit/Adafruit_SleepyDog
The D21G topboard has a built-in RTC (MCP79402) with a EUI-64 number that can be used as MAC address
The Ethernet module has an SD card and 8KB non-volatile FRAM memory
This sketch:
-reads 6-byte (48-bit) MAC address from RTC EEPROM over I2C
-checks if Ethernet module is connected (by writing/reading a flag in FRAM)
-starts Ethernet with MAC address and static IP address or DHCP
-sends an HTTP GET request to a server and shows the reply status
-if the code hangs anywhere for more than 20 seconds, the watchdog timer resets the unit
Tom Tobback June 2019 for Industruino
*/
////////////////// RTC MAC ////////////////////////////////////////
#include <Wire.h> // for RTC MAC
byte mac[6]; // read from RTC
////////////////// ETHERNET //////////////////////////////////////
#include <SPI.h> // for Ethernet
#include <SD.h> // for FRAM to work
#include <Ethernet2.h> // use Industruino version
EthernetClient client;
////////////////// USER CONFIGURATION /////////////////////////////
#define USE_DHCP 0
IPAddress industruino_ip (192, 168, 1, 100); // example
#define TCP_SERVER "www.arduino.cc"
#define TCP_SERVER_PORT 80 // for http
//////////////////////// FRAM ///////////////////////////////////
const byte FRAM_CMD_WREN = 0x06; //0000 0110 Set Write Enable Latch
const byte FRAM_CMD_WRDI = 0x04; //0000 0100 Write Disable
const byte FRAM_CMD_RDSR = 0x05; //0000 0101 Read Status Register
const byte FRAM_CMD_WRSR = 0x01; //0000 0001 Write Status Register
const byte FRAM_CMD_READ = 0x03; //0000 0011 Read Memory Data
const byte FRAM_CMD_WRITE = 0x02; //0000 0010 Write Memory Data
const int FRAM_CS1 = 6; //chip select 1
//////////////////// INDUSTRUINO LCD //////////////////////////////
#include <UC1701.h>
static UC1701 lcd;
#define LCD_PIN 26
/////////////////////// WATCHDOG TIMER ////////////////////////////
#include <Adafruit_SleepyDog.h>
const unsigned int watchdog_interval = 20000; // 20 seconds: if no wdt reset within this interval, wdt will reset the unit
///////////////////////////////////////////////////////////////////
/// FRAM functions need to be included before setup()
///////////////////////////////////////////////////////////////////
/**
Write to FRAM (assuming 2 FM25C160 are used)
addr: starting address
buf: pointer to data
count: data length.
If this parameter is omitted, it is defaulted to one byte.
returns: 0 operation is successful
1 address out of range
*/
int FRAMWrite(int addr, byte *buf, int count = 1) {
if (addr > 0x7ff) return -1;
byte addrMSB = (addr >> 8) & 0xff;
byte addrLSB = addr & 0xff;
digitalWrite(FRAM_CS1, LOW);
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
SPI.transfer(FRAM_CMD_WREN); //write enable
digitalWrite(FRAM_CS1, HIGH);
digitalWrite(FRAM_CS1, LOW);
SPI.transfer(FRAM_CMD_WRITE); //write command
SPI.transfer(addrMSB);
SPI.transfer(addrLSB);
for (int i = 0; i < count; i++) SPI.transfer(buf[i]);
digitalWrite(FRAM_CS1, HIGH);
return 0;
}
///////////////////////////////////////////////////////////////////
/**
Read from FRAM (assuming 2 FM25C160 are used)
addr: starting address
buf: pointer to data
count: data length.
If this parameter is omitted, it is defaulted to one byte.
returns: 0 operation is successful
1 address out of range
*/
int FRAMRead(int addr, byte *buf, int count = 1) {
if (addr > 0x7ff) return -1;
byte addrMSB = (addr >> 8) & 0xff;
byte addrLSB = addr & 0xff;
digitalWrite(FRAM_CS1, LOW);
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
SPI.transfer(FRAM_CMD_READ);
SPI.transfer(addrMSB);
SPI.transfer(addrLSB);
for (int i = 0; i < count; i++) buf[i] = SPI.transfer(0x00);
digitalWrite(FRAM_CS1, HIGH);
return 0;
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
void setup() {
// setup LCD
pinMode(LCD_PIN, OUTPUT); // LCD backlight
digitalWrite(LCD_PIN, HIGH); // LCD backlight ON
lcd.begin();
lcd.print("INDUSTRUINO ETHERNET");
// start SerialUSB for Serial Monitor
SerialUSB.begin(115200);
//while (!SerialUSB); // wait for Serial Monitor to connect, for testing only
SerialUSB.println("===================================");
SerialUSB.println("== INDUSTRUINO ETHERNET EXAMPLE ===");
SerialUSB.println("===================================");
printTime(); SerialUSB.println("SETUP");
// start watchdog timer
Watchdog.enable(watchdog_interval);
SerialUSB.println("Watchdog timer started");
// get MAC address from RTC
printTime();
readMACfromRTC(); // MAC stored in RTC eeprom
lcd.setCursor(0, 1);
lcd.print("MAC ");
for (int u = 0; u < 6; u++) {
lcd.print(mac[u], HEX);
if (u < 5) lcd.print(":");
}
// setup Ethernet module: SPI chip select pins
pinMode(FRAM_CS1, OUTPUT); // for FRAM
digitalWrite(FRAM_CS1, HIGH);
pinMode(4, OUTPUT); // for SD
digitalWrite(4, HIGH);
pinMode(10, OUTPUT); // for Ethernet
digitalWrite(10, HIGH);
// check if Ethernet module is connected (by write/read FRAM flag)
lcd.setCursor(0, 2);
printTime();
if (ethernetModule()) {
SerialUSB.println("Ethernet module found");
lcd.print("ETH module found");
} else {
SerialUSB.println("Ethernet module not found, stop here");
lcd.print("ETH module NOT found");
lcd.setCursor(0, 3);
lcd.print("STOP");
while (1); // stay here forever, this will trigger the watchdog timer to reset the unit
}
// start Ethernet
if (USE_DHCP) {
SerialUSB.print("requesting IP address from DHCP... ");
if (!Ethernet.begin(mac)) {
SerialUSB.println("could not get IP address over DHCP, stop here");
lcd.setCursor(0, 3);
lcd.print("DHCP failed, STOP");
while (1); // stay here forever, this will trigger the watchdog timer to reset the unit
}
SerialUSB.println("OK");
} else { // static IP
Ethernet.begin(mac, industruino_ip);
}
printTime();
SerialUSB.print("Ethernet started with above MAC and IP: ");
SerialUSB.println(Ethernet.localIP());
lcd.setCursor(0, 3);
lcd.print("IP ");
lcd.print(Ethernet.localIP());
if (USE_DHCP) lcd.print(" DHCP");
printTime();
SerialUSB.println("END OF SETUP");
SerialUSB.println();
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
void loop() {
Watchdog.reset(); // needed to avoid watchdog reset
printTime();
SerialUSB.print("connecting to server ");
SerialUSB.print(TCP_SERVER);
SerialUSB.print(" on port ");
SerialUSB.print(TCP_SERVER_PORT);
SerialUSB.print("... ");
lcd.setCursor(0, 4);
lcd.print("connecting to:");
lcd.setCursor(0, 5);
lcd.print(TCP_SERVER);
if (client.connect(TCP_SERVER, TCP_SERVER_PORT)) {
SerialUSB.println("connected");
lcd.print(" OK");
// example for HTTP GET REQUEST
client.println("GET /latest.txt HTTP/1.1");
client.print("Host: ");
client.println(TCP_SERVER);
client.println("User-Agent: arduino-ethernet");
client.println("Connection: close");
client.println();
/*
// this block outputs the reply
unsigned long timestamp = millis();
while (client.available() || (millis() - timestamp < 1000)) {
if (client.available()) {
SerialUSB.write(client.read());
}
}
*/
// this block just looks for '200 OK' in the reply
lcd.setCursor(0, 6);
printTime();
if (client.find("200 OK")) {
SerialUSB.println("server replies OK");
lcd.print("server replies OK ");
} else {
SerialUSB.println("no OK received from server");
lcd.print("no OK received ");
}
client.stop();
} else {
printTime();
SerialUSB.println("failed to connect, restarting Ethernet");
lcd.setCursor(0, 6);
lcd.print("failed to connect ");
lcd.setCursor(0, 7);
lcd.print("restart Ethernet... ");
if (USE_DHCP) {
SerialUSB.print("requesting IP address from DHCP... ");
if (!Ethernet.begin(mac)) {
SerialUSB.println("could not get IP address over DHCP, stop here");
lcd.setCursor(0, 3);
lcd.print("DHCP failed, STOP");
while (1); // stay here forever, this will trigger the watchdog timer to reset the unit
}
SerialUSB.println("OK");
} else { // static IP
Ethernet.begin(mac, industruino_ip);
}
// refresh IP on LCD
lcd.setCursor(0, 3);
lcd.print("IP ");
lcd.print(Ethernet.localIP());
if (USE_DHCP) lcd.print(" DHCP");
}
SerialUSB.println();
delay(5000);
// clear LCD lines about connection
lcd.setCursor(0,5);
lcd.print(" ");
lcd.setCursor(0,6);
lcd.print(" ");
lcd.setCursor(0,7);
lcd.print(" ");
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
//////////////////////////// MAC ADDRESS ////////////////////////////////////////////
// the RTC has a MAC address stored in EEPROM - 8 bytes 0xf0 to 0xf7
void readMACfromRTC() {
Wire.begin(); // I2C for RTC MAC
SerialUSB.print("Reading MAC from RTC EEPROM: ");
int mac_index = 0;
for (int i = 0; i < 8; i++) { // read 8 bytes of 64-bit MAC address, 3 bytes valid OUI, 5 bytes unique EI
byte m = readByte(0x57, 0xf0 + i);
SerialUSB.print(m, HEX);
if (i < 7) SerialUSB.print(":");
if (i != 3 && i != 4) { // for 6-bytes MAC, skip first 2 bytes of EI
mac[mac_index] = m;
mac_index++;
}
}
SerialUSB.println();
SerialUSB.print("Extracted 6-byte MAC address: ");
for (int u = 0; u < 6; u++) {
SerialUSB.print(mac[u], HEX);
if (u < 5) SerialUSB.print(":");
}
SerialUSB.println();
}
//////////////////////////// MAC ADDRESS /////////////////////////////////////////////
// the RTC has a MAC address stored in EEPROM
uint8_t readByte(uint8_t i2cAddr, uint8_t dataAddr) {
Wire.beginTransmission(i2cAddr);
Wire.write(dataAddr);
Wire.endTransmission(false); // don't send stop
Wire.requestFrom(i2cAddr, 1);
return Wire.read();
}
///////////////////////// CHECK MODULE //////////////////////////////////////////////
boolean ethernetModule() {
SD.begin(4); // FRAM seems to work only after SD is initialised
byte set_flag[] = {1};
FRAMWrite(0, set_flag, 1);
byte read_flag[1];
FRAMRead(0, read_flag, 1);
if (read_flag[0] == 1) return true;
else return false;
}
///////////////////////// PRINT TIME ////////////////////////////////////////////////
void printTime() {
SerialUSB.print("[" + String(millis() / 1000.0) + "] ");
}