Help making sketch more efficient

I have this sketch that uses 3 main features:

  1. RTC module to set time & TimeAlarms library to fire alarms based on the time.
  2. Tinysine WiFi shield sitting atop the UNO.
  3. Grove Base Shield v2 which only has a CO2 sensor connected to it via jumpers (because the shield doesnt fit over the TinySine WiFi shield)

The sketch currently sets up:

  1. The Serial just for monitoring purposes (debugging so far)
  2. A SoftwareSerial on 2,3 for the WiFi shield.
  3. A SoftwareSerial on 7,8 for the Grove Shield connection.
  4. Takes CO2 measurement everytime an alarm fires (about 1 every hour or every 2 hours).
  5. Posts the results to the web.

I believe it is inefficient because so far, in setup I:

  1. Begin the Serial and RTC module.
  2. Set the alarms.

Then the loop() method is empty pretty much.

Finally when an alarm configured in the setup() fires, I call:

  1. printCO2() which prints results from a dataReceive() call which takes the CO2 measurement.
  2. wifiStuff() which initializes the wifly module, logs onto network. There is a wishy-washy part that connects the wifly serial to the serial.
  3. reportToCloud() which opens a website page, php form and posts to it.

I feel uneasy about it because Im unsure as to the adequate chronology of events.

Originally I called the wifiStuff() in the setup and only called the measurement printCO2() sampling code method and the reportToCloud() method right after the sampling code.

Now Im calling the wifiStuff() and printCO2() and reportToCloud() only when the alarm fires.

I was thinking this was more efficient because I didnt have the wifi module sitting there for a long time until it was time to sample and then post. So now its only activated when its time to sample. But I sample about 12 times a day so this means the first time it boots up, the wifli module is inactive but after I sample the first time, the wifi module sits there until the next sample.

Should I close the connection or something?

I believe it is inefficient because

you didn't post it, and now we have to ask you to post it, and wait for that to happen...

ARGH! Sorry!

Btw, last night it logged data fine. Today I switched over from being connected to my laptop, to being powered by a wall adapter, and today's first post has a value of 0. I believe it has something to do with that terminal method which is said to connect the wifly to the serial port to write the data but now the serial port is disconnected because I removed the laptop.

Here:

#include <Wire.h>
#include "RTClib.h"
#include <Time.h>
#include <TimeAlarms.h>
#include <WiFlyHQ.h>
#include <SoftwareSerial.h>

RTC_DS1307 rtc;

////////////////////////SETUP GROVE & CO2 ////////////////////////////////////
int pin = 8;
unsigned long duration;
unsigned long starttime;
unsigned long sampletime_ms = 30000;//sampe 30s ;
unsigned long lowpulseoccupancy = 0;
float ratio = 0;
float concentration = 0;

long previousMillis = 0;
long interval = 1000;

SoftwareSerial wifiSerial(2,3); //WIFI SHIELD CLIENT

#define DEBUG 0                       //CO2 Sensor
const int pinRx = 8;                  //CO2 Sensor
const int pinTx = 7;                  //CO2 Sensor
SoftwareSerial sensor(pinTx,pinRx);  //CO2 Sensor

const unsigned char cmd_get_sensor[] = 
{
    0xff, 0x01, 0x86, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x79
};
unsigned char dataRevice[9];          //CO2 Sensor
int temperature;                      //CO2 Sensor
int CO2PPM;                           //CO2 Sensor

WiFly wifly;                          //WIFI
String data;                          //WIFI

const char mySSID[] = "dlinkit";
const char myPassword[] = "mykey";
const char site[] = "myserver.com";

void terminal();

