Κυριακή 30 Αυγούστου 2020

Arduino Μελισσοζυγαρια Beehive Scale LCD SMS 3 Load Sensors Temperature Humidity

deprecated version.Just for temp history....
Find above post supporting also ThingSpeak
https://jimakoskx.blogspot.com/2021/01/arduino-beehive-5-scales-dht-thingspeak.html



OLD VERSION

....2000 lines 

  • Arduino r3/pro mini code for  4 Readings per SMS  Program
  • Adjustable "daily" sms from every 1 hour up to every 99 hours
  • 2 modes ( lcd  | sms) adjust or wait home for sms with weights from Farm field with BEES
  • 95% Flash memory 45% S-Ram
  • Aytonomus(paying only your sim card for sms/no 'monthly' costs to anyone)

  • 10 lcd menus
  • 3 buttons for lcd editing
  • 2x16 minimum supporting LCD with i2c
  • 1 push button for LCD
  • 1 Switch for General Power from Batteries
  • 2 Relays,one for SIM power on/off and one for Sensors
  • 1 SIM800L for micro-card of CELL PHONE network 

  • 1 Temperature Humidity sensor Dht 11||12||21||22
  • 5 supporting HX711 amplifiers..
  • 5.....LOAD SCALES up to 327.680 grams (327 Kg)

  • Supporting RTC for saving power (SLEEP_MODE_PWR_DOWN)
  • 1 sms per day to PRIMARY client cellphone encoded for man view
    (up to 4*6 sensor readings on sms of ~160 max chars)
  • ......allows SECONDARY cell phone number (doubled sms in case of two beehive partners)


  • 5 arduino libraries(EEPROM,Wire,SoftwareSerial, avr/sleep,avr/wdt)
  • 2 external libraries     LiquidCrystal_I2C,          DHT                            (dht._type must be changed to public)
  • Adjustable Trigger mode for the 2 relays(HIGH,LOW)
  • Adjustable DHT selection (11,12,21,22)
  • yyy

  • zzz




--------

You may need about 20 minutes to install the MINIMUM hardware
and upload this to check it out for 'me' ! 

Minimum harware: 60 dollars $$$  from hellas  € near greece and turkey !!

1 arduino uno r3 or pro mini (5 dollars)

3 buttons  (1 dollars)

1 breadboard ( 3 dollars)

1 lcd 2x16 with i2c (5 dollars)

1 relay mode        (3 dollars)

1 dht sensor        (2 dollars)

1 HX711 amplifier (1 dollars)

1 load scale  (20 dollars)

1 SIM800L module (15 dollars)

jumper wires (5 dollars)





















You may need about 1 month to install the MINIMUM hardware
if you only know some programming   


You may need about 150 euros  to install and release
your 1st minimum  project of
BEEHIVE/remote SCALE SMS LCD TEMPERATURE HUMIDITY

4 Readings and 1 SMS per day
Program


Menu of 10 adjustements ,,,saving preferences on  EEPROM

0.Enter password-pin of sim card is password for program

1. Set the hours to delay before take the first reading when   SMS mode(before you go home to wait 1 sms per day).    Set also the simulation hours contained in one day meaning that if this value is 12 instead of 24 you get 2 sms and 8 readings per day.

2.Showing sensors (when you are on the field/farm with bees)

3.Callibrating the 5 supporting load weight scales 

4.Taring the 5 scales

5.Enable/Disable the 6(5+1 dht) sensors

6.Set pin of THIS (sender) sim card and the sms TARGET (receiver) phone number of onother cellphone card.

7.Set the secondary phone number that will receive the same sms in case that two partners having the beehive farm.

8.Set Relays type triggering LOW or HIGH

9.Set DHT type 11 | 12 | 21 | 22 (temperature humidity)

10.SMS testing/sending manual/and call test

11.Set delay seconds for when SIM800L Power On/Off

12.showing the strenght of gsm/sim800l signal from cellphone network every some seconds in order to find a good location for the arduino scale.


