Hi guys, I'm having a problem for my project about Arduino and GSM SIM900A
My project is to monitoring the electricity usage for house appliances through SMS. The pulse is get from the energy meter through interrupt method and calculate with arduino. Supposedly I should get the results from the serial monitor when i send the command "check" to check the usage and cost, but it seems that the GSM can't receive the signal because of loop disturbing the signal. Can anyone know how to solve the problem?
#include <LiquidCrystal.h>
#include "EmonLib.h"
#include <SoftwareSerial.h>
SoftwareSerial SIM900A(7, 8);
LiquidCrystal lcd(12, 11, 5, 4, 6, 2);
EnergyMonitor emon1;
int pulse;
long pulseCount = 0; //Number of pulses, used to measure energy.
unsigned long pulseTime,lastTime; //Used to measure power.
double power, elapsedkWh; //power and energy
char inchar; //Will hold the incoming character from the Serial Port.
int led = 8;
int relay = 7;
char incoming_char=0;
char temp[60];
int m=0;
float TBill = 0.0;
float realPower= 0.0;
float IBill = 0.00;
int ppwh = 1; //Number of pulses per wh - found or set on the meter.
//1000 pulses/kwh = 1 pulse per wh
void setup()
{
SIM900A.begin(19200);
Serial.begin(19200); // Setting the baud rate of Serial Monitor (Arduino)
delay(10000); // give time to log on to network.
SIM900A.print("AT+CMGF=1\r"); // set SMS mode to text
delay(100);
SIM900A.print("AT+CNMI=2,2,0,0,0\r");
// blurt out contents of new SMS upon receipt to the GSM shield's serial out
delay(100);
attachInterrupt(1, onPulse, FALLING); // KWH interrupt attached to IRQ 1 = pin3
emon1.current(1, 20);
pinMode(pulse, INPUT);
}
void loop()
{
m=0;
for (int m=0;m<60;m++)
{
if(SIM900A.available() >0)
{
incoming_char=SIM900A.read(); //Get the character from the cellular serial port.
Serial.print(incoming_char); //Print the incoming character to the terminal.
if(inchar=='0')
{
delay(10);
inchar=SIM900A.read();
if(inchar=='1')
{
delay(10);
inchar=SIM900A.read();
if(inchar=='2')
{
for (int m=0;m<60;m++)
{
temp[m]=0;
}
}
}
}
}
temp[m]=incoming_char;
}
if(temp[50]=='C' && temp[51]=='h' && temp[52]=='e'&& temp[53]=='c'&& temp[54]=='k')
{
SIM900A.println("AT+CMGF=1"); //Sets the GSM Module in Text Mode
delay(1000); // Delay of 1000 milli seconds or 1 second
SIM900A.println("AT+CMGS=\"+60124920726\"\r"); // Mobile number of user
delay(1000);
SIM900A.println("Unit: "); // Power consumption by user
SIM900A.println(elapsedkWh);
SIM900A.println("RM: "); // Total cost by user
SIM900A.println();
SIM900A.println((char)26); // ASCII code of CTRL+Z
delay(1000);
}
float IBill = 0.00;
float Irms = emon1.calcIrms(1480);
float realPower = (Irms*240*4.86*0.85);
float Bill = (realPower * 21.80 * 1/3600 * 1/1000);
float TBill = Bill + IBill;
if (Irms <= 0.0500)
{
realPower = 0.00;
Bill = 0.00;
TBill = Bill + IBill ;
}
else (Irms > 0.0500);
{ realPower = realPower;
Bill = Bill;
TBill = Bill + IBill;
}
lcd.setCursor(0, 0);
lcd.print("Power(W):");
lcd.print(realPower);
lcd.setCursor(0, 1);
lcd.print("Price(C):");
lcd.print(TBill);
Serial.print(realPower);
Serial.print(" ");
Serial.println(TBill);
delay(500);
/*
Output Results - The result of its calculations are to
be output to the screen (via Serial port).
*/
Serial.print(" | Power (W): ");
Serial.print(power, 2);
Serial.print(" | Energy (kWh): ");
Serial.print(elapsedkWh, 3);
Serial.print(" | Pulse Count: ");
Serial.print(pulseCount);
Serial.print(" | Millis: ");
Serial.print(pulseTime);
Serial.println(" | ");
delay(3000);
}
// The interrupt routine
void onPulse()
{
//used to measure time between pulses.
lastTime = pulseTime;
pulseTime = micros();
//pulseCounter
pulseCount++;
//Calculate power
power = (3600000000.0 / (pulseTime - lastTime))/ppwh;
//Find kwh elapsed
elapsedkWh = (1.0*pulseCount/(ppwh*1000)); //multiply by 1000 to convert pulses per wh to kwh
}
declare the variables you use in the interrupt as [url=https://www.arduino.cc/en/Reference/Volatile]volatile[/url]
and don't do the maths into the interrupt function. do that in the main loop. The interrupt code needs to be as short as possible!
in your loop use time management techniques to only display every seconds, or 5 seconds, or when necessary to your LCD
Also this probably does not do what you want.
if (Irms <= 0.0500)
{
realPower = 0.00;
Bill = 0.00;
TBill = Bill + IBill ;
}
[color=red]else (Irms > 0.0500);[/color]
[color=blue] { realPower = realPower;
Bill = Bill;
TBill = Bill + IBill;
}[/color]
when you listen for data in
if (SIM900A.available() > 0)
{
incoming_char = SIM900A.read(); //Get the character from the cellular serial port.
Serial.print(incoming_char); //Print the incoming character to the terminal.
if (inchar == '0')
{
delay(10);
inchar = SIM900A.read();
if (inchar == '1')
{
delay(10);
inchar = SIM900A.read();
if (inchar == '2')
{
for (int m = 0; m < 60; m++)
{
temp[m] = 0;
}
}
}
}
}
don't use any Serial.print(incoming_char); nor delay(). Manage communication as it comes.
Thanks for the reply! Do you have any efficient solution to my problem? I can't think any better ways for managing the communication system. I need to send the command to the arduino to get the results and get the feedback from serial monitor.
remove Serial.print(incoming_char); when you listen for data from the SIM900A (if you need to print, print later when you've got the full command)
fix your if (Irms <= 0.0500) {} else {} thingy
use time management techniques (and study blink without delay) to display on the LCD only every 5 seconds / when needed only
you might want to boost your Serial.begin to 115200 to ensure you don't create unwanted delays and buffer overflows with your computer and debug traces
I didn't know how to use the management technique, try to understand the code but eventually make me more confuse, seem like the input of interrupt disturbing the signal receiving from the GSM, I try stop the loop but it will make the system inefficient.
#include <LiquidCrystal.h>
#include "EmonLib.h"
#include <SoftwareSerial.h>
SoftwareSerial SIM900A(7, 8);
LiquidCrystal lcd(12, 11, 5, 4, 6, 2);
EnergyMonitor emon1;
int pulse;
long pulseCount = 0; //Number of pulses, used to measure energy.
unsigned long pulseTime,lastTime; //Used to measure power.
double power, elapsedkWh; //power and energy
char inchar; //Will hold the incoming character from the Serial Port.
int led = 8;
int relay = 7;
char incoming_char=0;
char temp[60];
volatile int m=0;
volatile float TBill = 0.0;
volatile float realPower= 0.0;
volatile float IBill = 0.00;
volatile int ppwh = 1; //Number of pulses per wh - found or set on the meter.
//1000 pulses/kwh = 1 pulse per wh
void setup()
{
SIM900A.begin(115200);
Serial.begin(115200); // Setting the baud rate of Serial Monitor (Arduino)
delay(10000); // give time to log on to network.
SIM900A.print("AT+CMGF=1\r"); // set SMS mode to text
delay(100);
SIM900A.print("AT+CNMI=2,2,0,0,0\r");
// blurt out contents of new SMS upon receipt to the GSM shield's serial out
delay(100);
attachInterrupt(1, onPulse, FALLING); // KWH interrupt attached to IRQ 1 = pin3
emon1.current(1, 20);
pinMode(pulse, INPUT);
lcd.begin(16,2);
lcd.setCursor(0,0);
lcd.print("Energy Meter ");
lcd.setCursor(0,1);
lcd.print("Interfacing with");
delay(2000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print(" Arduino By ");
lcd.setCursor(0,1);
lcd.print(" Ayden Pin ");
delay(2000);
}
void loop()
{
m=0;
for (int m=0;m<60;m++)
{
if(SIM900A.available() >0)
{
incoming_char=SIM900A.read(); //Get the character from the cellular serial port.
if(inchar=='0')
{
delay(10);
inchar=SIM900A.read();
if(inchar=='1')
{
delay(10);
inchar=SIM900A.read();
if(inchar=='2')
{
for (int m=0;m<60;m++)
{
temp[m]=0;
}
}
}
}
}
temp[m]=incoming_char;
}
if(temp[50]=='C' && temp[51]=='h' && temp[52]=='e'&& temp[53]=='c'&& temp[54]=='k')
{
SIM900A.println("AT+CMGF=1"); //Sets the GSM Module in Text Mode
delay(1000); // Delay of 1000 milli seconds or 1 second
SIM900A.println("AT+CMGS=\"+60124920726\"\r"); // Mobile number of user
delay(1000);
SIM900A.println("Unit: "); // Power consumption by user
SIM900A.println(elapsedkWh);
SIM900A.println("RM: "); // Total cost by user
SIM900A.println();
SIM900A.println((char)26); // ASCII code of CTRL+Z
delay(1000);
}
float IBill = 0.00;
float Irms = emon1.calcIrms(1480);
float realPower = (Irms*240*4.86*0.85);
float Bill = (realPower * 21.80 * 1/3600 * 1/1000);
float TBill = Bill + IBill;
if (Irms <= 0.0500)
{
realPower = 0.00;
Bill = 0.00;
TBill = Bill + IBill ;
}
lcd.setCursor(0, 0);
lcd.print("Power(W):");
lcd.print(realPower);
lcd.setCursor(0, 1);
lcd.print("Price(C):");
lcd.print(TBill);
Serial.print(realPower);
Serial.print(" ");
Serial.println(TBill);
delay(500);
/*
Output Results - The result of its calculations are to
be output to the screen (via Serial port).
*/
Serial.print(" | Power (W): ");
Serial.print(power, 2);
Serial.print(" | Energy (kWh): ");
Serial.print(elapsedkWh, 3);
Serial.print(" | Pulse Count: ");
Serial.print(pulseCount);
Serial.print(" | Millis: ");
Serial.print(pulseTime);
Serial.println(" | ");
delay(3000);
}
// The interrupt routine
void onPulse()
{
//used to measure time between pulses.
lastTime = pulseTime;
pulseTime = micros();
//pulseCounter
pulseCount++;
//Calculate power
power = (3600000000.0 / (pulseTime - lastTime))/ppwh;
//Find kwh elapsed
elapsedkWh = (1.0*pulseCount/(ppwh*1000)); //multiply by 1000 to convert pulses per wh to kwh
}
Yes, you have to stop the loop. I'm talking about the for(int m... loop. It can't continue counting m when there's no incoming char. Remember serial is very very slow for the Arduino. You can do thousands of different things in between each character arriving, even if serial is sending with no gaps. You've already found that you need to delay(10) between some of the characters. There's probably other useful work you could be doing instead of plugging your ears with a delay.
Change it to a while() loop. Only increment m when it gets a character. Add some timeouts so that it doesn't wait forever. Add some synchronisation so that if it somehow misses the first character of the sequence, it can discard those chars and go back to waiting for the first char.
Where you are looking for the sequence "012" in the incoming data (to inexplicably erase all data) use a state machine rather than just a linear set of reads and delays.