void setup(){
  Serial.begin(115200);  
  Wire.begin();
  rtc.begin();
  if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running!");
    //rtc.adjust(DateTime(__DATE__, __TIME__));
  }
  //THIS IS WHERE WIFISTUFF USED TO BE
  DateTime now = rtc.now();
  
  setTime(now.hour(), now.minute(), now.second(), now.month(), now.day(), now.year());
  Serial.print("Time is: ");
  Serial.println(now.hour());
  Alarm.alarmRepeat(5,30,0, MorningAlarm);  // 1:00am every day 
  Alarm.alarmRepeat(6,30,0, MorningAlarm);  // 1:00am every day 
  Alarm.alarmRepeat(7,30,0, MorningAlarm);  // 1:00am every day 
  Alarm.alarmRepeat(8,30,0, MorningAlarm);  // 1:00am every day 
  Alarm.alarmRepeat(9,30,0, MorningAlarm);  // 1:00am every day 
  Alarm.alarmRepeat(10,30,0, MorningAlarm);  // 1:00am every day 
  Alarm.alarmRepeat(11,30,0, MorningAlarm);  // 1:00am every day 
  Alarm.alarmRepeat(12,30,0, MorningAlarm);  // 1:00am every day 
  Alarm.alarmRepeat(13,30,0, MorningAlarm);  // 1:00am every day 
  Alarm.alarmRepeat(14,30,0, MorningAlarm);  // 1:00am every day 
  Alarm.alarmRepeat(15,30,0, MorningAlarm);  // 1:00am every day 
  Alarm.alarmRepeat(16,30,0, MorningAlarm);  // 1:00am every day 
  Serial.print("Alarms set!");
  
  sensor.begin(9600);
  Serial.begin(9600);
  Serial.println("get a 'g', begin to read from sensor!");
  Serial.println("********************************************************");
  Serial.println();
}

void  loop(){  
  Alarm.delay(10);
}

void wifiStuff(){
   char buf[32];
   data = "";
   Serial.print("Free memory: ");
   Serial.println(wifly.getFreeMemory(),DEC);

   wifiSerial.begin(9600);
   if (!wifly.begin(&wifiSerial, &Serial)) {
       Serial.println("Failed to start wifly");
	terminal();
   }
   if (!wifly.isAssociated()) {
	Serial.println("Joining network");
	wifly.setSSID(mySSID);
        wifly.setPassphrase(myPassword);
	wifly.enableDHCP();

	if (wifly.join()) {
	    Serial.println("Joined wifi network");
	} else {
	    Serial.println("Failed to join wifi network");
	    terminal();
	}
   } else {
       Serial.println("Already joined network");
   }
   
   wifly.setDeviceID("Wifly-WebClient");
   Serial.print("DeviceID: ");
   Serial.println(wifly.getDeviceID(buf, sizeof(buf)));
   
   if (wifly.isConnected()) {
       Serial.println("Old connection active. Closing");
	wifly.close();
   }
}

void MorningAlarm(){
  Serial.println("Morning Alarm...");
  printCO2();
  wifiStuff();
  reportToCloud();
}

void reportToCloud() {
   if (wifly.available() > 0) {
	char ch = wifly.read();
	Serial.write(ch);
	if (ch == '\n') {
	    /* add a carriage return */ 
	    Serial.write('\r');
	}
   }
   
   if (wifly.open(site, 80)) {
       Serial.print("Connected to ");
	Serial.println(site);
	
    static char outstr[15];

    String dataString = dtostrf(CO2PPM, 8, 2, outstr);
    data = String("name=Station1&age=" + dataString);
    Serial.print(data);
    
	wifly.println("POST /arduino/postdata.php HTTP/1.0");
	wifly.println("Host: www.myserver.com"); // SERVER ADDRESS HERE TOO
        wifly.println("Content-Type: application/x-www-form-urlencoded" );
        wifly.print("Content-Length: ");
        wifly.println(data.length());
	wifly.println();
        wifly.print(data);

   } else {
       Serial.println("Failed to connect");
   }

   if (Serial.available() > 0) {
	wifly.write(Serial.read());
   } 
}

/* Connect the WiFly serial to the serial monitor. Not sure why*/
void terminal(){
   while (1) {
	if (wifly.available() > 0) {
	    Serial.write(wifly.read());
	}
	if (Serial.available() > 0) {
	    wifly.write(Serial.read());
	}
   }
}

void printCO2() {
  Serial.println("printing co2...");
  if(dataRecieve())
  {
    Serial.print("Temperature: ");
    Serial.print(temperature);
    Serial.print("  CO2: ");
    Serial.print(CO2PPM);
    Serial.println("");
  }
  delay(1000);
}

bool dataRecieve(void) {
  byte data[9];
  int i = 0;
  
  //transmit command data
  for(i=0; i<sizeof(cmd_get_sensor); i++)
  {
    sensor.write(cmd_get_sensor[i]);
  }
  delay(10);
  //begin reveiceing data
  if(sensor.available())
  {
    while(sensor.available())
    {
      for(int i=0;i<9; i++)
      {
        data[i] = sensor.read();
      }
    }
  }
  
  #if DEBUG
  for(int j=0; j<9; j++)
  {
    Serial.print(data[j]);
    Serial.print(" ");    
  }
  Serial.println("");
  #endif
  
  if((i != 9) || (1 + (0xFF ^ (byte)(data[1] + data[2] + data[3] 
    + data[4] + data[5] + data[6] + data[7]))) != data[8])
  {
    return false;
  }
  CO2PPM = (int)data[2] * 256 + (int)data[3];
  temperature = (int)data[4] - 40;  
  Serial.println(temperature);
  return true;
}

Your RAM usage needs reviewing and so do your comments.

OK

  1. I guess you mean the comments on the alarms. That was because I replicated them quick and dirty. But I agree. I was actually thinking of using a repeat alarm instead of 12 individual alarms.

  2. I'd like to understand that terminal() method in order to get my data back to working how it was when it was connected to my laptop.

What do you mean "efficient"? If you only need to take a measurement every hour, you can't do that "faster" can you? Hours are still hours.

The terminal method doesn't appear to exit. while(1){} will never exit unless there's a break statement and that loop doesn't appear to have any. It looks like calling that function will effectively stop your program right there. Interrupts will still run and your alarm library may be using those but I didn't look.

By efficient I mostly meant the thing about starting up the wifly from setup() vs from the sampling method. My reasoning being that obviously you shouldn't activate a resource until you need it. In that sense I shouldn't activate the wifly until I sample because that's when I need it to post. The problem is that that wifly is activated after the first measurement. So it would already active after that first measurement unless I somehow shut down the wifly again until I need it.

But should I shut it down? I don't need to in terms of power because it's plugged into a wall socket. But is it perhaps better to leave the wifly module connected from setup() because turning it on and off might raise the risk of something going wrong. Do you think it's worth it?

As for the terminal() method, The main program doesn't stop at any time. Yesterday I logged about 10 pieces of data while the project was connected to my laptop but ever since I unplugged the project from my laptop and left it powered by the wall adapter, all measurement readings posted to the server are 0.

How would that affect the measurements? Could the missing serial port be involved?

Looking at it again, terminal() is the end of the program. If it fails to join the WiFi network then it goes to terminal() and never tries again. This is probably not what you want for an unattended device. But since it's running and posting zeroes to the server, let's leave that alone for now.

Is there any way the WiFi can become disconnected and require a restart? Then it's probably a good idea to do that every hour you want to send data. However WiFiStuff looks like it will find you already have a connection and then it will close that connection. You are probably only getting a reading every 2 hours.

Two SoftwareSerial on the same device is asking for trouble. You know it can only receive from one at a time?

dataReceive() for the sensor is also going to give trouble. You only have enough delay() in there for one character to arrive but you're expecting to get ten. Since you're only logging once per hour, you can afford to put in a bigger delay here.

What kind of Arduino are you using? A Micro does have a slightly different dependency on the USB serial port, but it does have a free additional hardware serial.

Yes I read only one could receive at a time. I was thinking of adding a delay after sampling to make room for the WiFi post.

I'll work on the dataReceive().

Right now I need to figure out why I'm getting zeroes all of a sudden.

Ok 3 points:

  1. I understand the terminal() not trying again. I guess it makes sense that this was in the setup() because that way you were sure that it joined the network as it booted up. But if Im going to be trying to connect each time the alarm fires, it does make it more brittle code.