* ----example of SMS immidiatelly after power on on SMS MODE -----

 * 22.10C 65.00%

 * 0#4.29<109531/10=0

 * 1#-75.18<22607/10=-8

 * 2#Err

 * 3#Err

 * 4#Off

 * ---------


   * ---- example of SMS DAY 1----(remember that we sending Grams divided by 10 for shortage that mean 123=1230=1.230 grams/units = 1,23 Kg ----------

22C63%1  (JUST BEFORE SMS :22 C , 63 Humidity, Day 1

0#2-179   (JUST BEFORE SMS :#0 scale is 20 grams and the difference with previus day is -1.790 grams (day zero was 1.810))

1#0+7     (JUST BEFORE SMS :#1 scale is 0 grams and the difference with previus day is +70 grams

22C63%      

308+127   (-6 hours BEFORE SMS :#0 scale was 3.080 grams and difference is 1.270 (was 1.810))

9+16

22C62%

224+43  (-12 hours BEFORE SMS :#0 scale was 2.240 grams and difference is 430 (was 1.810))

9+16

22C63%

181+179  (-18 hours BEFORE SMS :#0 scale was 1.810 grams and difference is 1.790 (was 20))

-7+0

------------------------------

Warning to ensure Ground of Battery is connected to the Ground of Arduino. 
(and with resistor divider for 3.3(and not 5)volts on RX of Sim800L
...decide what you want because i am confused if SIM800L is 5volts tolerable and 
i have not yet DARE to test it!
Personally i have 10K/20K resistor divider cutting voltage to 3.3 volts a little higher than Logic High of 5volt Arduino)

------------------------------------

To begin copy and compile the following blue small arduino program 
meaning to solve first the libraries issue...and if succesfull then 
just change the private access to public
of the uint8_t  _type from DHT.cpp (as from below image)... ...and you are ready to copy and compile the ~2000 lines code!

#include <EEPROM.h>

#include <SoftwareSerial.h>

//https://www.arduino.cc/en/Reference/LiquidCrystal

#include<LiquidCrystal_I2C.h>

//https://github.com/adafruit/DHT-sensor-library

#include <DHT.h>

LiquidCrystal_I2C lcd(0x27,16,2);//find your own address if not 0x27

DHT dht(SomePin,11);

void setup(){}

void loop(){}









Copy and build the below arduino code



//jimakoskx@all released about November 2020

//28798 93%  , 875 42%

 


#include <SoftwareSerial.h>

#include <LiquidCrystal_I2C.h>

#include <EEPROM.h>

#include <DHT.h>//you must change the private aceess of dht._type to public in order to compile

//#include <HX711.h> //this library managed to be avoided copying 1 method




//using 95%(~29.000 bytes)of Flash , 43%(~900 bytes)of SRAM

#define DEBUG //printing details 87% Flash if comment this

#define CALL_INSTEAD_SMS //use to finally test using batteries and just dont waste real money sending sms

//#define SIMEMPTY // dont delay after sim power on  and before sim power off (delay only 1500+2500 for debugging without actual sim card)



//for debug 5000=2 min day cycle,, 7500=3 min , 10000=4 min ,150.000=hour

unsigned long hourMillis1=100000ul ;//this will be updated on sms mode.

unsigned long hours6Millis(){unsigned long out=hourMillis1; out*=6;return out;}








byte currMenu=7;//=1 on release to be quicker for just when changing battery(or every time BEFORE reopen to SMS mode) and not quick for entering password and make adjustments

bool needPassword=0;//=1 on release







///DONT TOUCH

uint8_t ModeLcdOrSms;//only bit 0 need to remain constant,we have 7 bits to use!

/*

 * Beeheeve Scale,Temperature Humidity application.

 * Sending SMS every day counting 4 times .

 * Because sms have a length limit about 150 characters

 * we need a version that will support up to 3 load scales (with code as ready as can be for 5 scales)

 * and a Dht (temperature humidity) sensor.

 * That mean that a single sms wiil have this format with weights representing 10 grams(example 3456=34 Kg and 56X grams 

 * to save just a bit in sms and arduino sram

 * c=celsiu degrees

 * sms with (4*7)+(4*30)=148 chars

 * 36C62%9 MEAN 36 Celsiu 62 % Humidity and DAY 9 

 * So for the weights you pout a zero on the end and this is Grams

 * 

 * The first sms that will be send when user is on the field will NOT have the differences

 * and will be a fully report of sensors

 * and is just to ensure the user that scale is up and running.Is also in grams(not devides by 10) instead of ten grams even 

 * ----SMS immidiatelly afte power on on SMS MODE -----

 * 22.10C 65.00%

 * 0#4.29<109531/10=0

 * 1#-75.18<22607/10=-8

 * 2#Err

 * 3#Err

 * 4#Off

 * ---------

 * 

 * ------SMS DAY 0--------------

22C63%0  (JUST BEFORE SMS : 22 C , 63 Humidity, Day 0 =when user was on field and ...change batterries as example)

0#181+181 (JUST BEFORE SMS :  #0 scale is 1.810 grams ..and difference the FIRST day will be same as weight)

1#-7-7    (JUST BEFORE SMS : #1 scale is -70 grams

22C63%    (-6 hours BEFORE SMS : 

181+181  (-6 hours BEFORE SMS :#0 scale is 1.810 grams

-7-7    (-6 hours BEFORE SMS :

22C63%   (-12 hours BEFORE SMS :

181+181  (-12 hours BEFORE SMS :#0 scale is 1.810 grams

-7-7   (-12 hours BEFORE SMS :

22C63%  (-18 hours BEFORE SMS :

2+2    (-18 hours BEFORE SMS : #0 scale is 20 grams

-7-7  (-18 hours BEFORE SMS :

 * ------SMS DAY 1--------------

22C63%1  (JUST BEFORE SMS :22 C , 63 Humidity, Day 1

0#2-179   (JUST BEFORE SMS :#0 scale is 20 grams and the difference with previus day is -1.790 grams (day zero was 1.810))

1#0+7     (JUST BEFORE SMS :#1 scale is 0 grams and the difference with previus day is +70 grams

22C63%      

308+127   (-6 hours BEFORE SMS :#0 scale is 3.080 grams and difference is 1.270 (was 1.810))

9+16

22C62%

224+43  (-12 hours BEFORE SMS :#0 scale is 2.240 grams and difference is 430 (was 1.810))

9+16

22C63%

181+179  (-18 hours BEFORE SMS :#0 scale is 1.810 grams and difference is 1.790 (was 20))

-7+0

------------------------------




 * 

 * 

 * 

 * Part two

 * 

 * Usually user need to change baterries

 * So when is ready reopens the main Switch but must PRESSING ANY button in order to be on SMS mode 

 * If succesfull will receive the first sms  in about 1 minute

 * If no then something is false or didnt had PRESSed some button so we are on LCD mode 

 * that in this case the Push-Switch for lcd must be open(to have current)

 * In case Lcd-Push-Switch is not opened and we got in lcd mode the 

 * call to lcd.begin()...STACKS (halt) the execution as i am seeing ...so if lcd finally powered on ....we can see the welcome message

 * To be a little more understandable we flushing on-off the built in led (13) for 5 seconds in case of SMS mode.

 * 

 * So we have 2 MODES 

 * 1.LCD mode that is the default when powering on the arduino

 * 2.SMS mode that will be IF user pressing any button when powering on

 * 

 * This is also like this because if some error occur and reset we dont want to go sms mode without having control of what happened etc

 * Or if some one stranger go and touch arduino it will after ...be on lcd mode

 * but then will need PASSWORD-PIN in order to do anything else with arduino(if he power the lcd from push button)

 * 

 * So in LCD mode we have 3 buttons (OK,MENU,ROLL)

 * 

 * a)When i am saying to PRESS some button i mean PRESS-Release quickly.

 * b)And when i say Press/Hold i mean PRESS-HOLD for about half a second-and RELEASE

 * So pressing (quick press-release) the MENU (middle) button you can iterate to the following menu.

 * And holding (slow press-release) the MENU ,you iterating backward

 * 

 * 0.Enter PIN (password) 

 * 1.         a)Set the hours to delay before take the first reading when SMS mode 

 *            That is to lets say is 9 a.m. morning and user is ready to leave from field

 *            If powers on in sms mode he will (for the following days) 

 *            take the readings at 09:00 , 15:00 , 21:00 , 03:00

 *            But maybe wants to know weights when 

 *                a)morning bees are IN the beehive ,

 *                b)night bees are IN the beehive  , 

 *                c-d) and 2 times in the usually day that some bees are out

 *                Such a combination can be at 

 *                 06:00 , 12:00 , 18:00 , 24:00

 *            So having this quickly to adjust,,,you can put a delay of 3 hours in case that

 *            you powering on 09:00 o clock

 * ---0 and 1 menu are visble without password...and all the others will be unlocked if you put the correct password-PIN

 *           b)Set the hours contained in one day.This is by default 24.You can set this from 1 up to 99. 

 *           Meaning that if this value is 12 then you will receive the daily sms every 12 hours (so 2 sms per day)

 *           If it is 48 you will  receive 1 sms per 2 days

 * 

 * 

 * 2.Showing sensors

 *      There are 6 sensors 1 DHT (temperature humidity) and 5 load weight scales

 *      on default you are seeing results from DHT and SCALE #0 

 *      Press Ok to view readings from Scale #1 and Scale #2

 *      Press Ok again for Scale #1 and Scale #2

 *      Press Ok again to make a circle to DHT and Scale #0

 *      

 *      Press Roll Button for switching viewing (or not) the readings of HX711 amplifier beyond the translated Grams(kg)

 *      

 * 3.Callibrating the 5 load scales

 *      Every Load scale is connected to a HX711 amplifier

 *      The load cell is sending a reading to HX711 and HX711 amplifies this and give it to me

 *      Then we must translate this reading to grams or pounds or whatever

 *      A callibration needs at least ONE KNOWN WEIGHT

 *      So the siomplest callibration is to take 2 AVERAGES

 *      a)average-R0 of readings when scale is empty

 *      b)average-R1 of readings when scale have a KNOWN WEIGHT W

 *      After that ..every reading will be translate as

 *      R1 mean W

 *      Rcurr   x?

 *      --------

 *      x=W * (Rcurr-avZero) /  (R1-avZero) 

 *      OK?...but a better callibration will may use more than ONE standart known weight to be mpore precicelly 

 *      One good callibration may is at 0% ,25% , 50% ,75% and 100% of the LOAD CAPACITY

 *      So you can callibrate up to 7(+1 for zero autiomatically) KNOWN weights PER SCALE 

 *      -------------------------

 *      Now Press Ok (quick releasing) to select witch of 5 scales you want to callibrate

 *      Hold Ok for more than 666 millisecond and release  to start callibration for particular scale

 *      If sensor error you will see it in about 1-2 seconds

 *      Step 1 enter how many know weights you have to take the average of them

 *      Press Roll Button to roll 1-7 available chars

 *      If you decided(lets say 2 known weights) then ENSURE SCALE IS EMPTY and press OK

 *      because when you press OK will start averaging for ZERO

 *      Wait until 100/100 

 *      Now you see 

 *      lcd0:2/3:000000 grams

 *      lcd1:Weight is up ...OK

 *      And that mean that you must oput ONTTO the scale the known weight (example your phone)

 *      Press Roll button to roll digits

 *      Press (and release) Roll button more than 666 millisecond to switch digit POSITION(cursor)

 *      Press ok when you succesfully update the known weight to lcd (digits)

 *      Now do the same for the next weight ... 3/3

 *      1/3 is the average of zero

 *      You can not put the same grams twice....

 *      and the data will be sorted by known weights

 *      

 *      When finish you will be seeing the fresh readings and translations according the new callibration that you have just finished...

 * 

 * 4.Taring the scales

 *    We said that the translation is x=W * (Rcurr-avZero) /  (R1-avZero) 

 *    But we have one more adjustment ..the tare

 *    If you have empty scale and take the translated reading (X)

 *    then you can put this X as TARE so for DECREASING this X from next translations

 *    so the new formula is x=[W*(Rcurr-avZero)/(R1-avZero)  -  TARE ] 

 *    

 *    Press Ok to switch between 5 scales

 *    Hold Ok more than 666 millis fora start taking the NEW ZERO average 

 *    and set the translation of this reading as a TARE

 *    Make sure the scale is EMPTY on taring....

 *    Taring is just for better adjust and its not so important (?) for beheeve scales ...

 *    

 * 5.On/Off sensors   

 *    For shortage i use Of instead of Off (with 2 f)

 *    Press Roll button quick, to switch between N and F 

 *    Press Roll button more than 666 to switch to onother sensor

 *    Press Ok to update ROM (and hold the values)

 * 

 * 6.PIN and sms Target Phone number setting.Also setting of  SIM card Need or NOT a PIN before sms(loggin to sim )

 *    Roll quick to select digits

 *    Roll slow to move cursor

 *    After pin (sorry but you must move cursor to char n or f) for 

 *    switch On/Off if the PIN will be used ...meaning if sim card HAS PIN or its disabled

 *    Also this PIN will be the PASSWORD for arduino irrelative if it is On or Off (for smsing)

 *    Select also the phone number in whitch we will send the sms of readings

 *    Press Ok to UPDATE

 *    

 * 7.Set the secondary phone number that will receive the daily sms...in case that 2 partners have the beehive farm and both wants the readings.

 *   Use On/Of to disable sendiong DOUBLED sms.

 *      In the 2nd line you can see the proposision to hold the OK button in order to shwitch phones.

 *      Primary will be the secondary and secondary will be the primary.

 *      This is because if 2 partners are involved only 1 may is on the LCD mode testing/and adjusting things.

 *      And in case that the LCD user wants to test the sms or test the calling ,,,he can switch phone numbers if his phone is secondary.

 *      This is because the testing things(see below menu) only using the primary phone number.

 *      The secondary phone number only being used for DAILY SMS ...and nothing else.

 *    

 * 8.Relay types

 *    Tis is in case ....that some relay broke and need change!!??...

 *    Anyway select L or H for the triggering type of according relay

 * 

 * 9.DHT type (temperature humidity)

 *  supporting 11,12,21,22 type.

 *  This is also in case of a damage/replacement

 *  DHT 22 is the more accurate.....

 *  

 *  10.SMS testing

 *    a)Press Ok and release quick to send the String on LCD1 (second row) as SMS  

 *        If when press ok you have also Roll button pressed

 *        the string on lcd1 will be emptied so to sms what ever you want (16 chars only)

 *    b)Press Ok and release more than 666 for sms-ing a FULL sensor report

 *    

 * 11.Delay seconds for SIM800L     

 *    

 *    When the relay of SIM is triggered and the SIM is powered on 

 *    we need some seconds to be on network before try to send sms

 *    

 *    And also when we write the sms data 

 *    and want to go sleep fro 4 hours we dont want immidiatelly 

 *    to UN-trigger the relay(powering off the SIM chip)

 *    because maybe SIM800L didnt manage to send the SMS

 *    So we need some seconds to delay also BEFORE powering off SIM chip

 *    

 *    15 seconds for After Sim ON and

 *    25 seconds for Before Sim Off 

 *    i think is maybe a 'safe' pair of values

 *  

 * 12.Signaling  

 *    Press OK to start showing the strenght of the signal from cell phone network.

 *    Use this to find a good spot for your arduino-box.

 *    Theoretically a good signal is more than 30 .

 *    

 *      

 * 

 * 

 */






/*

 * Finally no possible to fit 5 HX711 in arduino Pro mini because A6 and A7 are INPUT ONLY (not even INPUT_PULLUP)

 * Even if i think that i can manage with an external PULL_up resitor ...the digitalRead() on A6 and A7 returns always 0 (zero)

 * So....fuck it and i must rewrite the program only for 3 scales...finally ..just before release!

 * (Must Delete eeprom usage,,,functions itemp(),some references and some code on showing,calibrating,taring)

 */

 

const uint8_t PinButOk=4;

const uint8_t PinButMenu=3;

const uint8_t PinButRoll=2;



const uint8_t PinSimTx=6;

const uint8_t PinSimRx=7;

//1st scale

const uint8_t PinHx0Dt=10;

const uint8_t PinHx0Sck=11;

//2nd scale

const uint8_t PinHx1Dt=8;

const uint8_t PinHx1Sck=9;

//3rd scale

const uint8_t PinHx2Dt=13;//better not use 13 (led) for SCK....

const uint8_t PinHx2Sck=12;///sos i finally put 13 on sck because if on dt remains half open the led!...

//4th scale

const uint8_t PinHx3Dt=5;

const uint8_t PinHx3Sck=A0;

//5th scale NOT APPLICABLE!!!!

const uint8_t PinHx4Dt=A6;//must use those pins for buttons with external pull up resistor

const uint8_t PinHx4Sck=A7;//and have 3,4 pin for scale(on final version)






const uint8_t PinDht=A1;//#17

const uint8_t PinRelaySensor=A2; //A6//#21 in Pro mini  ...meaning when test in Arduino uno r2 we can only have 4 FOUR scales

const uint8_t PinRelaySim=A3; //A7//# 20in Pro mini ...meaning when test in Arduino uno r2 we can only have 4 FOUR scales

//const uint8_t PinSDA=A4;for i2c lcd//#18

//const uint8_t PinCL=A5;//#19



////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

//ser instead of Serial for building/debugging

#include <SoftwareSerial.h>//construtor SoftwareSerial(rxPin, txPin, inverse_logic)

SoftwareSerial sim(PinSimTx,PinSimRx);

 //saving 106 Flush bytes instead of use Serial object (by the way that we must use SoftwareSerial for sim800l)

SoftwareSerial ser(0,1);//comment before release

void serPrintMillisForDebug(){///using with time stamps to check the program (ouput) on simulation (with onother...java..program!!)

  ser.print('(');

  ser.print(millis());

  ser.print(')');

}

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

//MY LCD 16X2...and find your own address if not 0x27

#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27,16,2);


////////////////////////

////////////////////////

////////////////////////

// MAIN STORAGE LOGIC

const byte ScalesCanHandle=5;//building for 3 scales but lets get ready for 5.Also those will be used for editing on lcd mode

const byte SensationsPerSms = 4;//4 readings before send sms.If sms length>150...would adjust this adjust this according free sram on release

const int ERROR_WEIGHT_SCALE_VALUE   =  32767; //if have error value will not be seen on sms....Meaning missing some reading...user responsible to notice...

const int DISABLED_WEIGHT_SCALE_VALUE= -32768; //if have disabled value will not be seen on sms

byte iter=0;//the currentposition in whitch i am storing.On iter 0 i am sending the daily sms


/**

 * structure of StoredWeights[ ScalesCanHandle * SensationsPerSms * 2 ]

 * 1st_ReadScale_0 , 1st_ReadScale_1 , 1st_ReadScale_2 , 1st_ReadScale_3 , 1st_ReadScale_4

 * 2nd_ReadScale_0 , 2nd_ReadScale_1 , 2nd_ReadScale_2 , 2nd_ReadScale_3 , 2nd_ReadScale_4

 * 3rd_ReadScale_0 , 3rd_ReadScale_1 , 3rd_ReadScale_2 , 3rd_ReadScale_3 , 3rd_ReadScale_4

 * 4th_ReadScale_0 , 4th_ReadScale_1 , 4th_ReadScale_2 , 4th_ReadScale_3 , 4th_ReadScale_4

 * *2 for the previus day toi have the difference that IS MORE IMPORTANT

 * 1st_PreviusRead_0 , 1st_PreviusRead_1 , 1st_PreviusRead_2 , 1st_PreviusRead_3 , 1st_PreviusRead_4

 * 2nd_PreviusRead_0 , 2nd_PreviusRead_1 , 2nd_PreviusRead_2 , 2nd_PreviusRead_3 , 2nd_PreviusRead_4

 * 3rd_PreviusRead_0 , 3rd_PreviusRead_1 , 3rd_PreviusRead_2 , 3rd_PreviusRead_3 , 3rd_PreviusRead_4

 * 4th_PreviusRead_0 , 4th_PreviusRead_1 , 4th_PreviusRead_2 , 4th_PreviusRead_3 , 4th_PreviusRead_4

 */

 //storing Ten Grams because we need RAM...and sms shrinking..............

//Storage is on arduino int :bits=16 ,signed 15 ...2^15=32.768

//32.768 Ten Grams -> 327.680 grams -> 327 Kg max 

int StoredWeights[ ScalesCanHandle * SensationsPerSms * 2 ] ;


//no need storage for Previus Day Readings on Temperature and Humidity.

//We are not weather station.......

char temperature[SensationsPerSms];    //-128:Disabled 127:Error(nan)

char humidity[SensationsPerSms];       //

        void printReadingsForDebug0(){

              int i=0;

              while(i<SensationsPerSms){

                ser.println(i);

                int j=0;

                while(j<ScalesCanHandle){

                  ser.print(StoredWeights[i*5+j]);

                  ser.print(',');

                  ser.println(StoredWeights[i*5+j+20]);

                  ++j;

                }

                ++i;

              }

        }

        //functions for Try using exsisting RAM for onother purpose (for lcd mode editing logic variables)

        //on callibration we need to temporarelly store 8 longs for standarts and 8 for averages

        //=16 longs = 32 ints we canuse from  StoredWeights[40] and remain 8 general int registers for usage on lcd mode to not bound rom declaring new variables

        long readTempLong(int i07){

            unsigned int l0=StoredWeights[i07];

            unsigned int l1=StoredWeights[i07+1];

            unsigned long l=l0;

            l<<=16;

            l|=l1;

            long out=l;

            return l;          

        }

        long getTempStandart(int i07){return readTempLong(   i07*4 );}

        long getTempAverage(int i07) {return readTempLong(2+(i07*4));}

        void writeTempLong(int i07,long tempVal){

            unsigned long l=tempVal; 

            unsigned int l0=l>>16;

            unsigned int l1=(l<<16)>>16;  

            StoredWeights[i07]=l0;

            StoredWeights[i07+1]=l1;

        }

        void setTempStandart(int i07,long tempVal){writeTempLong(   i07*4 ,tempVal);}

        void setTempAverage(int i07,long tempVal) {writeTempLong(2+(i07*4),tempVal);}

        //Now to remember on callibration that i have 8 ints more to use and not declare

        int iTemp32(){return StoredWeights[32];}

        void iTemp32(int storeTemp){StoredWeights[32]=storeTemp;}

        

        int iTemp33(){return StoredWeights[33];}

        void iTemp33(int storeTemp){StoredWeights[33]=storeTemp;}

        

        int iTemp34(){return StoredWeights[34];}

        void iTemp34(int storeTemp){StoredWeights[34]=storeTemp;}

        

        int iTemp35(){return StoredWeights[35];}

        void iTemp35(int storeTemp){StoredWeights[35]=storeTemp;}

        

        int iTemp36(){return StoredWeights[36];}

        void iTemp36(int storeTemp){StoredWeights[36]=storeTemp;}

        

        int iTemp37(){return StoredWeights[37];}

        void iTemp37(int storeTemp){StoredWeights[37]=storeTemp;}

        

        int iTemp38(){return StoredWeights[38];}

        void iTemp38(int storeTemp){StoredWeights[38]=storeTemp;}

        

        int iTemp39(){return StoredWeights[39];}

        void iTemp39(int storeTemp){StoredWeights[39]=storeTemp;}

        


        void sortInsertionTempStandartsAndAveragesByStandarts(int RequestedStandarts){

            int i,  j;  

            long key,followKey;

            for (i = 1; i < RequestedStandarts; i++) {  

                key = getTempStandart(i);  

                followKey = getTempAverage(i);  

                j = i - 1;  

                while (j >= 0 && getTempStandart(j) > key) {  

                    setTempStandart(j+1,getTempStandart(j)); //tempStandarts[j+1] = tempStandarts[j];  

                    setTempAverage(j+1,getTempAverage(j));   //tempAverages[j+1] = tempAverages[j];

                    j = j - 1;  

                }  

                setTempStandart(j+1,key); //tempStandarts[j + 1] = key;  

                setTempAverage(j+1,followKey); //tempAverages[j+1] = followKey;

            }  

        }

        

     

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

//try saving some RAM storing strings on FLASH memory

const __FlashStringHelper* strEmpty(){return F("");}

const __FlashStringHelper* strSpace(){return F(" ");}

const __FlashStringHelper* strSpace16(){return F("                ");}

const __FlashStringHelper* strCelsiu(){return F("C");}

const __FlashStringHelper* strPercent(){return F("%");}

const __FlashStringHelper* strLess(){return F("<");}

const __FlashStringHelper* strLine(){return F("\n");}

const __FlashStringHelper* strDefPin(){return F("1111");}

const __FlashStringHelper* strDefPhone(){return F("306949496595");}

const __FlashStringHelper* strHigh(){return F("High ");}

const __FlashStringHelper* strLow(){return F("Low  ");}

const __FlashStringHelper* str8Ones(){return F("11111111");}

const __FlashStringHelper* strJimakoskx(){return F("jimakoskx@all");}

const __FlashStringHelper* strSOS(){return F("SOS");}

const __FlashStringHelper* strDone(){return F("Done ! ");}

const __FlashStringHelper* strOff(){return F("Off");}

const __FlashStringHelper* strErr(){return F("Err");}

const __FlashStringHelper* strStep1(){return F("step_1");}

const __FlashStringHelper* strDelay(){return F("Delay");}



//TRYING TO LEARN HOW TO SAVE SRAM and RAM

//#define FV(x) (__FlashStringHelper*)(x)/*template<class T> inline Print &operator << (Print &obj, T arg){obj.print(arg);return obj;};*/

//to easy the editing i use On/Of for On/Off on something like On=High and Off=Low.....


void setOnCharAt(int index,const String & str){str.setCharAt(index,'n');}

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

//GENERAL REGISTERS AND FUNCTIONS

void ledBlink(byte seconds){

    int i=0;

    pinMode(LED_BUILTIN,OUTPUT);

    while(i<seconds){

      digitalWrite(LED_BUILTIN,HIGH);

      delay(5);//advise to blink few millis for saving battery!!!

      digitalWrite(LED_BUILTIN,LOW);

      delay(995);

      ++i;

    }

}

char ch;

//parsing long values from text

long parselong0p(const String & str,int from,int len){

  long out=0;

  if(len<0){len=str.length();}

  while(from<len){ch=str.charAt(from);//skipping high zeros

    if(ch!='0'){break;}++from;

  }

  while(from<len){ch=str.charAt(from);

    ch-=48;

    if(ch>=0&&ch<=9){out+=ch;out*=10;}

    else{break;}++from;

  }out/=10;

  return out;

}

//prints on str 8 binary bits of byt

      void clearToBinaryByte0(byte byt,const String & str){

          str.remove(0,str.length());

          str+=str8Ones();

          int i=0;

          while(i>=0){if(byt%2==0){str.setCharAt(i,'0');}byt>>=1;--i;}

      }

const String lcd0;

const String lcd1;

//String *lcdCurr=&lcd0;//finally i found how to de-reference!...but ...no need

bool changingLines=true;//if editing both lcd lines or just one

void strRemoveAll(const String & str){str.remove(0,str.length());}

void lcd0RemoveAll(){strRemoveAll(lcd0);}

void lcd1RemoveAll(){strRemoveAll(lcd1);}

void lcd01RemoveAll(){strRemoveAll(lcd0);strRemoveAll(lcd1);}

      

byte cursCol=0;//lcd cursor position

byte cursRow=0;//if we are current editing 1st or 2nd line of lcd

void lcdResetCursor(){lcd.setCursor(cursCol,cursRow);}

void lcd0SetCursor(int col=0){lcd.setCursor(col,0);}

void lcd1SetCursor(int col=0){lcd.setCursor(col,1);}

void lcdClearRefresh(){

  lcd.clear();

  lcd.print(lcd0);

  lcd1SetCursor();

  lcd.print(lcd1);

  lcdResetCursor();

}

//For quick viewing status on lcd

void lcdQuickPrintAndRefresh(const __FlashStringHelper * some,long toDelay=444){

  lcd.clear();

  lcd.print(some);

  delay(toDelay);

  lcdClearRefresh();

}

void lcdQuickPrintAndRefresh(const String & some,long toDelay=444){

  lcd.clear();

  lcd.print(some);

  delay(toDelay);

  lcdClearRefresh();

}

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

//GENERAL EEPROM FUNCTIONS-lib

    #include <EEPROM.h>

    void romUpdate2DigitsPerByte(int address,const String & strWithDigits48_57,int digits){

        int i=0;

        char leftChar,rightChar;

        byte b;

        while(i<digits){

            leftChar=strWithDigits48_57.charAt(i);

            ++i;

            rightChar=strWithDigits48_57.charAt(i);

            ++i;

            leftChar-=48;

            rightChar-=48;

            b=leftChar;

            b<<=4;

            b+=rightChar;

            EEPROM.update(address,b);

            ++address;

        }

    }    

    void romRead2DigitsPerByte(int address,const String & strTobeFilledWithDigits48_57,int digits){

        int l=address+(digits/2);

        byte b;

        byte leftChar,rightChar;

        while(address<l){

            b=EEPROM.read(address);

            ++address;

            leftChar=b; leftChar>>=4; leftChar+=48;

            strTobeFilledWithDigits48_57+=(char)leftChar;

            rightChar=b; rightChar<<=4; rightChar>>=4; rightChar+=48;

            strTobeFilledWithDigits48_57+=(char)rightChar;

        }

    }

    long romReadLong(int address){

        byte b=EEPROM.read(address);++address;

        long out=b;  out<<=8;

        b=EEPROM.read(address);++address;

        out+=b;      out<<=8;

        b=EEPROM.read(address);++address;

        out+=b;      out<<=8;

        out+=EEPROM.read(address);

        return out;

    }

    void romUpdateLong(int address,long out){

        address+=3;

        EEPROM.update(address,out);--address;

        out>>=8;

        EEPROM.update(address,out);--address;

        out>>=8;

        EEPROM.update(address,out);--address;

        out>>=8;

        EEPROM.update(address,out);

    }

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

//LOGIC of MY EEPROM 

bool romCheck(){

  byte br=romReadProgramId();

  if(br!=181){return false;}

  br=romReadDhtType();

  if(!dhtAcceptableType(br)){return false;}

  byte i=0;

  while(i<ScalesCanHandle){

      br=romReadScaleStandartsUsing(i);

      if(br>8){return false;}//only 2 min, max 8 values for callibration including zero standart and average

      ++i;

  }

  lcd0RemoveAll();

  romReadSimPin(lcd0);

  if(lcd0.length()!=4){return false;}

  lcd0RemoveAll();

  romReadSmsPhoneNumber(lcd0);

  if(lcd0.length()!=12){return false;}

  

  return true;

}

     // A D D R E S S S = 0 

     byte romReadProgramId(){return EEPROM.read(0);}

     

     ////////// SCALEs ////////////////////////////////////////

      //each scale has 

      //1 byte for ScaleStandartsUsing,if 0 or 1 then means UNCALLIBRATED (Callibrartion needs at least 2 averages Zero and a standart weight)

      //1 long ScaleGramsDif, this is the tare that will be deducted lastly on result (after translation of sensor reading to Grams according callibration)

      //8 available callibration standarts for ScaleStandart INCLUDING ZERO,

      //8 available callibration averages for ScaleAverage INCLUDING AVERAGE on EMPTY-ZERO

      //Generally Callibration need averages(and standarts) for 0%,25%,50%,75%,100% (5 values)

      //////////...but lets say someone need 0%,5%,20%,40%,60%,80%,100%,105% (8 values)

      //example if i have a load cell for 200 Kg then 

      //a simple callibration is with 2 values 0 kg and lets say 10 kg

      //but a better callibration will be to take the averages on 0 Kg , 50 Kg , 100 Kg ,150 Kg ,200 Kg 

      // 1 +4 + 32+32 = 69 bytes on rom

      // Scale 0 : A D D R E S S S = 1  +69

      // Scale 1 : A D D R E S S S = 70 +69

      // Scale 2 : A D D R E S S S = 139+69

      // Scale 3 : A D D R E S S S = 208+69

      // Scale 4 : A D D R E S S S = 277+69

     //byte temperatureCallibrated????

     byte romReadScaleStandartsUsing(int scale){return EEPROM.read(1 + scale*69);}

     long romReadScaleGramsDif(int scale){ return romReadLong(2 + scale*69);}

     long romReadScaleStandart(int scale ,int standart){return romReadLong(standart*4 + 6 + scale*69);}

     long romReadScaleAverage(int scale ,int standart){return romReadLong(standart*4 + 38 + scale*69);}

     

     void romUpdateScaleStandartsUsing(int scale,byte val){EEPROM.update(1 + scale*69 , val);}

     void romUpdateScaleGramsDif(int scale,long val){romUpdateLong(2 + scale*69,val);}

     void romUpdateScaleStandart(int scale ,byte standart,long val){romUpdateLong(standart*4 + 6 + scale*69,val);}

     void romUpdateScaleAverage(int scale ,int standart,long val){romUpdateLong(standart*4 + 38 + scale*69,val);}

     

     //address=346(free)

     //////////// GSM - SIM //////////////////////////////////////

     // A D D R E S S S = 346 

     void romReadSimPin(const String & output){return romRead2DigitsPerByte(346,output,4);}//sim pin 4 digits on 2 bytes

     void romUpdateSimPin(const String & input){romUpdate2DigitsPerByte(346,input,4);}

     

     // A D D R E S S S = 348

     void romReadSmsPhoneNumber(const String & output){return romRead2DigitsPerByte(348,output,12);}//sim phone 12 digits on 6 bytes

     void romUpdateSmsPhoneNumber(const String & input){romUpdate2DigitsPerByte(348,input,12);}

     

     // A D D R E S S S = 354 

     byte romReadFlagsByte(){return EEPROM.read(354);}

     //remains 1 unused bits

     void romUpdateFlagsByte(byte flagsbyte){EEPROM.update(354,flagsbyte);}

     void romUpdateFlagsByte(bool scale4On,bool scale3On,bool scale2On,bool scale1On,bool scale0On,bool dhtOn,bool SimNeedPin){

          byte b=scale4On;

          b<<=1;

          b+=scale3On;

          b<<=1;

          b+=scale2On;

          b<<=1;

          b+=scale1On;

          b<<=1;

          b+=scale0On;

          b<<=1;

          b+=dhtOn;

          b<<=1;

          b+=SimNeedPin;

          romUpdateFlagsByte(b);

     }

     void romUpdateOnScale(byte scaleId,bool enabled){

        byte one=1;

        one<<=2+scaleId;

        scaleId=EEPROM.read(354);

        if(enabled){

            one|=scaleId;

        }

        else{

            one=~one;

            one&=scaleId;

        }

        EEPROM.update(354,one);

     }

     // A D D R E S S S = 355 

     byte romReadRelaysFlag(){return EEPROM.read(355);}

     void romUpdateRelaysFlag(byte RelaysFlag){EEPROM.update(355,RelaysFlag);}

     void romUpdateRelaysFlag(bool SimRelayTriggeringOnHigh,bool SensorsRelayTriggeringOnHigh){

          byte out=SimRelayTriggeringOnHigh;

          out<<=1;

          out+=SensorsRelayTriggeringOnHigh;

          romUpdateRelaysFlag(out);

     }

     // A D D R E S S S = 356

     byte romReadDhtType(){return EEPROM.read(356);}

     void romUpdateDhtType(byte DhtType){EEPROM.update(356 ,DhtType);}

     // A D D R E S S S = 357

     byte romReadHourDelayBeforeSMSwhile(){return EEPROM.read(357);}

     void romUpdateHourDelayBeforeSMSwhile(byte HourDelay){EEPROM.update(357 ,HourDelay);}

     

     // A D D R E S S S = 358

     byte romReadSimSecWaitAfterPowerOn(){return EEPROM.read(358);}

     void romUpdateSimSecWaitAfterPowerOn(byte SecondsSimDelayWaitingNetwork){EEPROM.update(358 ,SecondsSimDelayWaitingNetwork);}

     // A D D R E S S S = 359

     byte romReadSimSecWaitBeforePowerOff(){return EEPROM.read(359);}

     void romUpdateSimSecWaitBeforePowerOff(byte SecondsSimDelayBeforePowerOff){EEPROM.update(359 ,SecondsSimDelayBeforePowerOff);}

     

     // A D D R E S S S = 360

     byte romReadHoursSimulationPerDay(){return EEPROM.read(360);}

     void romUpdateHoursSimulationPerDay(byte HoursPerDay){EEPROM.update(360 ,HoursPerDay);}

     

     // A D D R E S S S = 361

     void romReadSecondaryPhoneNumber(const String & output){return romRead2DigitsPerByte(361,output,12);}//sim phone 12 digits on 6 bytes

     void romUpdateSecondaryPhoneNumber(const String & input){romUpdate2DigitsPerByte(361,input,12);}

     bool romIsEnabledSmsAlsoAtSecondaryPhone(){

        byte flagsavailable=romReadRelaysFlag();

        flagsavailable>>=2;

        return flagsavailable%2>0;

     }

     void romSetEnabledSmsAlsoAtSecondaryPhone(bool doubledSms){

        byte oldflag=romReadRelaysFlag();

        oldflag<<=6;

        oldflag>>=6;

        byte newflag=doubledSms;

        newflag<<=2;

        newflag|=oldflag;

        romUpdateRelaysFlag(newflag);

     }

     //end eeprom at address=367(free) !!!!!!!!!!!


     

     

     //i dont know if better to have Arrays on end to save some bits for declaring addresses in flash (such as return EEPROM.read(355);)

     //meaning ..maybe crazy but if 355 wass less than 255(byte) ...if some earning somewhere.Anyway..i am leaving eeprom array in beginning to be easier to decide if i want something else more in EEPROM 

///////// END FUCTIONS BASIC IO-EEPROM ////////////////////////////////////////

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

//OTHER EPROM functions

    void romFlash(){

        ser.println(F("FLASING ROM !!!"));

        int i=0;

        while(i<360){

          EEPROM.update(i,0);

          ser.print(i);

          //happened on building to Flush on some 'normal execution'

          //!!!.....This an issue !!!!!!!!

          ser.println(strSOS());//so printing at least to see it on debug.....

          ++i;

        }

        ///////////////////////////////////////////////////////////////////////////

        EEPROM.update(0,181);

        

        lcd0RemoveAll();

        lcd0+=strDefPin();

        romUpdateSimPin(lcd0);

        lcd0RemoveAll();

        lcd0+=strDefPhone();

        romUpdateSmsPhoneNumber(lcd0);

        romUpdateSecondaryPhoneNumber(lcd0);

        romSetEnabledSmsAlsoAtSecondaryPhone(false);

        //Scales 4,3,2 disabled Scales 1,0 enabled ,dht enabled,Sim not need Pin in order to send sms

        //(user must have remove pin from his cellphone before place sim card in arduino)

        romUpdateFlagsByte(false,false,false,true,true,true,false);

        romUpdateRelaysFlag(HIGH,HIGH);

        romUpdateDhtType(11);//11 type according my library that i ve changed the private to public

        romUpdateHourDelayBeforeSMSwhile(0);

        romUpdateSimSecWaitAfterPowerOn(12);//for 'safety'...AT LEAST...YOU MUST ADJUST THIS WELL 

        romUpdateSimSecWaitBeforePowerOff(13);//for 'safety'...AT LEAST...YOU MUST ADJUST THIS WELL 

        romUpdateHoursSimulationPerDay(24);

        

        //here is my callibration for default (0) scale

        //scale 1 that is enabled will be uncallibrated and user will see just the readings from the HX711 amplifier(of load cell)

        romUpdateScaleStandartsUsing(0,5);

        romUpdateScaleGramsDif(0,-30);

            romUpdateScaleStandart(0,0,0);

            romUpdateScaleAverage(0,0,115315l);

            romUpdateScaleStandart(0,1,295);

            romUpdateScaleAverage(0,1,181679l);

            romUpdateScaleStandart(0,2,885);

            romUpdateScaleAverage(0,2,318885l);

            romUpdateScaleStandart(0,3,1775);

            romUpdateScaleAverage(0,3,525671l);

            romUpdateScaleStandart(0,4,3776);

            romUpdateScaleAverage(0,4,1002444l);

        //and this is just for be visibe some data from rom  

        romUpdateScaleGramsDif(1,11);

        romUpdateScaleGramsDif(2,22);

        romUpdateScaleGramsDif(3,-33);

        romUpdateScaleGramsDif(4,-44);

        ser.println(F("EEPROM FLASHED"));

    }

    void showReadEEPROM(){

        #ifdef DEBUG

        ser.print(F("id:"));ser.println(romReadProgramId());

        ser.print(F("dht:"));ser.println(isOnDht());

        int j=0,jl,i;

        while(j<ScalesCanHandle){

            ser.print(j);

            ser.print(F("#On: "));ser.print(isOnScale(j));

            ser.print(F("  tare:"));ser.println(romReadScaleGramsDif(j));

            jl=romReadScaleStandartsUsing(j);

            ser.print(F("Weights : "));ser.println(jl);

            i=0;

            while(i<jl){

                ser.print(i);ser.print(F(":"));ser.print(romReadScaleStandart(j,i));ser.print(F("->"));ser.println(romReadScaleAverage(j,i));

                ++i;

            }

          ++j;

        }

        String str;

        romReadSimPin(str);

        ser.print(F("PIN:"));

        ser.print(str);

        ser.print(F(" Need: "));

        ser.println(isSimNeedPin());

        

        

        strRemoveAll(str);

        romReadSmsPhoneNumber(str);

        ser.print(F("Phone:"));

        ser.println(str);

        strRemoveAll(str);

        romReadSecondaryPhoneNumber(str);

        if(romIsEnabledSmsAlsoAtSecondaryPhone()){ser.print(F("On "));}

        else{ser.print(F("Off "));}

        ser.print(F("2nd#"));

        ser.println(str);

        

        

        ser.print(F("On/Off:"));ser.println(romReadFlagsByte());

        ser.print(F("Relays:"));ser.println(romReadRelaysFlag());

        ser.print(F("DHT:"));ser.println(romReadDhtType());

        ser.print(F("DelayHours:"));ser.println(romReadHourDelayBeforeSMSwhile());

        ser.print(F("HoursPerDay"));ser.println(romReadHoursSimulationPerDay());

        ser.print(F("SecSimOn:"));ser.print(romReadSimSecWaitAfterPowerOn());

        ser.print(F(" Off:"));ser.println(romReadSimSecWaitBeforePowerOff());

        

        #endif

        

        

      }

     bool isSimNeedPin(){return (romReadFlagsByte())%2==1;}

     bool isOnDht(){return (romReadFlagsByte()>>1)%2==1;}

     bool isOnScale(int scaleId){return (romReadFlagsByte()>>(2+scaleId))%2==1;}

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

//logic funcs for relayed hardware


     void relaySensorsTrigger(){

        byte br=romReadRelaysFlag();

        digitalWrite(PinRelaySensor,br%2);

     }

     void relaySensorsUnTrigger(){

        byte br=romReadRelaysFlag();

        --br;

        digitalWrite(PinRelaySensor,br%2);

        digitalWrite(LED_BUILTIN,LOW);//ensuring saving battery 

     }

     void relaySimTrigger0(){

        byte br=romReadRelaysFlag();

        br>>=1;

        digitalWrite(PinRelaySim,br%2);

     }

     void relaySimUnTrigger0(){

        byte br=romReadRelaysFlag();

        br>>=1;

        --br;

        digitalWrite(PinRelaySim,br%2);

     }

void relaySensorsOpenNice(){

    relaySensorsTrigger();

    delay(3000);//for safety just because dht datasheet saying wait 1 sec before first read

    //with my first dht11 worked with 1000 but with the second module needs 2000!

    //Also here may take some readings ???.....in some version..

}

void relaySensorsCloseNice(){

    relaySensorsUnTrigger();

}

    

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

//WEIGHT LOAD SCALE SENSOR XH711 LOGIC 

    bool lastReadingOk=false;

    long lastReading;//what we have read from HX711 weight sensor

    float lastGrams; //register for translating the reading into Grams or whatever...Unit 

    long lastTenthOfGrams;//this is before cast to int ...?!?

    

    void roundLastGrams(){

      char roundingDirection=1;

      if(lastGrams<0.0){roundingDirection=-1;}

      lastGrams+= ( 0.5 * roundingDirection );

      lastTenthOfGrams=lastGrams;

      lastTenthOfGrams+=( 5 * roundingDirection );

      lastTenthOfGrams/=10;

    }

    /* AVOIDING use library for Flash economy saving 21 bytes and i copied the algorithm from https://github.com/bogde/HX711

    #include <HX711.h>

    HX711 scale0;//having this to test with mine //HX711 scale1;

    bool readWeightFromScale0(){

        scaleReadsOk=scale0.wait_ready_timeout(3000);

        if(scaleReadsOk){lastReading=scale0.read();//now 'safelly' attempt to read..translateByScale(0);}

        return scaleReadsOk;

    }*/

    bool readWeight(byte DOUT,byte PD_SCK,bool check=true) {

        lastReadingOk=false; lastReading=0x7FFFFE; lastGrams=0;

        uint8_t filler=0;

        int del=10;

        

        if(!check){

          //del=1;ser.println("not checking I MUST DECIDE HERE...uncomment and solve before release");

        }

        else{

          //ser.println("checking");

        }


        

        while(digitalRead(DOUT)==HIGH){

            ++filler;

            delay(del);

            if(filler>100){

                lastReadingOk=false; 

                //ser.println(F("error on reading scale (HX711)"));

                return lastReadingOk;

            }

        }

        //next i copied from library

        noInterrupts();

        // Pulse the clock pin 24 times to read the data.

        uint8_t data2 = shiftIn(DOUT, PD_SCK, MSBFIRST);

        uint8_t data1 = shiftIn(DOUT, PD_SCK, MSBFIRST);

        uint8_t data0 = shiftIn(DOUT, PD_SCK, MSBFIRST);

        //reading mode normal

        digitalWrite(PD_SCK, HIGH);

        digitalWrite(PD_SCK, LOW);

        interrupts();

        /*

         * https://cdn.sparkfun.com/datasheets/Sensors/ForceFlex/hx711_english.pdf

         * The output 24 bits of data is in 2’s complement

            format. When input differential signal goes out of

            the 24 bit range, the output data will be saturated

            at 800000h (MIN) or 7FFFFFh (MAX), until the

            input signal comes back to the input range

            8388608

         */

        //Replicate the most significant bit to pad out a 32-bit signed integer

        filler = 0x00;

        if (data2 & 0x80) {  filler = 0xFF;  } 

        // Construct a 32-bit signed integer

        unsigned long value = ( static_cast<unsigned long>(filler) << 24

            | static_cast<unsigned long>(data2) << 16

            | static_cast<unsigned long>(data1) << 8

            | static_cast<unsigned long>(data0) );

        //////////////////////////////////////

        lastReading= static_cast<long>(value);

        lastGrams=lastReading;

        lastReadingOk=!(lastReading==0 || lastReading==0x7FFFFF || lastReading==0x80);

        //if(!scaleReadsOk){ser.println(F("error"));}

        return lastReadingOk;

    }




    /*

     * TRANSLATING HX711 readings to GRAMS or Kg or whatever using Callibration values 

     */

    void translateByScale(byte scale,bool useTareAlso=true){

        //ser.print(F("Checking last Reading =  "));ser.println(lastReading);

        byte LoadsUsing=romReadScaleStandartsUsing(scale);

        if(LoadsUsing<2){

            //ser.println(F("Not callibrated return 0,leaving new users to callibrate and understand hx711"));

        }

        else{

            byte i=LoadsUsing-1;

            long av=romReadScaleAverage(scale,i);

            long avpreOrForZero;

            //ser.print(F("last average "));ser.println(av);

            if(lastReading<av){

                i=1;

                while(i<LoadsUsing){

                  av=romReadScaleAverage(scale,i);

                  //ser.println(i);ser.print(F("average "));ser.println(av);

                  if(av<lastReading){++i;}

                  else{//ser.print(F("breaking "));ser.println(i);

                    break;

                  }

                }

                if(i>1){//ser.print(F("Must decide to choose between i-1 and i "));

                    avpreOrForZero=romReadScaleAverage(scale,i-1);

                    if(  lastReading-avpreOrForZero  <   av-lastReading   ){

                        av=avpreOrForZero;

                        i--;

                        //ser.print(F("Much closer to previus"));

                    }

                    //ser.println(i);

                }

                //else{ser.println(F("Using first"));}

            }

            //else{ser.println(F("Using last"));}

            //ser.print(i);ser.print(F(" FINAL av "));ser.println(av);


            

            avpreOrForZero=romReadScaleAverage(scale,0);

            //x=(Rcurr-avZero) *( W /  [R1-avZero]   )

            lastGrams = lastReading;

            lastGrams -= avpreOrForZero;

            lastGrams *= romReadScaleStandart(scale,i);//NOT IN PREVIUS LINE to NOT OVERFLOW...

            lastGrams /= (av-avpreOrForZero);

            if(useTareAlso){

                lastGrams-=romReadScaleGramsDif(scale);

            }

        }

        

    }

    

    bool readWeight(byte scale,bool useTareAlso=true,bool translate=true,bool check=true){

        switch(scale){

            case 0:if(readWeight(PinHx0Dt,PinHx0Sck,check)){if(translate){translateByScale(0,useTareAlso);}}break;

            case 1:if(readWeight(PinHx1Dt,PinHx1Sck,check)){if(translate){translateByScale(1,useTareAlso);}}break;

            case 2:if(readWeight(PinHx2Dt,PinHx2Sck,check)){if(translate){translateByScale(2,useTareAlso);}}break;

            case 3:if(readWeight(PinHx3Dt,PinHx3Sck,check)){if(translate){translateByScale(3,useTareAlso);}}break;

            case 4:if(readWeight(PinHx4Dt,PinHx4Sck,check)){if(translate){translateByScale(4,useTareAlso);}}break;

         }

        return lastReadingOk;

    } 

    bool readWeightOnly(byte scale){return readWeight(scale,false,false);}


////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

//TEMPERATURE HUMIDITY SENSOR DHT LOGIC 

#include <DHT.h>


bool dhtAcceptableType(byte typeProposing){

    return  typeProposing == 11 || typeProposing==12 || typeProposing==21 || typeProposing==22 ;

}

DHT dht(PinDht,11);

float t=0,h=0;

void dhtRead(){t = dht.readTemperature(); h = dht.readHumidity();}

void printTempHum(const String & str){

  str+=t;

  //ser.println(t);ser.println(h);

  str+=strCelsiu();

  str+=strSpace();

  str+=h;

  str+=strPercent();

}



////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

//APPLICATION LOGIC for LCD MODE


////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

//BUTTONS

      unsigned long lastOkPressed;

      unsigned long lastMenuPressed;

      unsigned long lastRollPressed;

      bool RollJustClickedLessThan555(){return millis()-lastRollPressed<555;}

      bool OkJustClickedLessThan555(){return millis()-lastOkPressed<555;}

      

      bool OkPressed,OkPrevPressed;

      bool butOkJustPressed(){return OkPressed && (!OkPrevPressed);}

      bool butOkJustReleased(){return (!OkPressed) && OkPrevPressed;}

      

      

      bool MenuPressed,MenuPrevPressed;

      bool butMenuJustPressed(){return MenuPressed && (!MenuPrevPressed);}

      bool butMenuJustReleased(){return (!MenuPressed) && MenuPrevPressed;}

      

      bool RollPressed,RollPrevPressed;

      bool butRollJustPressed(){return RollPressed && (!RollPrevPressed);}

      bool butRollJustReleased(){return (!RollPressed) && RollPrevPressed;}



      //bool someButtonJustPressed;

      void readButtons(){

          OkPressed=!digitalRead(PinButOk);

          MenuPressed=!digitalRead(PinButMenu);

          RollPressed=!digitalRead(PinButRoll);


          

          

      }

      void storeButtons(){

          OkPrevPressed=OkPressed;

          MenuPrevPressed=MenuPressed;

          RollPrevPressed=RollPressed;

      }



      

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////


void setupLcd(){

    //ser.println(F("setupLcd(){"));

    lcd.begin();

    relaySensorsOpenNice();

    lcdQuickPrintAndRefresh(strJimakoskx());

    currentMenuChanged();

    

}

void loopLcd(){

    readButtons();

    if(butMenuJustPressed()){lastMenuPressed=millis();}

    else if(butMenuJustReleased()){

        if(millis()-lastMenuPressed<555){

            menuNext();

        }

        else{

          menuPrevius();

        }

    }

    else{

        switch(currMenu){

          case 0: LoopPasswordPinRequesting();break;

          case 1: LoopChangeHoursToDelayOnStartupSmsMode();break;//this no need pasword pin !!!

          case 2: LoopShowingSensors();break;

          case 3: LoopCallibratingSensors();break;

          case 4: LoopTaringSensors(); break;

          case 5: LoopOnOffSensorsAndDht();break;

          case 6: LoopChangingPinPhoneAndIfSimNeedsPin();break;

          case 7: LoopChangingSecondaryPhone();break;

          case 8: LoopChangeRelaysTriggerType();break;

          case 9: LoopChangeDhtType();break;

          case 10: LoopTestSms();break;

          case 11: LoopChangeSimSeconds();break;

          case 12: LoopSignalingSimCard();break;

          default: LoopChangeHoursToDelayOnStartupSmsMode();break;  

        }

    }

    storeButtons();

    delay(40);

}

void currentMenuChanged(){

    relaySimUnTrigger0();//because of 11 menu -signaling ...

    lcd.noBlink();

    lcd01RemoveAll();

    switch(currMenu){

      case 0: prepareLoopPasswordPinRequesting();break;

      case 1: prepareLoopChangeHoursToDelayOnStartupSmsMode();break;//this no need pasword pin !!!On beginning needPasword and currMenu = 1 =this to quick delay before mode sms on the field

      case 2: prepareLoopShowingSensors();break;

      case 3: prepareLoopCallibratingSensors();break;

      case 4: prepareLoopTaringSensors();break;

      case 5: prepareLoopOnOffSensorsAndDht();break;

      case 6: prepareLoopChangingPinPhoneAndIfSimNeedsPin();break;

      case 7: prepareLoopChangingSecondaryPhone();break;

      case 8: prepareLoopChangeRelaysTriggerType();break;

      case 9: prepareLoopChangeDhtType();break;

      case 10: prepareLoopTestSms();break;

      case 11: prepareLoopChangeSimSeconds();break;

      case 12: prepareLoopSignalingSimCard();break;

      default: prepareLoopChangeHoursToDelayOnStartupSmsMode();break;

    }

    lcdClearRefresh();

}

void menuNext(){

    ++currMenu;

    if(needPassword){currMenu%=2;}

    else{

        currMenu%=13;//unlocked menu count

        if(currMenu==0){++currMenu;}//bypass entering pin menu

    }

    currentMenuChanged();

}

void menuPrevius(){

  if(!needPassword){

      if(currMenu<2){currMenu=12;}

      else{ --currMenu; }

      currentMenuChanged();

  }

}

void done(){

    lcd.setCursor(0,0);

    lcd.print(strDone());

    delay(981);

    menuNext();

}

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

//APPLICATION LOGIC for LCD MODE (harder/main to understand if you are new)

long loopCounter=0;

char rchmin,rchmax;//,rinc0,rinc1,rmax0,rmax1,rmin0,rmin1;

char rinc[2];char rmin[2];char rmax[2];

const String strTemp;//only for editing


void increasingCurrEditingIndex(){

  cursCol+=rinc[cursRow];//void lcdColInc

  if(cursCol>rmax[cursRow]){//>max

    if(changingLines){++cursRow;cursRow%=2;}

    cursCol=rmin[cursRow];//set as min

  }

}

void switchingDigit(){

    if(ch>=rchmin && ch<=rchmax){  //switching Digit

        ++ch;if(ch>rchmax){ch=rchmin;}

        strTemp+=ch;

    }

  

}

void copyAndPrintCharsFromTemp(const String & target){

  int i=0;

  int l=strTemp.length();

  int targetPos=cursCol;

  while(i<l){

    ch=strTemp.charAt(i);

    lcd.print(ch);

    target.setCharAt(targetPos,ch);

    ++targetPos;

    ++i;

  }

}

void increasingCurrEditingChar(){

  //I dont know how to de-reference this String!!!! hahahah !!!..programmer for C++ need!!!not from java ...sol SoftwareSerial *fonaSerial = &fonaSS;

  bool rowOne=cursRow>0;

  if(rowOne){ch=lcd1.charAt(cursCol);}

  else{ ch=lcd0.charAt(cursCol);}

  

  strRemoveAll(strTemp);

  if(currMenu<10){

      if(ch=='n'){strTemp+='f';}//switching On-Off

      else if(ch=='f'){strTemp+='n';}

      else if(ch=='H'){strTemp+=strLow();}//switching High-Low

      else if(ch=='L'){strTemp+=strHigh();}

      else {switchingDigit();}

  }

  else{switchingDigit();

  }//else{ser.println(F("Char not changing"));}

  

  copyAndPrintCharsFromTemp((rowOne)?(lcd1):(lcd0));

}

//util to have menu function in a pointer instead of if else 

typedef void (*SomeVoidFunction) ();

void checkButtonsAndActIfOk(SomeVoidFunction action); // prototype

void checkButtonsAndActIfOk(SomeVoidFunction action){

   if(butOkJustPressed()){lastOkPressed=millis();}

   if(butRollJustPressed()){lastRollPressed=millis();}

   

   if(butRollJustReleased()){

        //ser.println(F("rolling"));

        if(RollJustClickedLessThan555()){//ser.println(F("increasingCurrEditingChar()"));

          increasingCurrEditingChar();

        }

        else{//ser.println(F(" increasingCurrEditingIndex()"));

          increasingCurrEditingIndex();

        }

        lcdResetCursor();

    }

    else if(butOkJustReleased()){

        action();

    }  

}  

void prepareForEdit(const __FlashStringHelper *quick,

const char chMin,const char chMax,const boolean canChangeLines,

const byte colInc0,const byte colInc1,const byte colMax0,const byte colMax1,const byte colMin0,const byte colMin1,

const byte row,const byte col

){

  

  rchmin=chMin;

  rchmax=chMax;

  lcdQuickPrintAndRefresh(quick);

  rinc[0]=colInc0;

  rinc[1]=colInc1;

  lcd01RemoveAll();

  rmax[0]=colMax0;

  rmax[1]=colMax1;

  changingLines=canChangeLines;

  rmin[0]=colMin0;

  rmin[1]=colMin1;

  lcd.blink();

  cursRow=row;

  cursCol=col;

}

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

//EDITING -'SIMPLE LOGIC'- APPLICATION LOGIC (for lcd mode)

const __FlashStringHelper* strWrongPin(){return F("Wrong pin");}

const __FlashStringHelper* strEnterPassword(){return F("Enter Password");}

void prepareLoopPasswordPinRequesting(){

  prepareForEdit(strEnterPassword(),'0','9',false,1,1,3,3,0,0,0,0);

  lcd0+=strDefPin();

  lcd1+=strEnterPassword();

}

void LoopPasswordPinRequesting(){checkButtonsAndActIfOk(OnokPasswordPinRequesting);}

void OnokPasswordPinRequesting(){

        String pin;

        romReadSimPin(pin);

        int i=0;

        while(i<4){//ser.print(pin.charAt(i));ser.print("-");ser.println(cursorRef.charAt(i));

            if(pin.charAt(i)!=lcd0.charAt(i)){i=5;}

            ++i;

        }

        needPassword=i>4;

        if(needPassword){//ser.println(F("Wrong"));

          lcd.setCursor(0,1);

          lcd.print(strWrongPin());

          lcdResetCursor();

        }

        else{//ser.println(F("pin unlocked"));

          ++currMenu;//going in directly on showing and not in the next that is to set the hours delay

          done();

        }  

}


const __FlashStringHelper* strHourDelayBeforeStart(){return F("00HourDelBefStart");}

const __FlashStringHelper* strHoursSimaulateADay(){return F("24HoursSimulADay");}


void prepareLoopChangeHoursToDelayOnStartupSmsMode(){

      prepareForEdit(F(""),'0','9',true,1,1,1,1,0,0,0,0);

      //lcd0+=(romReadHourDelayBeforeSMSwhile());

      lcd0+=strHourDelayBeforeStart();

      byte val=romReadHourDelayBeforeSMSwhile();

      char ch=val%10;

      ch+=48;

      lcd0.setCharAt(1,ch);

      val/=10;

      ch=val%10;

      ch+=48;

      lcd0.setCharAt(0,ch);

      

      lcd1+=strHoursSimaulateADay();

      val=romReadHoursSimulationPerDay();

      ch=val%10;

      ch+=48;

      lcd1.setCharAt(1,ch);

      val/=10;

      ch=val%10;

      ch+=48;

      lcd1.setCharAt(0,ch);

      

}

void LoopChangeHoursToDelayOnStartupSmsMode(){checkButtonsAndActIfOk(OnokChangeHoursToDelayOnStartupSmsMode);}

void OnokChangeHoursToDelayOnStartupSmsMode(){

      byte HourDel=parselong0p(lcd0,0,2);//[0,99] hours

      ser.print(strHourDelayBeforeStart());ser.println(HourDel);

      romUpdateHourDelayBeforeSMSwhile(HourDel);

      

      byte simulationMode=parselong0p(lcd1,0,2);//

      if(simulationMode==0){

        simulationMode=24;

      }

      ser.print(strHoursSimaulateADay());ser.println(simulationMode);

      romUpdateHoursSimulationPerDay(simulationMode);

      computeHourMillisBySimulationMode();

      done();  

}

unsigned long computeHourMillisBySimulationMode(){

      unsigned long out=3600000ul;

      byte simulationMode=romReadHoursSimulationPerDay();

      if(simulationMode>0){

          //1 day is simulation mode hours

          out/=24;

          out*=simulationMode;

          //ser.print(F("Simulation hour millis "));ser.print(out);

      }

      return out;

}








void prepareLoopChangeSimSeconds(){

      prepareForEdit(F(""),'0','9',true,1,1,1,1,0,0,0,0);

      byte val=romReadSimSecWaitAfterPowerOn();

      if(val<10){

        lcd0+='0';

      }

      lcd0+=val;

      lcd0+=F(" SecAftSimOn");

      

      val=romReadSimSecWaitBeforePowerOff();

      if(val<10){

        lcd1+='0';

      }

      lcd1+=val;

      lcd1+=F(" SecBefSimOff");

}

void LoopChangeSimSeconds(){checkButtonsAndActIfOk(OnokChangeSimSeconds);}

void OnokChangeSimSeconds(){

      byte val=parselong0p(lcd0,0,2);//[0,9] hours

      romUpdateSimSecWaitAfterPowerOn(val);

      val=parselong0p(lcd1,0,2);//[0,9] hours

      romUpdateSimSecWaitBeforePowerOff(val);

      --currMenu;

      done();

  

  

}

// Change PIN and if must use pin before send SMS(or else job with mobile card) .(The PIN of installed mobile card)

//WARNING .PIN of card will be used also as PASSWORD for entering LCD EDIT mode......to avoid some stranger....doing things...

// Chage the TELEPHONE NUMBER on witch we SENDING SMS (not the number of this card installed on SIM800L)

void prepareLoopChangingPinPhoneAndIfSimNeedsPin(){

      prepareForEdit(F("Pin&Phone"),'0','9',true,1,1,14,11,9,0,0,9);

      String pin("");

      romReadSimPin(pin);

      lcd0+=F("Use Pin Of ");

      lcd0+=pin;

      if(isSimNeedPin()){setOnCharAt(9,lcd0);}

      romReadSmsPhoneNumber(lcd1);

}

void LoopChangingPinPhoneAndIfSimNeedsPin(){checkButtonsAndActIfOk(OnokChangingPinPhoneAndIfSimNeedsPin);}

void OnokChangingPinPhoneAndIfSimNeedsPin(){  

       byte flagsbyte=romReadFlagsByte();

       flagsbyte>>=1;

       flagsbyte<<=1;

       flagsbyte+=lcd0.charAt(9)=='n';

       romUpdateFlagsByte(flagsbyte);

       

       String pin("");

       int i=11;

       while(i<15){

          pin+=lcd0.charAt(i);

          ++i;

       }

       romUpdateSimPin(pin);

       romUpdateSmsPhoneNumber(lcd1);//hm!

       

       currMenu--;

       done();

}


void prepareLoopChangingSecondaryPhone(){

    prepareForEdit(F("2nd Phone"),'0','9',false,1,0,14,0,1,0,0,1);

    String secondaryPhone("");

    romReadSecondaryPhoneNumber(secondaryPhone);

    lcd0+="Of ";

    lcd0+=secondaryPhone;

    if(romIsEnabledSmsAlsoAtSecondaryPhone()){setOnCharAt(1,lcd0);}

    lcd1+="2nd# Hold2Switch";

}

void LoopChangingSecondaryPhone(){checkButtonsAndActIfOk(OnokChangingSecondaryPhone);}

void OnokChangingSecondaryPhone(){  


    if(OkJustClickedLessThan555()){

        //ser.println("ok");

        romSetEnabledSmsAlsoAtSecondaryPhone(lcd0.charAt(1)=='n');

        lcd0.remove(0,3);

        ser.println(lcd0);

        romUpdateSecondaryPhoneNumber(lcd0);

        

    }

    else{

      //ser.println("switching phones meaning that the user is the secondary phone and wants to test with HIS phone and not with his partner that in this time is NOT the LCD user");

      String primaryPhone("");

      String secondaryPhone("");

      romReadSmsPhoneNumber(primaryPhone);

      romReadSecondaryPhoneNumber(secondaryPhone);

      romUpdateSmsPhoneNumber(secondaryPhone);

      romUpdateSecondaryPhoneNumber(primaryPhone);

    

    }

    currMenu-=2;//go back to see the 1st phone number just to avoid confusion

    done();

    

}









//On#0 Of#1 Of#2

//Of#3 Of#4 On#dht

void prepareLoopOnOffSensorsAndDht(){

  prepareForEdit(F(""),'0','9',true,5,5,11,11,1,1,0,1 );

  lcd0+=F("Of#0 Of#1 Of#2  ");

  lcd1+=F("Of#3 Of#4 Of#dht");

  byte flagsbyte=romReadFlagsByte();//on first bit is if sim need pin

  flagsbyte>>=1;if(flagsbyte%2>0){setOnCharAt(11,lcd1);}

  flagsbyte>>=1;if(flagsbyte%2>0){setOnCharAt(1,lcd0);}

  flagsbyte>>=1;if(flagsbyte%2>0){setOnCharAt(6,lcd0);}

  flagsbyte>>=1;if(flagsbyte%2>0){setOnCharAt(11,lcd0);}

  flagsbyte>>=1;if(flagsbyte%2>0){setOnCharAt(1,lcd1);}

  flagsbyte>>=1;if(flagsbyte%2>0){setOnCharAt(6,lcd1);}

  

}

void LoopOnOffSensorsAndDht(){checkButtonsAndActIfOk(OnokOnOffSensorsAndDht);}

void OnokOnOffSensorsAndDht(){

       romUpdateFlagsByte(

        lcd1.charAt(6)=='n'//#4

        ,lcd1.charAt(1)=='n'//#3

        ,lcd0.charAt(11)=='n'//#2

        ,lcd0.charAt(6)=='n'//#1

        ,lcd0.charAt(1)=='n'//#0

        ,lcd1.charAt(11)=='n'//dht

        ,isSimNeedPin());

       

       currMenu--;

       done();

}

// LOW or HIGH Triggering type for Relay of Sensors

// LOW or HIGH Triggering type for Relay of SIM card-chip ... sim800l 

void prepareLoopChangeRelaysTriggerType(){

  prepareForEdit(F(""),'0','9',true,1,1,0,0,0,0,0,0 );

  

  byte flagsbyte=romReadRelaysFlag();

  if(flagsbyte%2>0){lcd0+=strHigh();}

  else {lcd0+=strLow();}

  lcd0+=F("RelSensors");

  

  flagsbyte>>=1;

  if(flagsbyte%2>0){lcd1+=strHigh();}

  else {lcd1+=strLow();}

  lcd1+=F("Sim Relay");

}

void LoopChangeRelaysTriggerType(){checkButtonsAndActIfOk(OnokChangeRelaysTriggerType);}

void OnokChangeRelaysTriggerType(){

       

       romUpdateRelaysFlag(lcd1.charAt(0)=='H',lcd0.charAt(0)=='H');

       relaySensorsTrigger();//ONLY relay of SENSORs on lcd mode

       relaySimUnTrigger0();//No SIM on when lcd mode

       currMenu--;

       done();

}

//(11,12,21,22)...default 11

void prepareLoopChangeDhtType(){

  prepareForEdit(F(""),'1','2',false,1,1,1,1,0,0,0,0 );

  lcd0+=dht._type;

  lcd1+=F("DHT?11,12,21,22");

  

}

void LoopChangeDhtType(){checkButtonsAndActIfOk(OnokChangeDhtType);}

void OnokChangeDhtType(){

       

       dht._type=parselong0p(lcd0,0,2);

       romUpdateDhtType(dht._type);

       dht.begin();

       done();  

}




void prepareLoopTestSms(){

      prepareForEdit(F("Test Sms"),' ' ,'z',false,1,1,15,15,0,0,1,0);

      lcd0+=F("TestSim&Rep&Sms");

      lcd1+=F("TextRoll&OkClear");

}

void LoopTestSms(){checkButtonsAndActIfOk(OnokTestSms);}

void OnokTestSms(){

    long nowTime=millis();

    long dif=nowTime-lastOkPressed;

    if(RollPressed){//if have also beyond Ok button ...the rolll  button....clearing sms string 

      

        lcd1RemoveAll();

        lcd1SetCursor(0);

        lcd.print(strSpace16());

        lcd1+=strSpace16();

        lcd1SetCursor(cursCol);

    }

    else{


      String str;//is this safe dynamic allocation????

      str+=lcd1;

      lcd01RemoveAll();

      

      byte state=0;

      if(OkJustClickedLessThan555()){

          //lcd0+=F("sms .. ing "); 

          lcd1+=F("Pin&Batt&Call");

      }

      else if(millis()-lastOkPressed>2000){

          lcd0+=F("sms .. ing "); 

          lcd1+=F("lcd1");

          state=2;

      }

      else{

          lcd0+=F("sms .. ing "); 

          lcd1+=F("FullSensorReport");

          state=1;

      }

      //notify user whitch action we are going to take

      lcdQuickPrintAndRefresh(F("sms"));

      

      if(state<1){

          bool pinMatchToSim=relaySimOpenNice();

          //ser.println("sim opened");


          int i=0,l=0;

          String str("");

          lcd01RemoveAll();

          


          if(pinMatchToSim){

            if(isSimNeedPin()){

                lcd0+=F("Pin ok");

            }

            else{

              lcd0+=F("Unused PIN");

            }

          }

          else{

            lcd0+=strWrongPin();

          }


          sim.println("AT+CBC");

          delay(100);

          while(sim.available()>0){str+=((char)sim.read());delay(10);}


          #ifdef DEBUG

          ser.println(str);

          #endif

          

          i=0;

          l=str.length();

          bool found=false;

          while(i<l){

            ch=str.charAt(i);

            //ser.print(i);ser.print(' ');ser.print(ch);ser.print(' ');ser.println((int)ch);

            if(ch==':'){found=true;}

            else if((ch==13||ch==10) && found){break;}

            else{if(found){lcd1+=ch;}}

            ++i;

          }


          

          lcdClearRefresh();

          

          if(pinMatchToSim){

              callAndHangUpAfterSeconds(15);

              relaySimCloseNice();

          }

          else{

            relaySimUnTrigger0();

            delay(4000);

          }

          

          //

          

          //ser.println("end calling"); 

                

      }

      else if(state>1){

        sendSms(str,false);//send what the user has on 2nd row of lcd (2x16)

        //but we are loosing previus...any way..better to user indertand that every button press is an SMS

        //strRemoveAll(str);////for....is this safe dynamic allocation????

      }

      else {

        //str here is not being used.TRUE is changing what printing.

        //Its just a trick to decide if sms some String(on false) or if sms readings(on true)

        sendSms(str,true);//send Initial SMS Full report of sensors

      }


      

      --currMenu;

      done();

    }

}














////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

//ACTION LOGIC for Showing Dht Temperature,Humidity,  HX711 Weight from Scale (grams ( Kg , g )or else defined by translations)

byte currScale;

void takeFullReadingsAndPrint(const SoftwareSerial & output){//this is called from setup()....

  if(isOnDht()){

      dhtRead();

      output.print(t);

      output.print(strCelsiu());

      output.print(strSpace());

      output.print(h);

      output.println(strPercent());

  }

  else{output.println(strOff());}

  currScale=0;

  while(currScale<ScalesCanHandle){

      output.print(currScale);

      output.print('#');

      if(isOnScale(currScale)){

          if(readWeight(currScale)){

            output.print(lastGrams);

            output.print(strLess());

            output.print(lastReading);

            roundLastGrams();

            output.print(F("/10="));output.print(lastTenthOfGrams);

          }

          else{output.print(strErr());}

      }

      else{output.print(strOff());}

      output.println();

      ++currScale;

   }

}

//This is not about outputing....Only for storing in ram supposing sensors powered on....

void storeIterReadings(){

    if(isOnDht()){

        dhtRead();

        if(isnan(t)){

          lastTenthOfGrams=127;

        }//error ...means not showing on sms

        else{

          lastTenthOfGrams=t;

        }

        temperature[iter]=lastTenthOfGrams;

        #ifdef DEBUG

            ser.print(lastTenthOfGrams);ser.print(' ');

        #endif  

        

        if(isnan(h)){

          lastTenthOfGrams=127;

        }//error ...means not showing on sms

        else{

          lastTenthOfGrams=h;

        }

        humidity[iter]=lastTenthOfGrams;

        #ifdef DEBUG

            ser.print(lastTenthOfGrams);ser.print(' ');

        #endif  

    }

    else{

        temperature[iter]=-128;//disabled ...means not showing on sms

        humidity[iter]=-128;//disabled ...means not showing on sms


    }


      

    char roundingDirection;

    currScale=0;

    int posOfFreshReading=iter * ScalesCanHandle;

    int half=SensationsPerSms*ScalesCanHandle +posOfFreshReading;

    while(currScale<ScalesCanHandle){

        if(isOnScale(currScale)){

            if(readWeight(currScale)){

                #ifdef DEBUG

                ser.print(lastGrams);ser.print(strLess());ser.print(lastReading);

                #endif

                roundLastGrams();

                #ifdef DEBUG

                ser.print(F("/10="));ser.println(lastTenthOfGrams);

                #endif

            }

            else{

              #ifdef DEBUG

              ser.print(strErr());

              #endif

              lastTenthOfGrams=ERROR_WEIGHT_SCALE_VALUE;

            }

        }

        else{

          #ifdef DEBUG

          ser.print(strOff());

          #endif

          lastTenthOfGrams=DISABLED_WEIGHT_SCALE_VALUE;

        }

        StoredWeights[half]=StoredWeights[posOfFreshReading];//store previus

        StoredWeights[posOfFreshReading]=lastTenthOfGrams;

        ++half;

        ++posOfFreshReading;

        ++currScale;

     }

     

     #ifdef DEBUG

     ser.println();

     #endif

     

     ++iter;

     iter%=SensationsPerSms;

}









////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

//more menus


byte showEveryCycles=100;

bool showingReadingsAlso=false;


const __FlashStringHelper* strSignaling(){return F("Signaling");}

bool nowSignaling;

void prepareLoopSignalingSimCard(){

          nowSignaling=false;

          lcd1+=strSignaling();

          //lcdQuickPrintAndRefresh(strSignaling());

          //ser.println("sim opened");



}


void LoopSignalingSimCard(){

    if(!nowSignaling){

        if(butOkJustReleased()){

          loopCounter=0;

          lcd0+=F("Connecting");

          lcdClearRefresh();

          relaySimOpenNice();

          nowSignaling=true;

        }

    }


    if(nowSignaling && (loopCounter==0)){//ser.println(F("cpu cycle showing "));

          lcd0RemoveAll();


          String str("");

          sim.println("AT+CSQ");

          delay(100);

          while(sim.available()>0){str+=((char)sim.read());delay(10);}

          

          #ifdef DEBUG

          ser.println(str);

          #endif

          

          int i=0;

          int l=str.length();

          bool found=false;

          while(i<l){

            ch=str.charAt(i);

            //ser.print(i);ser.print(' ');ser.print(ch);ser.print(' ');ser.println((int)ch);

            if(ch==':'){found=true;}

            else if((ch==13||ch==10) && found){break;}

            else{if(found){lcd0+=ch;}}

            ++i;

          }

          l=parselong0p(lcd0,1,-1);

          //ser.print("l=");ser.println(l);


          

          if(l==99){

              lcd0+=F(" Unknown");//or unknown

          }

          else if(l<10){

              lcd0+=F(" Low");

          }



          lcdClearRefresh();

        

    }

    ++loopCounter;

    loopCounter%=showEveryCycles;

  

}




void prepareLoopShowingSensors(){

  lcdQuickPrintAndRefresh(F("Showing"));

  currScale=0;

}

void LoopShowingSensors(){

   if(butOkJustReleased()){

            ++currScale;

            currScale%=3;//pseydo curr scale here...just 3 paisrs of sensors

            loopCounter=0;

            //ser.println(F("next pair to show"));

    }

    else if(butRollJustReleased()){

      showingReadingsAlso=!showingReadingsAlso;

      loopCounter=0;

    } 

    if(loopCounter==0){//ser.println(F("cpu cycle showing "));

      lcd01RemoveAll();

      if(currScale==0){showingReadingsOfDhtAndScale0();}

      else if(currScale==1){showingReadingsOfScales(1,2);}

      else{showingReadingsOfScales(3,4);}

      lcdClearRefresh();

    }

    ++loopCounter;

    loopCounter%=showEveryCycles;

}

void showingReadingsOfDhtAndScale0(){

      dhtRead();

      printTempHum(lcd0);

      showingReadingsForScale(0,lcd1);

}

void showingReadingsOfScales(int scalex,int scaley){

  showingReadingsForScale(scalex,lcd0);

  showingReadingsForScale(scaley,lcd1);

}

void showingReadingsForScale(int scale,const String & str){

    readWeight(scale,true,true,false);//tare,translate but not check for errors because getting slow?????last bool on function for this reason i added

    str+=(int)lastGrams;

    if(showingReadingsAlso){

      str+=strLess();

      str+=lastReading;

    }

}

void lcdShowScaleOutOfOrder(){

  //#ifdef DEBUG

  //ser.println(F("Scale out of order"));

  //#endif

  lcdQuickPrintAndRefresh(F("Sensor ERROR"),666);

}

void prepareLoopTaringSensors(){

  //lcdQuickPrintAndRefresh(F("Taring"));

  currScale=0;

  refreshTareOnlcd0();

}

void refreshTareOnlcd0(){

  lcd0RemoveAll();

  lcd0+=currScale;

  lcd0+=F("# Tare ");

  lcd0+=romReadScaleGramsDif(currScale);

}

void LoopTaringSensors(){

   if(butRollJustPressed()){lastRollPressed=millis();}

   else if(butOkJustPressed()){lastOkPressed=millis();}

   else if(butRollJustReleased()){

      loopCounter=romReadScaleGramsDif(currScale);

      if(RollJustClickedLessThan555()){loopCounter+=10;}

      else{loopCounter-=10;}

      romUpdateScaleGramsDif(currScale,loopCounter);

      refreshTareOnlcd0();

      loopCounter=0;

   }

   else if(butOkJustReleased()){

      if(OkJustClickedLessThan555()){

          ++currScale;

          currScale%=5;

          loopCounter=0;

          refreshTareOnlcd0();

          //ser.println(F("next scale"));

      }

      else{

          if(readWeightOnly(currScale)){

              //ser.println(F("Now taring supposing scale is empty.We will find the average first.Then translate without use existing tare.And set this Grams result as Tare"));

              

              lastReading=takeAverage();

              translateByScale(currScale,false);

              romUpdateScaleGramsDif(currScale,lastGrams);

              //ser.print(lastReading);ser.print(F("=Average mean Grams(new Tare) -> "));ser.println(lastGrams);            

              loopCounter=0;

              refreshTareOnlcd0();

          }

          else{

            lcdShowScaleOutOfOrder();

          }

          

      }

    } 

    if(loopCounter==0){//ser.println(F("this cpu cycle is that we are reading pair of sensor and showing "));

      lcd1RemoveAll();

      showingReadingsForScale(currScale,lcd1);

      lcdClearRefresh();

    }

    ++loopCounter;

    loopCounter%=showEveryCycles;

}

//0.Showing number of callibrated values for each scale 

//1.Callibration for specific scale starts and asking how many callibrating values we want.(How many known weights we have to take average of them)

//////// must be at least 1 up to 7  (+1 the average for zero =8 values on rom)

//2.Now Callibration will take the average of zero ,reminding you to have empty the scale before press ok

//3.For each standart [1,7] asking to enter the Grams in order to start taking average

//4.Finishing Callibration,checking and updating rom if all good.

void refreshCallibratingSensors(){

  lcd01RemoveAll();

  iTemp33(romReadScaleStandartsUsing(currScale));

  lcd0+=currScale;

  lcd0+=F("# ");

  if(iTemp33()>0){

        lcd0+=romReadScaleAverage(currScale,iTemp32());

        

        lcd1+=iTemp33();

        lcd1+=F("/");

        lcd1+=(iTemp32()+1);

        lcd1+=F(" ");

        lcd1+=romReadScaleStandart(currScale,iTemp32());

  }

  else{

    lcd0+=F("Uncallibrated");

    lcd1+=F("Hold Ok To Start");

  }

  lcdClearRefresh();

}

void prepareLoopCallibratingSensors(){

  lcdQuickPrintAndRefresh(F("Callibrating"));

  currScale=0;//for scale

  loopCounter =0;//for just showing callibration values .....to be prettier but needs to remind to user that if hold key longer than 555 millis callibration process will start

  iTemp32(0);//the index that we are showing / or sampling

  iTemp33();//the index we are storing currentScaleSamples / requestedScaleSamples

  iTemp34(0);//current Callibration Step

  iTemp35();//

  

  refreshCallibratingSensors();

}

void LoopCallibratingSensors(){

    switch(iTemp34()){

      case 0: LoopCallibratingSensors_Step_0();break;

      case 1: LoopCallibratingSensors_Step_1();break;

      case 2: LoopCallibratingSensors_Step_2();break;

      case 3: LoopCallibratingSensors_Step_3();break;

      //default: LoopCallibratingSensors_Step_0();break;//50 flash bytes this default!!

    }

}

void LoopCallibratingSensors_Step_0(){

   //ser.println(F("_Step_0-Showing (if have) Callibration values of current scale.Press Ok for next scale()"));

   if(butOkJustPressed()){lastOkPressed=millis();}

   else if(butOkJustReleased()){

      if(OkJustClickedLessThan555()){

          ++currScale;

          currScale%=5;

          iTemp32(0);//start from beginning the standartsUsing/currentShow

          refreshCallibratingSensors();

          //ser.println(F("next scale"));

      }

      else{

          readWeight(currScale);

          //ser.print(lastGrams);ser.print(F(" < "));ser.println(lastReading);

          if(lastReadingOk){

              //ser.println(F("Now Callibrating"));

              iTemp34(iTemp34()+1);

              

              //clearing  to Zero temp Storage for standarts and averages because on finishing we will STORE on EPROM all 1+(8*2) values 

              int i=0;while(i<8){setTempStandart(i,0);setTempAverage(i,0);++i;}

              

              prepareForEdit(strStep1(),'1','7',false,1,1,0,0,0,0,0,0 );

              lcd0+=F("1 known weights");

              lcd1+=F("ScaleIsEmpty..Ok");

              lcdClearRefresh();

              return;

          }

          else{

            

            lcdShowScaleOutOfOrder();

          }

          

      }

    } 

    if(loopCounter==0 && iTemp33()>0){

          refreshCallibratingSensors();

          int ttt=iTemp32();

          ttt++;

          ttt%=iTemp33();

          iTemp32(ttt);//next pair of of standart-average in rom

    }

    ++loopCounter;

    loopCounter%=50;  

}

void LoopCallibratingSensors_Step_1(){

    checkButtonsAndActIfOk(OnOkToProccedFromStep1);

}

void OnOkToProccedFromStep1(){

  //ser.println("procceding to 2");

  iTemp34(iTemp34()+1);

  

  iTemp32(0);//

  iTemp33(lcd0.charAt(0)-47);//+1 for zero,,here is what how much known weight the user have

  //ser.println(iTemp33());

  //taking zero average here

  

      long average=takeAverage();//the 

      setTempStandart(0,0);

      setTempAverage(0,average);

  

  

  iTemp32(iTemp32()+1);

  prepareForEdit(strStep1(),'0','9',false,1,1,9,0,4,0,0,9 );

  lcd1+=F("Weight Is Up..Ok");

  resetlcd0fprcallstep();

}

void resetlcd0fprcallstep(){

  lcd0RemoveAll();

  lcd0+=(iTemp32()+1);

  lcd0+=F("/");

  lcd0+=iTemp33();

  lcd0+=F(":");

  lcd0+=F("000000 grams");

  //starting with 1 here..not zero...thats why 10-iTemp32() = for sure 9 index on lcd of 16 columns

  cursCol=10-iTemp32();if(cursCol<4){cursCol=4;}//reming user to ...put bigger weights even if we are sorting at the end...

  lcdClearRefresh();

}

void LoopCallibratingSensors_Step_2(){

    //ser.println(F("_Step_2()"));

    checkButtonsAndActIfOk(OnOkToProccedFromStep2);

}

void OnOkToProccedFromStep2(){

  long gramsKnowned=parselong0p(lcd0,4,-1);

  //ser.println(gramsKnowned);

  

  #ifdef DEBUG

  ser.println(lcd0);

  #endif

  

  int index=iTemp32();

  

  //#ifdef DEBUG

  //ser.print(index);ser.print(F(" Try Averaging for : "));ser.println(gramsKnowned);

  //#endif

  

  //checking if user already took average for such weight 

  int i=0;

  while(i<index){if(gramsKnowned==getTempStandart(i)){break;}++i;}

  if(i<index){

      //ser.println(F("Already have this standard weight"));

      lcdQuickPrintAndRefresh(F("WeightAlready In"),666);

  }

  else{

      //taking known weights average here

      long average=takeAverage();

      setTempStandart(index,gramsKnowned);

      setTempAverage(index,average);

      

      

      iTemp32(index+1);//increasing to next known weight   

      if(iTemp32()<iTemp33()){

          resetlcd0fprcallstep();

      }

      else{iTemp34(iTemp34()+1);}//skipping step if finish taking averages of all known weights...and going to finilize this callibration

  }

}

long minv=2147483646;

long maxv=-2147483647;

void printCallibrationAverageProcess(){

    lcd.clear();lcd.print(iter);lcd.print(F("/"));lcd.print(100);lcd.print(F(" "));lcd.print(t);

    lcd.setCursor(0,1);lcd.print(minv); lcd.print(F(","));lcd.print(maxv);

}

//HX711 sensor amplifier can take 10 readings per second?

long takeAverage(){

    //we already check that sensor works...

    //sum2 dividing every time by 2..Use only this?

    t=1;//avoiding devide by zero?....(but adding 1 uniti of scale HX711 reading) to sum

    if(readWeightOnly(currScale)){

        t=lastReading;

    }

    h=0;//total sum

    

    byte errors=0;

    byte goods=0;

    iter=0;

    byte samples=100;

    minv=2147483646;

    maxv=-2147483647;

    readWeightOnly(currScale);//no need for translation now

    

    

    

    while(iter<samples){

      if(iter%10==0){

        printCallibrationAverageProcess();

        //delay(2000);//have this to further slow down sensor....

      }

      delay(123+iter);//~5 readings per second

      

      if(readWeightOnly(currScale)){

        t+=lastReading;//continiuslly have middle from the time i was thinking that float in arduino overflows long max!!!          

        t/=2.0;

        

        h+=lastReading;

        

        if(lastReading<minv){minv=lastReading;}

        if(lastReading>maxv){maxv=lastReading;}

        ++goods;

        //ser.print(iter);ser.print(F(": "));ser.print(lastReading);ser.print(F(" -> "));ser.print(t);ser.print(F(" or "));ser.println(h/goods);

      }

      else{

        ++errors;

      }

      ++iter;

    }

    

    if(goods>0){h/=goods;}

    printCallibrationAverageProcess();

    delay(2345);//may want to see min-max !!!

    ///???????????????????

    if(errors>0){

      //ser.println(F("Average with errors"));

      lcdQuickPrintAndRefresh(F("ErroredAverages"),1981);

    }

    

    //#ifdef DEBUG

    //ser.print(F("Min: "));ser.print(minv);ser.print(F(" , "));ser.print(maxv);ser.println(F(" :Max"));

    //ser.print(F("Diff: "));ser.println(maxv-minv);

    //ser.print(F("Average found="));ser.print(t);ser.print(F(" or "));ser.println(h);

    //#endif

    

    return (long)h;// h=sum/sampls ....or t that is divided by 2 in every step

}

void LoopCallibratingSensors_Step_3(){

    //ser.println(F("_Step_3()"));

    int l=iTemp33();

    int i=0;


    #ifdef DEBUG

    while(i<l){ser.print(i);ser.print(F(": "));ser.print(getTempStandart(i));ser.print(F(" <- "));ser.println(getTempAverage(i));++i;}

    #endif

    

    sortInsertionTempStandartsAndAveragesByStandarts(iTemp33());

    

    #ifdef DEBUG

    i=0;

    ser.print(F("sorting"));ser.println(iTemp33());

    while(i<8){ser.print(i);ser.print(F(": "));ser.print(getTempStandart(i));ser.print(F(" <- "));ser.println(getTempAverage(i));++i;}

    #endif

    

    /////////////////////////////////////////////////////////////////////////

    /////////////////////////////////////////////////////////////////////////

    /////////////////////////////////////////////////////////////////////////

    /////////////////////////////////////////////////////////////////////////

    lcdQuickPrintAndRefresh(F("updating EEPROM"),666);

    i=0;

    romUpdateScaleStandartsUsing(currScale,l);

    while(i<8){

        romUpdateScaleStandart(currScale,i,getTempStandart(i));

        romUpdateScaleAverage(currScale,i,getTempAverage(i));

        ++i;

    }

    i=currScale;//hold current scale to set this view after finish

    if(!isOnScale(currScale)){

        //automatically turn this on for sms mode to work.....freeing user from second action..most propably

        romUpdateOnScale(currScale,true);

        lcdQuickPrintAndRefresh(F("ScaleTurned On"),666);

    }

    /////////////////////////////////////////////////////////////////////////

    /////////////////////////////////////////////////////////////////////////

    /////////////////////////////////////////////////////////////////////////

    /////////////////////////////////////////////////////////////////////////

    readWeight(currScale);

    lcd.clear();

    lcd.print(F("Now weight is "));

    lcd.setCursor(0,1);

    lcd.print(lastGrams);

    delay(981);

    

    iTemp34(iTemp34()+1);

    --currMenu;

    done();

    

    currScale=i;

    refreshCallibratingSensors();

}

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

//APPLICATION LOGIC for SMS MODE

long currDay=0;//long!!!??? or int..? or even 255 days to keep batgteries?



      void smsOpenDialog(const SoftwareSerial & output,bool useSecondaryPhone){

          String phone("");

          if(useSecondaryPhone){

              romReadSecondaryPhoneNumber(phone);

          }

          else{

              romReadSmsPhoneNumber(phone);

          }

          //1.

          output.println(F("AT+CMGF=1"));

          delay(100);

          

          #ifdef DEBUG

          //while(sim.available()>0){ser.print((char)sim.read());delay(100);}ser.println();

          #endif


          //2.

          output.print(F("AT+CMGS=\""));output.print(phone);output.println('\"');

          delay(100);

          

          #ifdef DEBUG

          //while(sim.available()>0){ser.print((char)sim.read());delay(100);}ser.println();

          #endif

      }

      

      void smsCloseDialog(const SoftwareSerial & output){

         output.write(26);delay(1000);

         

         #ifdef DEBUG 

         output.println();//this is for debug

         #endif

         

      }

      void callAndHangUpAfterSeconds(unsigned long seconds){

         

          seconds*=1000;

          String phone("");

          romReadSmsPhoneNumber(phone);

          phone+=';';


 

          sim.print("ATD+ +");

          sim.println(phone);


          #ifdef DEBUG

          ser.println("call");

          #endif

          delay(seconds);

          

          sim.println("ATH");//stop calling

          delay(1981);//????????

          


      }



      

      

void smsStoredWeights(const SoftwareSerial & output){

    int i=0,timThe=0,index=iter,half=SensationsPerSms*ScalesCanHandle;

    int posOfFreshReading,prev,neo;

    

    

    

    while(i<SensationsPerSms){

        --index;if(index<0){index += SensationsPerSms;}

        posOfFreshReading=index*ScalesCanHandle;

        //prev=0;

        neo=(int)temperature[index];

        if(neo>-128 && neo<127){

            output.print(neo);output.print(strCelsiu());

            //++prev;//in case we dont want time to know if some printed so to print new line

        }

        neo=(int)humidity[index];

        if(neo>-128 && neo<127){

          output.print(neo);output.print(strPercent());

          //++prev;

        }

        if(i==0){output.print(currDay);}

        output.println();

        

        timThe-=(24/SensationsPerSms);// -6th hour on default

        currScale=0;

        while(currScale<ScalesCanHandle){

            neo = StoredWeights[posOfFreshReading];

            if(neo!=DISABLED_WEIGHT_SCALE_VALUE && neo!=ERROR_WEIGHT_SCALE_VALUE){

                

                //this may no need finally....its more confusing because user wants also to see scale number in every line 

                //but this is impossible because of sms shrinkage

                //if(not have 4 or 5 scales to send data....){output.print(currScale);output.print('#');}


                

                output.print(neo);

                prev = StoredWeights[posOfFreshReading+half];

                if(prev!=DISABLED_WEIGHT_SCALE_VALUE && prev!=ERROR_WEIGHT_SCALE_VALUE){

                  neo-=prev;

                  if(neo>=0){output.print('+');}

                  output.print(neo);

                }

                else{output.print('?');}///previus was error or disabled?...(disabled?!!!)

                output.println();

            }//else not showing offcource on sms

            ++posOfFreshReading;

            ++currScale;

        }

        

        ++i;

        if(i<SensationsPerSms){

            output.println();//1 line char more for easy read sms from human between readings

        }

    }

}

unsigned long delayWhenSensorPowerOn=3000;

unsigned long startMillis=0,someTime=0;

void sendSms(const String & message,bool useFreshReadingsInsteadOfmessage){

        relaySensorsTrigger();//no use delay for sensors here because we gone delay below for sim

        relaySimOpenNice();

        

        #ifdef DEBUG 

        //simLoginWithPin(ser);

        smsOpenDialog(ser,false);

        if(useFreshReadingsInsteadOfmessage){takeFullReadingsAndPrint(ser);}

        else{ser.print(message);}

        smsCloseDialog(ser);

        #endif 


        /////////////////////

        #ifdef CALL_INSTEAD_SMS //use to finally test using batteries and just dont waste real money sending sms

        callAndHangUpAfterSeconds(8);

        #endif

        //// OR 

        #ifndef CALL_INSTEAD_SMS

        //simLoginWithPin(sim);

        smsOpenDialog(sim,false);

        if(useFreshReadingsInsteadOfmessage){takeFullReadingsAndPrint(sim);}

        else{sim.print(message);}

        smsCloseDialog(sim);

        #endif

        /////////////////////////

        

        

        relaySimCloseNice();

        

        #ifdef DEBUG 

        ser.println(strDone());

        #endif

        

}




void simulateTake4Readings(){

  

      int i=0;

      while(i<SensationsPerSms){

          //1.hold time

          someTime=startMillis;//the before slept time

          startMillis=millis();//reference to when on this line


          //2.open sensors

          if(ModeLcdOrSms){}

          else{

             relaySensorsOpenNice();

           }


        

          //iter:0,1,2,3 //THIS IS COMING EXACTLY EVERY....THIS IS TO BE TESTED!!!

          #ifdef DEBUG

          //ser.print(F("slept "));

          ser.print(someTime);//no possible to upt this before ..like not slept but sleeping...!!!!!!!!!!

          serPrintMillisForDebug();ser.print(F("iter:"));ser.println(iter);

          #endif

          


          //3.Read and Store readings from sensors

          storeIterReadings();//BASIC JOB OF THE WHILE


          //4.close sensors

          if(ModeLcdOrSms){}

          else{relaySensorsUnTrigger();} 


          //5.send sms if is the starting of the day

          //BEFORE the sleep 6 hours send the sms!!!

          if(i==0){//this must be BEFORE millis()

              sendDailySms();

          }


          

          //6.Decrease worked time and sleep accordingly to match previus time EXACTLY

          ++i;

          someTime=millis();//end time. Next commands must be as less as possible!!!

          someTime-=startMillis;//total time (diff)

          //warning..loosing some millis calling this method!!!!!!!!!!

          startMillis=hours6Millis();//time to sleep just avoiding onother value

          if(startMillis>someTime){

            startMillis-=someTime;

          }

          delay(startMillis);

      }

  

}


void sendDailySms(){

              relaySimOpenNice();

              

              #ifdef DEBUG

              //simLoginWithPin(ser);

              smsOpenDialog(ser,false);

              smsStoredWeights(ser);

              smsCloseDialog(ser);

              #endif



              #ifdef CALL_INSTEAD_SMS //use to finally test using batteries and just dont waste real money sending sms

                  callAndHangUpAfterSeconds(10);

              #endif


              //else if real sms (if NOT DEFINED CALL_INSTEAD_SMS)

              #ifndef CALL_INSTEAD_SMS

              

                  smsOpenDialog(sim,false);

                  smsStoredWeights(sim);

                  smsCloseDialog(sim);


                  if(romIsEnabledSmsAlsoAtSecondaryPhone()){

                      someTime=romReadSimSecWaitBeforePowerOff();

                      someTime*=1000; 

                      delay(someTime);

                      

                      #ifdef DEBUG

                      ser.println("2nd sms");

                      #endif

                      

                      smsOpenDialog(sim,true);//this true indicating that function will use secondary phone number

                      smsStoredWeights(sim);

                      smsCloseDialog(sim);

                  }

                  

              #endif

              

              

              relaySimCloseNice();

              ++currDay;

              #ifdef DEBUG

              ser.print(F("Day "));ser.println(currDay);//THIS IS COMING EXACTLY EVERY....THIS IS TOI BE TESTED!!!

              #endif  

}

/**

 * return true if no need pin or if cpin command was succesfull  

 */

bool relaySimOpenNice(){

        bool out=true;

  

        relaySimTrigger0();

        someTime=romReadSimSecWaitAfterPowerOn();

        someTime*=1000;

        

        #ifdef SIMEMPTY

        someTime=1500;//for debug

        #endif

        

        //#ifdef DEBUG

        //ser.print(F("Waiting for network :"));ser.println(someTime);

        //#endif

        

        delay(someTime);

        

        sim.begin(9600);

        delay(100);


        //"reading" initial answer

        while(sim.available()>0){sim.read();delay(10);}

        //#ifdef DEBUG

        //ser.print("Initial Answer:");

        //ser.println(com);

        //#endif

        


        if(isSimNeedPin()){


          out=false;


          

          String com("");

          String pin("");

          romReadSimPin(pin);


          com+=F("AT+CPIN=");

          com+=pin;

          sim.println(com);

          

          #ifdef DEBUG

          ser.print(F("Unlocking pin:"));ser.println(com);

          #endif

          

          delay(1234);//WARNING 1000 millisecond here !!! With 100 (like others) DOES NOT WORK FOR ME ON DEBUG (for PIN only this 1000 at least now i am writing!)


          

          com.remove(0,com.length());

          while(sim.available()>0){com+=((char)sim.read());delay(10);}


          #ifdef DEBUG

          ser.println(":");

          ser.println(com);

          ser.println("-");

          #endif


          int l=com.length()-1;

          char chprev;

          while(l>0){

            ch=com.charAt(l);

            //ser.print(l);ser.print(' ');ser.print(ch);ser.print(' ');ser.println((int)ch);

            if(ch=='O' || ch=='o'){

                if(chprev=='K' || chprev=='k'){

                    out=true;

                    

                    #ifdef DEBUG

                    ser.println("Pin Ok");

                    #endif

                    

                    break;

                }

            }

            chprev=ch;

            --l;

          }

          

          

          

        }

        //ser.println(out);

        return out;

}

void relaySimCloseNice(){

        someTime=romReadSimSecWaitBeforePowerOff();

        someTime*=1000; 

          

        #ifdef SIMEMPTY

        someTime=2500;//for debug

        #endif

        

        //#ifdef DEBUG

        //ser.print(F("Waiting SMS to FLY :"));ser.println(someTime);

        //#endif

              

        delay(someTime);

        relaySimUnTrigger0();  

}












unsigned long LOOP_STARTED_AT=0;

void setupSms(){


    #ifndef DEBUG

    hourMillis1=computeHourMillisBySimulationMode();

    #endif //else just use what i have in the beginning of the file


    


    byte hoursToDelay=romReadHourDelayBeforeSMSwhile();

    if(hoursToDelay>0){

    /**

     * 3!!!.ensure(before sleep) user will NOW take some sms...to be sure that he can go home and all set good (most propably) 

     */

        //send initial sms (full report) just for the user to know all ok

        sendSms(lcd0,true);//lcd0 is pseudocode her!..Nothing to do with this String just sending the TRUE variable



    /**

     * 2.

     */

        ///and wait before read and send the DAY ZERO sms.while can be avoided here with a simple multiplication but i am just leving it....

        #ifdef DEBUG

        ser.print(strDelay());ser.println(hoursToDelay);

        #endif

        int i=0;

        while(i<hoursToDelay){

            ++i;

            delay(hourMillis1);

            

            #ifdef DEBUG

            ser.print(i);ser.println(strDelay());

            #endif

        }

    }

    //else{}

    /*

      //else ...........

      //go directly to daily loop meaning that we will read and send the first DAILY SMS

      //Its a little tricky but for consistence on user timing to be understnable

      //Previus 'version' i had this to 

      //1.every time send initial

      //2.delay hours if need

      //3.and LOOP as

      //    example 9:00  read-sleep 6 hours

      //            15:00 read-sleep 6 hours

      //            21:00 read-sleep 6 hours

      //             3:00 read - SEND SMS and sleep 6 hours

      //But like this may user confused on how to face the program and how to adjust the hours to wait before main daily loop

      //(because its 9:00 and we will keep taking sms at 3:00 (minus 6 hours from coming on sms mode).... 

      //So finally fix it like this so that WHEN SMS MODE

      //ALWAYS user will IMMIDIATELLY take some SMS

      //Case 1 .(Delay hours 0) will take a normal daily reading 

      //Case 2. (Delay >0) will take a FULL REPORT READING and after the desired hours will take the first DAILY(ZERO DAY) SMS


      ////////discussing if in case of full report better to set previus values in order to avoid on sms day 0 to see same numbers like 800+800....

    */

    

    



     /**

      * 4.Hold that time because i was thinking to adjust some milliseconds but finally may no need for this.!

      */

     LOOP_STARTED_AT=millis();//will may need this for adjustment????

    

}


void loopSms(){

  simulateTake4Readings();

}


////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////

//MAIN ARDUINO LOGIC ARDUINO LOGIC - ARDUINO LOGIC - ARDUINO LOGIC 

//MAIN ARDUINO LOGIC ARDUINO LOGIC - ARDUINO LOGIC - ARDUINO LOGIC 

void setup() {

    ser.begin(9600);

    //ser.println(F("no need to begin in sms mode if not debug!!!!"));

    

    pinMode(PinButOk,INPUT_PULLUP);

    pinMode(PinButMenu,INPUT_PULLUP);

    pinMode(PinButRoll,INPUT_PULLUP);

    

    ModeLcdOrSms=digitalRead(PinButOk) && digitalRead(PinButMenu) && digitalRead(PinButRoll) ;

    //ModeLcdOrSms=0;//for debug


    //this check immidiatelly because using 13 led

    if(!ModeLcdOrSms){

          /**

            * 1.VISUAL INFORM user , with 5 seconds blinking , that we are on SMS mode (and not lcd mode).

            *   (blinking cannot been seen with CLOSED arduino COVER-BOX so this is basically for DEBUGGER-USER)

            */

          ledBlink(5);//using 13 led build in

      

    }

    

    pinMode(PinHx0Dt, INPUT_PULLUP);

    pinMode(PinHx0Sck, OUTPUT);

    pinMode(PinHx1Dt, INPUT_PULLUP);

    pinMode(PinHx1Sck, OUTPUT);

    pinMode(PinHx2Dt, INPUT_PULLUP);

    pinMode(PinHx2Sck, OUTPUT);

    pinMode(PinRelaySensor, OUTPUT);

    pinMode(PinRelaySim, OUTPUT);


    //only if lcd mode we are checking to FLUSH EEPROM that i have it here for when a new arduino is running the program

    if(ModeLcdOrSms){

        if(!romCheck()){

            ////happened on building to Flush() !!!.....This an issue !!!!!!!!

            romFlash();

            ////but at least only in the beginnning..not somwhere(yet) when running sms mode!!! !!!!!!!!

        }

    }

    

    //now that we know how to trigger off ...just saving energy in any case!

    relaySensorsUnTrigger();

    relaySimUnTrigger0();


    

    #ifdef DEBUG

    showReadEEPROM();

    #endif



    /**

     * In ANY case (sms or lcd) we MUST NOW BE READY to setup the DHT type and begin() it.

     *            I changed original code from private to public for this member _type

     *            ...just in case that i want to put some better (than DHT 11) sensor

     *            ...even if a friend told me that DHT 11 is better for those jobs!

     */

    dht._type=romReadDhtType();//was initiated as type=11 

    dht.begin();//this will also be called when we changing TYPE.No need to  be called every time that we are opening the Sensor Relays!

    

  

    if(ModeLcdOrSms){

        #ifdef DEBUG

        ser.println(F("\nMODE LCD\n"));

        #endif

      setupLcd();//will directly open relay sensor

    }

    else{

          #ifdef DEBUG

          ser.println(F("\nMODE SMS\n"));

          #endif

      setupSms();//will directly open relay sim and sensor

    }



    

}

void loop() {

    if(ModeLcdOrSms){loopLcd();}

    else{loopSms();}

}




















































/**

 *    28/10/2020

 *    checked 3x1.5 volts battery supply for SIM900L module

 *    4.3 volt on sim start at 14:36

 *    4.28 volts after 42 simulation days/sms-es at 17:26 

 *    

   SIM800L sources

   https://predictabledesigns.com/the-sim800-cellular-module-and-arduino-a-powerful-iot-combo/

   https://github.com/thehapyone/BareBoneSim800

   https://www.teachmemicro.com/arduino-sim800l-tutorial/

   https://robu.in/sim800l-interfacing-with-arduino/

   https://lastminuteengineers.com/sim800l-gsm-module-arduino-tutorial/

   https://www.youtube.com/watch?v=eQRArraNxTw

   

   SIM800L pinout

    '0'.net pin Antenna

    '1'.vcc 3.4-4.4 volts but demands 2Amper ,Pined on Sim Relay NORMAL OPEN .

            NORMAL CLOSE of relay is 'Pined' on 3x 1.5 volts Batteries?! or lithium cellphone like baterry ?!? 

    '2'.reset pin,,, i am not using this 

    '3'.Rx sim pin   -> Officially this pin needs to be voltaged DOWN (with RESISTOR DEVIDER i choose) 

            from 5 volts of arduino to 3.3? Volts affortable by SIM800L chip on the SIM800L module.

    '4'.TX sim pin   

    '5'.Ground   On Batteries.WARNING WARNING WARNING This also connected to ARDUINO ground in order for the above resistor divider to work !

            (I destroyed one sim module just to see WHY I COULD NOT communicate with SIM800L just because i had forgot 

            to connect the GROUND of Battery to GROUND of Arduino (beyond with the output of above resistor divider)

            and finally /thankfully i discovered that the module was ok but i didnt CONNECT GROUND of Battery to GROUND of Arduino !!!!

     

 */




 /**

  * Next version with an RTC

  * http://www.gammon.com.au/power

  * http://www.home-automation-community.com/arduino-low-power-how-to-run-atmega328p-for-a-year-on-coin-cell-battery/

  * https://dronebotworkshop.com/real-time-clock-arduino/

  * 

  */






Δεν υπάρχουν σχόλια: