I have Arduino + Ethenet shield. I want to dynamically change the ip depending on that will come at the input com-port.
The main problem is - input string has a String type, and Ethernet.begin method accepts a byte array. In general, I can not understand how to convert this string correctly. Tried to make a bicycle(crutch) by moving the string in the char array, and then in the byte array. Did not work out. How convert string to byte[]?
#include <SPI.h>
#include <Ethernet.h>
String readString;
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte myserver[] = { 208, 104, 2, 86 }; // zoomkat web page server IP address
EthernetClient client;
void initEthernetConfig(byte ip[])
{
Ethernet.begin(mac, ip);
Serial.begin(9600);
Serial.println("Better client test 12/01/11"); // so I can keep track of what is loaded
Serial.println("Send an e in serial monitor to test"); // what to do to test
}
void setup(){
byte ip[] = { 10, 28, 33, 4 };
// initEthernetConfig(ip);
}
void loop(){
// check for serial input
while (Serial.available()) {
char c = Serial.read(); //gets one byte from serial buffer
readString += c; //makes the String readString
delay(2); //slow looping to allow buffer to fill with next character
}
if (readString.length() >0) {
byte inArray[4];
char * tokens;
int i = 0;
tokens = strtok(readString, ".");
while (tokens != NULL) {
inArray[i] = atoi(tokens);
tokens = strtok(NULL, ".");
i++;
}
initEthernetConfig(inArray);
}
}
This is a possible solution, it parses, dumps and sets an IPv4:
#include <SPI.h>
#include <Ethernet.h>
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte myserver[] = { 208, 104, 2, 86 }; // zoomkat web page server IP address
EthernetClient client;
/**
* help functions declaration
*/
void get_ip(byte ip[4]);
void dump_ip(byte ip[4]);
void initEthernetConfig(byte ip[]);
/**
* setup && loop
*/
void setup(){
Serial.begin(9600);
}
void loop()
{
byte ip[4];
get_ip(ip);
dump_ip(ip);
initEthernetConfig(ip);
}
/**
* help function implementation
*/
void get_ip(byte ip[4])
{
for (byte i = 0; i < 4; ++i) {
while (Serial.available() <= 0) { };
ip[i] = (byte) Serial.parseInt();
if (i < 3) { Serial.read(); } // throw away dot
}
};
void dump_ip(byte ip[4]) {
for (byte i = 0; i < 4; ++i) {
Serial.print(ip[i]);
if (i < 3) {
Serial.print('.');
} else {
Serial.println();
}
}
};
void initEthernetConfig(byte ip[])
{
Ethernet.begin(mac, ip);
Serial.println("Better client test 12/01/11"); // so I can keep track of what is loaded
Serial.println("Send an e in serial monitor to test"); // what to do to test
};
Obviously, outside this testing application you should ensure that the Serial input is ready for sending an IPv4 in the first place, otherwise the function will likely return 0.0.0.0. An idea would be to enrich your communication protocol with string-based commands, e.g. ip: would signal the start of an IPv4 input.
Try this:
while (tokens != NULL) {
inArray[i++] = atoi(tokens) & 0xFF;
tokens = strtok(NULL, ".");
}
Which will get only the last byte of the integer, but that is all you want in the case of values 0-255.
But then again why not just read it in as bytes...
void loop(){
byte inArray[4];
int i = 0;
// check for serial input
while (Serial.available()) {
inArray[i++] = (byte) Serial.read(); //gets one byte from serial buffer
delay(2); //slow looping to allow buffer to fill with next character
}
initEthernetConfig(inArray);
}
Related
I have some code, that turns a LED on/off based on a value on a website (blank page containing a number. The number on the page indicate the number of times the LED should flash.
The problem is that the loop keep running.
I can fix the problem by setting the integer value manually (int c = 3).
Not sure what my problem is.
Maybe one of you can point me in the right direction.
Url: http://b2b.as/lan.php?pid=8855
Code:
#include <Ethernet.h>
#include <SPI.h>
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 1, 104 };
char server[] = "b2b.as";
void setup()
{
Ethernet.begin(mac, ip);
Serial.begin(9600);
delay(1000);
Serial.println(Ethernet.localIP());
Serial.println();
// Set digital pin as output
// 5V
pinMode(8, OUTPUT);
}
void loop()
{
//
Serial.print("\n-----\n");
// Connect to the server
Serial.print("connecting to URL ...");
// Start LAN connection
EthernetClient client;
if (client.connect(server, 80)) {
Serial.println("connected");
client.println("GET /lan.php?pid=8855");
client.println();
} else {
Serial.println("connection failed");
}
// Wait a moment for data to arrive
// Note: This is NOT a reliable way of doing this!
delay(1000);
if (client.available() > 0) {
char c = atoi(client.read());
Serial.print("page value (pick): ");
Serial.print(c, DEC);
Serial.print("\n");
for (int x = 1; x <= int(c); x++) {
Serial.print("picking: #");
Serial.println(x);
digitalWrite(8, HIGH);
Serial.println("8 HIGH ...");
delay(5000); // Add switch
digitalWrite(8, LOW);
Serial.println("8 LOW ...");
delay(1000);
}
Serial.print("end");
}
// Disconnect the client
if (client.connected()) {
//Serial.println();
Serial.print("disconnecting");
client.stop();
}
// Wait another 9s, which will give us a delay of roughly 10s
delay(9000);
}
I assume that the call to lan.php?pid=8855 will just return the value without any formatting, e.g., HTML, XML, JSON. Then your code basically converts the ASCII character 3 to an integer which gives you the integer value 33 (see ASCII Table). Therefore, your loop won't stop.
Solution
Just use the atoi function to convert it to an integer.
char c = atoi(client.read());
It seems that toInt() was the function I was looking for. It convert a string to integer and fixes the loop.
https://www.arduino.cc/en/Reference/StringToInt
Code has been updated and it seems to work:
#include <Ethernet.h>
#include <SPI.h>
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 1, 104 };
char server[] = "b2b.as";
void setup()
{
Ethernet.begin(mac, ip);
Serial.begin(9600);
delay(1000);
Serial.println(Ethernet.localIP());
Serial.println();
// Set digital pin as output
// 5V
pinMode(8, OUTPUT);
}
void loop()
{
//
Serial.print("\n-----\n");
// Connect to the server
Serial.print("connecting to URL ...");
// Start LAN connection
EthernetClient client;
if (client.connect(server, 80)) {
Serial.println("connected");
client.println("GET /lan.php?pid=8855");
client.println();
} else {
Serial.println("connection failed");
}
// Wait a moment for data to arrive
// Note: This is NOT a reliable way of doing this!
delay(1000);
if (client.available() > 0) {
String pickNum;
while (client.available()) {
char c = client.read(); // gets one byte from serial buffer
pickNum += c; // count
delay(2); // delay for buffer
}
Serial.print("page value (pick): ");
Serial.println(pickNum);
for (int x = 1; x <= pickNum.toInt(); x++) {
Serial.print("picking: #");
Serial.println(x);
digitalWrite(8, HIGH);
Serial.println("8 HIGH ...");
delay(1000); // Add switch
digitalWrite(8, LOW);
Serial.println("8 LOW ...");
delay(1000);
}
Serial.print("end");
}
// Disconnect the client
if (client.connected()) {
//Serial.println();
Serial.print("disconnecting");
client.stop();
}
// Wait another 9s, which will give us a delay of roughly 10s
delay(9000);
}
I have multiple variable length strings which do not exceed 32 bytes each which need to be sent via I2C between 2 Arduino Nanos. I use # to terminate each string. I can successfully send and receive the first string, but for the life of me I can't figure out how to send the succeeding strings. The samples I am testing with look like this:
String answer = "12345,123.4,50.6,12345#";
String answerV = "4.10,4.15,4.13,4.05,4.18,0#";
Master Code
#include <Wire.h>
int esc = 0;
int throttle = 80;
void setup() {
Wire.begin();
Serial.begin(115200);
Serial.println("I2C Master ready!");
esc = throttle * 100 / 180;
}
void loop() {
delay(500);
Serial.println("Receive data");
Wire.requestFrom(9,32);
String response = "";
Serial.println(Wire.available());
while (Wire.available())
{
char b = Wire.read();
response += b;
}
String dataString = response;
int hashIndex = dataString.indexOf('#');
String SportData = dataString.substring(0, hashIndex);
Serial.println(SportData);
String SDdata = String (esc) + "," + String(SportData);
Serial.println(SDdata);
}
Slave Code
#include <Wire.h>
byte ANSWERSIZE= 22;
String answer = "12345,123.4,50.6,12345#";
String answerV = "4.10,4.15,4.13,4.05,4.18,0#";
void setup() {
Wire.begin(9);
Wire.onRequest(requestEvent); // data request from Master
Serial.begin(115200);
Serial.println("I2C Slave ready!");
ANSWERSIZE = answer.length();
}
void requestEvent() {
byte response[ANSWERSIZE];
for (byte i=0;i<ANSWERSIZE;i++)
{
response[i] = (byte)answer.charAt(i);
}
Wire.write(response,sizeof(response));
}
void loop() {
delay(50);
}
Can someone please show me how this can be done?
A simple idea is to keep track of the number of times requestEvent() is called, and use that to decide what to send back to the master.
Here is the code (n.b. I took the liberty to optimise it a bit):
Master:
#include <Wire.h>
/**
* globals
*/
int esc = 0;
int throttle = 80;
char i2c_buffer[33];
/**
* setup & loop
*/
void setup()
{
Serial.begin(115200);
Wire.begin();
esc = throttle * 100 / 180;
i2c_buffer[32] = '\0';
Serial.println("I2C Master ready!");
}
void loop()
{
delay(500);
Wire.requestFrom(9, 32);
for (byte i = 0; i < 32 && Wire.available(); ++i)
{
i2c_buffer[i] = Wire.read();
if (i2c_buffer[i] == '#') {
i2c_buffer[i] = '\0';
break;
}
}
Serial.print(esc);
Serial.print(',');
Serial.println(i2c_buffer);
}
Slave:
#include <Wire.h>
/**
* globals
*/
const byte DATA_SIZE = 2;
String data[DATA_SIZE] = {
"12345,123.4,50.6,12345#",
"4.10,4.15,4.13,4.05,4.18,0#"
};
/**
* setup & loop
*/
void setup()
{
Serial.begin(115200);
Wire.begin(9);
Wire.onRequest(requestEvent); // data request from Master
Serial.println("I2C Slave ready!");
}
void loop()
{
delay(50);
}
/**
* help functions
*/
void requestEvent()
{
static byte req_number = 0;
Wire.write(reinterpret_cast<const unsigned char*>(data[req_number].c_str()),
data[req_number].length());
req_number = (req_number + 1) % DATA_SIZE;
}
Note: I don't have two Arduino devices, so I could not test this code. If you spot some bugs, report back and I'll fix them.
I am relatively new to arduino and its programming though I have been plodding along and getting results. I have added a shortened version of my sketch which connects to my webserver and obtains the current temp for a particular city. I will then use that temp to set my heater also controlled by my arduino. The problem I am facing is I cant get my head around how to return to the main loop after I've read the data on webserver from the connectandread function. The sketch stops/halts after receiving the data(temp). Eventually I will be calling the function every 2 hours. So all in all I need to call the function then after it runs return to the main loop. I hope I've explained clearly enough.
byte server[] = { 192,168,0,2 }; //ip Address of the server you will connect to
//The location to go to on the server
//make sure to keep HTTP/1.0 at the end, this is telling it what type of file it is
String location = "/index1.php HTTP/1.0";
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
EthernetClient client;
int convertedtemp;
char inString[32]; // string for incoming serial data
int stringPos = 0; // string index counter
boolean startRead = false; // is reading?
String responseString;
void setup() {
Ethernet.begin(mac);
delay( 1000 );
// Start the I2C interface
Wire.begin();
}
void ReadDS3231() {
int second,minute,hour,date,month,year,temperature;
second=Clock.getSecond();
minute=Clock.getMinute();
hour=Clock.getHour(h12, PM);
date=Clock.getDate();
month=Clock.getMonth(Century);
year=Clock.getYear();
lcd.setCursor(0, 1);
}
void loop() {
connectAndRead();
// Reading temperature or humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
int h0 = dht0.readHumidity();
int h1 = dht1.readHumidity();
int t0 = dht0.readTemperature();
int t1 = dht1.readTemperature();
ReadDS3231(); delay(3000);
}
String connectAndRead() {
//connect to the server
Serial.println("connecting...");
if (client.connect(server, 80)) {
Serial.println("connected");
client.print("GET ");
client.println(location);
client.println();
//Connected - Read the page
return readPage(); //go and read the output
} else {
return "connection failed";
}
}
String readPage() {
//read the page, and capture & return everything between '<' and '>'
stringPos = 0;
memset( &inString, 0, 32 ); //clear inString memory
while (true) {
if (client.available()) {
char c = client.read();
if (c == '<' ) { //'<' is our begining character
startRead = true; //Ready to start reading the part
} else if (startRead) {
if (c != '>') { //'>' is our ending character
inString[stringPos] = c;
stringPos ++;
} else {
//got what we need here! We can disconnect now
startRead = false;
//Serial.print(inString);
//convertedtemp = atoi(inString).c_str());
int val = atoi(inString);
//int convertedhumid = atoi(getValuesFromKey(responseString, "relative_humidity").c_str());
Serial.println(val);
client.stop();
client.flush();
Serial.println("disconnecting.");
//return inString;
//return ;
}
}
}
}
}
Well, at least the version of the sketch you have posted have an infinite loop in it... (while(true)).
Try un-commenting the line with return inString;.
Also unrelated, since you read data from external source, you need to stop reading after you have received 31 characters even if you have not received an ">". Otherwise, a bug or malicious code on the server can corrupt your memory.
I'm testing with my arduino and MQTT cloud.
For the publish everything goes well, the arduino publishes "hello world"
But with the void callback function nothing happens.
With my MQTT.fx client, I'm subscribed to the topics "status" and "commando".
At the "status" I see that the arduino is a live.
When I publish with my MQTT.fx client to the topic "commando".
I can see it arrived in my client, but not in the serial monitor of the arduino.
Why is the void callback function not used?
#include <SPI.h>
#include <PubSubClient.h>
#include <Ethernet.h>
#define server "m20.cloudmqtt.com"
int port = 13365;
// Update these with values suitable for your network.
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
byte ip[] = { 192, 168, 0, 120 };
unsigned long time;
char message_buff[100];
EthernetClient ethClient;
PubSubClient client(server, port, callback, ethClient);
void setup()
{
// init serial link for debugging
Serial.begin(115200);
Ethernet.begin(mac, ip);
if (client.connect("arduino-MQTT","test","test")) {
client.publish("/arduino/status/","hello world");
client.subscribe("/arduino/commando/");
Serial.println("Connected");
}
if (Ethernet.begin(mac) == 0)
{
Serial.println("Failed to configure Ethernet using DHCP");
return;
}
}
void loop()
{
// MQTT client loop processing
client.loop();
}
void callback(char* topic, byte* payload, unsigned int length) {
if (strcmp(topic, "/arduino/commando/") == 0) {
String msg = toString(payload, length);
Serial.println(msg);
}else{
Serial.println("arduino topic not found");
}
}
//
// toString function
//
String toString(byte* payload, unsigned int length) {
int i = 0;
char buff[length + 1];
for (i = 0; i < length; i++) {
buff[i] = payload[i];
}
buff[i] = '\0';
String msg = String(buff);
return msg;
}
I have just tested your code with RSMB broker and it works. I do not have DHCP on my computer so I had to comment out DHCP handling code - Ethernet.begin(mac). I think that's where your bug is. Because:
You assign static IP to your Ethernet
Connect to mqtt broker, and subscribe to a topic
Query DHCP for a new IP. Probably at this point your Arduino gets a different IP than statically configured and the broker cannot reach your Arduino any more to publish subscribed topic.
Fix your Ethernet handling code. I like this formula:
// Start the Ethernet connection:
Serial.println(F("Querying DHCP"));
if ( Ethernet.begin( mac ) == 0 ) {
Serial.println(F("DHCP failed, fallback to static IP"));
// When DHCP fails, fallback to static configuration ;
Ethernet.begin( mac, ip ) ;
}
printIp() ;
And printIp function:
void printIp() {
// Print local IP address
Serial.print(F("My IP "));
for (byte thisByte = 0; thisByte < 4; thisByte++) {
// print the value of each byte of the IP address:
Serial.print(Ethernet.localIP()[thisByte], DEC);
Serial.print('.');
}
}
I want get json from my website. I am trying send POST request for authentication and after it i want to send GET request for getting json, but i can't do it, because it gives me en error 401 unauthorized (cookies doesn't saved) . How can i keep session with Arduino ,ethernet shield and WebClien?
Source:
#include <SPI.h>
#include <Ethernet.h>
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
//Change to your server domain
char serverName[] = "bh.quickle.me";
// change to your server's port
int serverPort = 80;
// change to the page on that server
char pageName[] = "/users/sign_in";
char sensorPage[] = "/api/v1/sensors";
EthernetClient client;
int totalCount = 0;
// insure params is big enough to hold your variables
char params[68];
// set this to the number of milliseconds delay
// this is 30 seconds
#define delayMillis 30000UL
unsigned long thisMillis = 0;
unsigned long lastMillis = 0;
void setup() {
Serial.begin(9600);
// disable SD SPI
pinMode(4,OUTPUT);
digitalWrite(4,HIGH);
pinMode(7,OUTPUT);
digitalWrite(7,HIGH);
Serial.print(F("Starting ethernet..."));
if(!Ethernet.begin(mac)) Serial.println(F("failed"));
else Serial.println(Ethernet.localIP());
delay(2000);
Serial.println(F("Ready"));
sprintf(params,"{\"user\": {\"email\": \"EgorkZe#gmail.com\",\"password\": \"1234asdf\"}}");
postPage(serverName,serverPort,pageName,params);
}
void loop()
{
thisMillis = millis();
if(thisMillis - lastMillis > delayMillis)
{
lastMillis = thisMillis;
getPage(serverName, serverPort, sensorPage);
}
}
byte postPage(char* domainBuffer,int thisPort,char* page,char* thisData)
{
int inChar;
char outBuf[64];
Serial.print(F("connecting..."));
if(client.connect(domainBuffer,thisPort))
{
Serial.println(F("connected"));
// send the header
sprintf(outBuf,"POST %s HTTP/1.1",page);
client.println(outBuf);
sprintf(outBuf,"Host: %s",domainBuffer);
client.println(outBuf);
client.println(F("Connection: close\r\nContent-Type: application/json"));
sprintf(outBuf,"Content-Length: %u\r\n",strlen(thisData));
client.println(outBuf);
// send the body (variables)
client.print(thisData);
}
else
{
Serial.println(F("failed"));
return 0;
}
int connectLoop = 0;
while(client.connected())
{
while(client.available())
{
inChar = client.read();
Serial.write(inChar);
connectLoop = 0;
}
delay(1);
connectLoop++;
if(connectLoop > 10000)
{
Serial.println();
Serial.println(F("Timeout"));
client.stop();
}
}
Serial.println();
Serial.println(F("disconnecting."));
client.stop();
return 1;
}
byte getPage(char* domainBuffer, int thisPort, char* page)
{
int inChar;
char outBuf[128];
Serial.print(F("connecting..."));
if(client.connect(domainBuffer,thisPort))
{
Serial.println(F("connected"));
sprintf(outBuf,"GET %s HTTP/1.1",page);
client.println(outBuf);
sprintf(outBuf,"Host: %s",serverName);
client.println(outBuf);
client.println(F("Connection: close\r\n"));
}
else
{
Serial.println(F("failed"));
return 0;
}
// connectLoop controls the hardware fail timeout
int connectLoop = 0;
while(client.connected())
{
while(client.available())
{
inChar = client.read();
Serial.write(inChar);
// set connectLoop to zero if a packet arrives
connectLoop = 0;
}
connectLoop++;
// if more than 10000 milliseconds since the last packet
if(connectLoop > 10000)
{
// then close the connection from this end.
Serial.println();
Serial.println(F("Timeout"));
client.stop();
}
// this is a delay for the connectLoop timing
delay(1);
}
Serial.println();
Serial.println(F("disconnecting."));
// close client end
client.stop();
return 1;
}
To begin, yes, the Arduino Ethernet is capable to store and send back cookies.
You have to catch the cookies from the authentication request and send them back in the header of the second HTTP request (the GET one).
Have a look at informations about RFC HTTP Protocol
You can also look at :
http://en.wikipedia.org/wiki/HTTP_cookie#Setting_a_cookie