How would code so that it tries again? I was thinking set a flag if connection fails. Check that flag until it clears by trying again? Something like:

//INSIDE WIFISTUFF
wifiSerial.begin(9600);
   if (!wifly.begin(&wifiSerial, &Serial)) {
       Serial.println("Failed to start wifly");
	terminal();
        failedToJoin = true;
        waitAndTryAgain();
   }

//...NEW METHOD
void waitAndTryAgain(){
   if (failedToJoin) {
     MorningAlarm();
   }
}
//CLEAR FLAG
/* Join wifi network if not already associated */
   if (!wifly.isAssociated()) {
	/* Setup the WiFly to connect to a wifi network */
	Serial.println("Joining network");
	wifly.setSSID(mySSID);
        wifly.setPassphrase(myPassword);
	wifly.enableDHCP();

	if (wifly.join()) {
	    Serial.println("Joined wifi network");
            //CLEAR FLAG
            //failedToJoin = false;
	} else {
	    Serial.println("Failed to join wifi network");
	    terminal();
	}
   } else {
       Serial.println("Already joined network");
   }

Actually I modified the alarm to fire every 2 minutes and that way I get many failed connect attempts but much more successful attempts. That could be another way to work around the failed attempts. Have samples made every 15 minutes instead of every 60 minutes.

  1. Im working on the dataReceive() delay. Whats the calculation there?

9600bps, 1 character is 8 bits? So thats 10 characters would be 80 bits or 9600/80 = 120s? I guess I missed something.

  1. I've reconnected my project to my laptop and its working fine, logging data and posting:

Alarms set!get a 'g', begin to read from sensor!


Morning Alarm...
printing co2...
35
Temperature: 35 CO2: 351
Free memory: 376
setPrompt failed
Joining network
Joined wifi network
DeviceID: Wifly-WebClient
open myserver.com 80
Connected to myserver.com
name=Station1&age= 351.00

So it seems the Serial being available does have something to do with my values going to zero. So I did a search for Serial.available and noticed its available in 2 places:

reportToCloud()
and
terminal()

Im trying to figure out why !Serial.available would make the value 0?

Ok I modified the sketch to this (which basically calls the sensor.begin() on each sample) because I was getting the exact same values for every subsequent sample. This means it was just taking the original sample in the chain and posting that exact same value each time the alarm fired. I noticed this because dataReceive() was actually being called only on the first sample of a chain.

Im down to 2 errors:

  1. Wifly crashes and logs it will reboot, but never does.

  2. Try running the project on wall adapter again to make sure it only produces zeroes when not connected to the serial.

9600 baud is not 9600 bits per second. Each byte is transmitted with a start bit and a stop bit, so you need to divide by 10 to get an estimate of the bytes per second.

Normally you would not want to wait so long as most Arduinos are doing active things like driving a robot around the room. Waiting half a second for a packet of serial data will probably cause the robot to crash because it didn't stop the motors in time. Therefore most Arduino programs will use other techniques to determine when the serial data has arrived. But in your case, logging every 2 minutes or 60 minutes, a few extra half-seconds of delay is not bad.

9600 baud is not 9600 bits per second

whisky tango foxtrot!

MorganS:
9600 baud is not 9600 bits per second. Each byte is transmitted with a start bit and a stop bit, so you need to divide by 10 to get an estimate of the bytes per second.

And the ACTUAL data throughput will be slower still, because of interrupt latency. There will be random-length gaps between successive characters, especially if interrupts are being used.

Regards,
Ray L.

OK the project works fine with the wall adapter as expected. I'm not sure why it was sending zeroes at first.

My only problem now is the wifly module crashing. Every now and then, perhaps after 5 or as much as 10 successful samples & postings, I get:

"Wifly module crashed and will reboot"

But it never reboots. I've managed to get it back to work by tapping the reset button on the wifly shield.

I'm using the WiflyHQ library which has a reboot method which does this:

send_P(PSTR("reboot\r"));

Is this the same as hitting the reset button?

If so, I could simply call wifly.reboot() if and after my wifly crashes, right? Has anyone used this?