Advertisement
Guest User

Arduino ESP8266 China power meter

a guest
Mar 16th, 2015
818
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 9.55 KB | None | 0 0
  1. #include<stdlib.h>
  2. #include <SoftwareSerial.h>
  3.  
  4. /*
  5.  Reads voltage and power from China-style energy meter.
  6.  Collecting data by eavesdropping on the MOSI-line (master in slave out)
  7.  between the energy monitoring chip (ECH1560) and the main processor*/
  8.  
  9. const int CLKPin = 2; // Pin connected to CLK (D2 & INT0)
  10. const int MISOPin = 5;  // Pin connected to MISO (D5)
  11.  
  12. //All variables that is changed in the interrupt function must be volatile to make sure changes are saved.
  13. volatile int Ba = 0;   //Store MISO-byte 1
  14. volatile int Bb = 0;   //Store MISO-byte 2
  15. volatile int Bc = 0;   //Store MISO-byte 2
  16. float U = 0;    //voltage
  17. float P = 0;    //power
  18. float ReadData[3] = {0, 0, 0}; //Array to hold mean values of [U,I,P]
  19.  
  20. volatile long CountBits = 0;      //Count read bits
  21. volatile int Antal = 0;           //number of read values (to calculate mean)
  22. volatile long ClkHighCount = 0;   //Number of CLK-highs (find start of a Byte)
  23. volatile boolean inSync = false;  //as long as we ar in SPI-sync
  24. volatile boolean NextBit = true;  //A new bit is detected
  25.  
  26. #define SSID "SSID_HERE" //Put SSID here, leave quotes
  27. #define PASS "PASSWORD" //Put password here, leave quotes
  28. #define IP "192.168.4.4" // Domoticz address
  29. #define PORT "8084" //Port where Domoticz is running, not working yet.
  30.  
  31. String GET = "GET /json.htm?type=command&param=udevice&idx="; //Domoticz JSON URL
  32. String IDX = "52"; //IDX value of virtual sensor in Domoticz
  33. String GET2 = "&nvalue=0&svalue="; //Second part of Domoticz JSON URL
  34. String GET3 = ";0"; //ENERGY part (last part of Domoticz JSON URL)
  35.  
  36. SoftwareSerial monitor(10, 11); // Use these pins to monitor the Arduino from the PC. Hardware pins are used for ESP8266
  37.  
  38. String AmountOfWatts;
  39.  
  40. void setup() {
  41.   //Setting up interrupt ISR on D2 (INT0), trigger function "CLK_ISR()" when INT0 (CLK)is rising
  42.   attachInterrupt(0, CLK_ISR, RISING);
  43.  
  44.   //Set the CLK-pin (D5) to input
  45.   pinMode(CLKPin, INPUT);
  46.  
  47.   //Set the MISO-pin (D5) to input
  48.   pinMode(MISOPin, INPUT);
  49.  
  50.   // initialize serial communications at 9600 bps: (to computer)
  51.   monitor.begin(9600);
  52.   Serial.begin(9600);
  53.   sendDebug("AT");
  54.   delay(5000);
  55.   if(Serial.find("OK")){
  56.     monitor.println("RECEIVED: OK");
  57.     //connectWiFi();
  58.   }
  59. }
  60.  
  61. void loop() {
  62.  
  63.   //do nothing until the CLK-interrupt occures and sets inSync=true
  64.   if(inSync == true){
  65.     CountBits = 0;  //CLK-interrupt increments CountBits when new bit is received
  66.     while(CountBits<40){}  //skip the uninteresting 5 first bytes
  67.     CountBits=0;
  68.     Ba=0;
  69.     Bb=0;
  70.     while(CountBits<24){  //Loop through the next 3 Bytes (6-8) and save byte 6 and 7 in Ba and Bb
  71.       if(NextBit == true){ //when rising edge on CLK is detected, NextBit = true in in interrupt.
  72.         if(CountBits < 9){  //first Byte/8 bits in Ba
  73.           Ba = (Ba << 1);  //Shift Ba one bit to left and store MISO-value (0 or 1) (see http://arduino.cc/en/Reference/Bitshift)
  74.           //read MISO-pin, if high: make Ba[0] = 1
  75.           if(digitalRead(MISOPin)==HIGH){
  76.             Ba |= (1<<0);  //changes first bit of Ba to "1"
  77.           }   //doesn't need "else" because BaBb[0] is zero if not changed.
  78.           NextBit=false; //reset NextBit in wait for next CLK-interrupt
  79.         }
  80.         else if(CountBits < 17){  //bit 9-16 is byte 7, stor in Bb
  81.           Bb = Bb << 1;  //Shift Ba one bit to left and store MISO-value (0 or 1)
  82.           //read MISO-pin, if high: make Ba[0] = 1
  83.           if(digitalRead(MISOPin)==HIGH){
  84.             Bb |= (1<<0);  //changes first bit of Bb to "1"
  85.           }
  86.           NextBit=false;  //reset NextBit in wait for next CLK-interrupt
  87.         }
  88.       }
  89.     }
  90.     if(Bb!=3){ //if bit Bb is not 3, we have reached the important part, U is allready in Ba and Bb and next 8 Bytes will give us the Power.
  91.       Antal += 1;  //increment for mean value calculations
  92.      
  93.      //Voltage = 2*(Ba+Bb/255)
  94.       U=2.0*((float)Ba+(float)Bb/255.0);
  95.      
  96.       //Power:
  97.       CountBits=0;
  98.       while(CountBits<40){}//Start reading the next 8 Bytes by skipping the first 5 uninteresting ones
  99.      
  100.       CountBits=0;
  101.       Ba=0;
  102.       Bb=0;
  103.       Bc=0;
  104.       while(CountBits<24){  //store byte 6, 7 and 8 in Ba and Bb & Bc.
  105.         if(NextBit == true){
  106.           if(CountBits < 9){
  107.             Ba = (Ba << 1);  //Shift Ba one bit to left and store MISO-value (0 or 1)
  108.             //read MISO-pin, if high: make Ba[0] = 1
  109.             if(digitalRead(MISOPin)==HIGH){
  110.               Ba |= (1<<0);  //changes first bit of Ba to "1"
  111.             }
  112.             NextBit=false;
  113.           }
  114.           else if(CountBits < 17){
  115.             Bb = Bb << 1;  //Shift Ba one bit to left and store MISO-value (0 or 1)
  116.             //read MISO-pin, if high: make Ba[0] = 1
  117.             if(digitalRead(MISOPin)==HIGH){
  118.               Bb |= (1<<0);  //changes first bit of Bb to "1"
  119.             }
  120.             NextBit=false;
  121.           }
  122.           else{
  123.             Bc = Bc << 1;  //Shift Bc one bit to left and store MISO-value (0 or 1)
  124.             //read MISO-pin, if high: make Bc[0] = 1
  125.             if(digitalRead(MISOPin)==HIGH){
  126.               Bc |= (1<<0);  //changes first bit of Bc to "1"
  127.             }
  128.             NextBit=false;
  129.           }
  130.         }
  131.        
  132.       }
  133.      
  134.       ////////////////////////
  135.       Ba=255-Ba;
  136.       Bb=255-Bb;
  137.       Bc=255-Bc;
  138.       ///////////////////Needed for my China meter, remove if power calculation is not done correctly!
  139.  
  140.       //Power = (Ba*255+Bb)/2
  141.       P=((float)Ba*255+(float)Bb+(float)Bc/255.0)/2;
  142.  
  143.       //Convert power (float) to string
  144.       static char outstr[15];
  145.       AmountOfWatts = dtostrf(P,4,1,outstr); //4 digits, 1 decimal.... needs to be looked at, not done yet
  146.            
  147.       //Voltage mean
  148.       ReadData[0] = (U+ReadData[0]*((float)Antal-1))/(float)Antal;
  149.       //Current mean
  150.       ReadData[1] = (P/U+ReadData[1]*((float)Antal-1))/(float)Antal;
  151.       //Power mean
  152.       ReadData[2] = (P+ReadData[2]*((float)Antal-1))/(float)Antal;
  153.      
  154.       //Print out results (i skipped the mean values since the actual ones are very stable)
  155.       monitor.print("U: ");
  156.       monitor.print(U,1);
  157.       monitor.println("V");
  158.       monitor.print("I: ");
  159.       monitor.print(P/U*1000,0);  //I=P/U and in milli ampere
  160.       monitor.println("mA");
  161.       monitor.print("P: ");
  162.       monitor.print(P,1);
  163.       monitor.println("W");
  164.       monitor.println("");
  165.      
  166.       if(Antal==10){ //every 10th 70-package = every ~10s
  167.         //transmit ReadData-array to nRF or Wifi-module here:
  168.         //transmission function here...
  169.  
  170.         SendToDomoticz(AmountOfWatts);
  171.        
  172.         //Reset ReadData-array
  173.         ReadData[0] = 0;
  174.         ReadData[1] = 0;
  175.         ReadData[2] = 0;
  176.         //reset mean-value counter
  177.         Antal=0;  
  178.       }
  179.       inSync=false;  //reset sync variable to make sure next reading is in sync.
  180.      
  181.     }
  182.    
  183.    
  184.     if(Bb==0){  //If Bb is not 3 or something else than 0, something is wrong!
  185.       inSync=false;
  186.       monitor.println("Nothing connected, or out of sync!");
  187.     }
  188.  
  189.  
  190.   }
  191. }
  192.  
  193. //Function that triggers whenever CLK-pin is rising (goes high)
  194. void CLK_ISR(){
  195.   //if we are trying to find the sync-time (CLK goes high for 1-2ms)
  196.   if(inSync==false){
  197.     ClkHighCount = 0;
  198.     //Register how long the ClkHigh is high to evaluate if we are at the part wher clk goes high for 1-2 ms
  199.     while(digitalRead(CLKPin)==HIGH){
  200.       ClkHighCount += 1;
  201.       delayMicroseconds(30);  //can only use delayMicroseconds in an interrupt.
  202.     }
  203.     //if the Clk was high between 1 and 2 ms than, its a start of a SPI-transmission
  204.     if(ClkHighCount >= 33 && ClkHighCount <= 67){
  205.        inSync = true;
  206.     }
  207.   }
  208.   else{ //we are in sync and logging CLK-highs
  209.     //increment an integer to keep track of how many bits we have read.
  210.     CountBits += 1;
  211.     NextBit = true;
  212.   }
  213. }
  214.  
  215. boolean connectWiFi(){
  216.   Serial.println("AT+CWMODE=1");
  217.   delay(2000);
  218.   //String cmd="AT+CWJAP=\"";
  219.   //cmd+=SSID;
  220.   //cmd+="\",\"";
  221.   //cmd+=PASS;
  222.   //cmd+="\"";
  223.   //sendDebug(cmd);
  224.   //delay(5000);
  225.   //if(Serial.find("OK")){
  226.   //  monitor.println("RECEIVED: OK (connectWiFi)");
  227.   //  return true;
  228.   //}else{
  229.   //  monitor.println("RECEIVED: Error (connectWiFi)");
  230.   //  return false;
  231.   return true;
  232.   //}
  233. }
  234.  
  235. void sendDebug(String cmd){
  236.   monitor.print("SEND: ");
  237.   monitor.println(cmd);
  238.   Serial.println(cmd);
  239. }
  240.  
  241. void SendToDomoticz(String AmountOfWatts){
  242.   String cmd = "AT+CIPSTART=\"TCP\",\"";  //make this command: AT+CPISTART="TCP","192.168.4.4",8084
  243.   cmd += IP;
  244.   cmd += "\",8084"; //Domoticz is running on port 8084... needs to be changed into a variable that is set on top of sketch
  245.   sendDebug(cmd);
  246.   delay(2000);  //wait a little while for 'Linked' response - this makes a difference
  247.   if(Serial.find("Linked")) {
  248.     monitor.println("Linked!");
  249.   }
  250.  
  251.   cmd = GET;
  252.   cmd += IDX;
  253.   cmd += GET2;
  254.   cmd += AmountOfWatts;
  255.   cmd += GET3;
  256.   cmd += " HTTP/1.0\r\n";  //construct http GET request
  257.   cmd += "Host: 192.168.4.4\r\n\r\n";        //Domoticz IP > Needs to be changed into variable that is set on top of sketch
  258.   Serial.print("AT+CIPSEND=");              
  259.   Serial.println(cmd.length());  //esp8266 needs to know message length of incoming message - .length provides this
  260.  
  261.   if(Serial.find(">"))    //prompt offered by esp8266
  262.   {
  263.    monitor.println("found > prompt - issuing GET request");  //a debug message
  264.     Serial.println(cmd);  //this is our http GET request
  265.   }
  266.   else
  267.   {
  268.     Serial.println("AT+CIPCLOSE");  
  269.     monitor.println("No '>' prompt received after AT+CPISEND");
  270.   }
  271. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement