Κυριακή 26 Σεπτεμβρίου 2021

13os & 22os

 2147483647

long   max : 9223372036854775807 =(double)9.223372036854776E18

double max : 1.7976931348623157E308

0 : 1 , 6.283185307179586 , 3.141592653589793 , 4.1887902047863905 = 14.613568165555769

1 : 7 , 43.982297150257104 , 153.93804002589985 , 1436.755040241732 = 1641.675377417889

2 : 44 , 276.46015351590177 , 6082.12337734984 , 356817.9048045239 = 363220.48833538964

3 : 277 , 1740.4423300887454 , 241051.26271729125 , 8.902826636358623E7 = 8.927133506863362E7

4 : 1741 , 10939.02561979966 , 9522421.802035604 , 2.2104715143125317E10 = 2.2114250244952972E10

5 : 10940 , 68738.04726054467 , 3.7599711851517934E8 , 5.484544635408083E12 = 5.4849207122046455E12

6 : 68739 , 431899.87483021757 , 1.4844182747977163E10 , 1.360499037217603E15 = 1.3605138819009898E15

7 : 431900 , 2713707.7341708634 , 5.860251851941979E11 , 3.3747236998049882E17 = 3.3747295600882963E17

8 : 2713708 , 1.70507302335757E7 , 2.3135351520348125E13 , 8.37101180047745E19 = 8.371014114014578E19

9 : 17050731 , 1.071329024958715E8 , 9.133471508531668E14 , 2.0764315438418355E22 = 2.076431635176563E22

10 : 107132903 , 6.731358820450958E8 , 3.6057500578478344E16 , 5.150592949195419E24 = 5.15059298525292E24

11 : 673135883 , 4.229437489800957E9 , 1.42349306964523546E18 , 1.277605685840035E27 = 1.277605687263528E27

13 : 26574339495 , 1.6697149946298618E11 , 1.6396741103857283E19 , 5.809767529406992E29 = 5.80976752957096E29

14 : 166971499463 , 1.0491128721436658E12 , 2.0267474644981096E19 , 4.512120842401103E30 = 4.5121208424213703E30

16 : 6591770583829 , 4.141731608061298E13 , 1.4567571263057058E19 , 1.2803478363953625E32 = 1.2803478363955082E32

18 : 260232671860521 , 1.635090100282112E15 , 2.7989727909981094E19 , 9.711788904884501E33 = 9.711788904884528E33

21 : 64550769799687888 , 4.0558444837253069E17 , 1.5343523050039808E19 , 1.3205816324257662E36 = 1.3205816324257662E36




public static void main(String[] args) {

        // TODO code application logic here

        System.out.println(""+Integer.MAX_VALUE);

        System.out.println("long   max : "+Long.MAX_VALUE+" =(double)"+((double)Long.MAX_VALUE));

        System.out.println("double max : "+Double.MAX_VALUE);

        

        int i=0;

        long aktina=1;

        double Per=2*aktina*Math.PI;

        double Emb=aktina*aktina*Math.PI;

        double Og=aktina*aktina*((aktina*4*Math.PI)/3);

        double sum=aktina+Per+Emb+Og;

        while(i<100000){

            Per=2*aktina*Math.PI;

            Emb=aktina*aktina*Math.PI;

            Og=aktina*aktina*((aktina*4*Math.PI)/3);

            sum=aktina+Per+Emb+Og;

            //System.out.print(i+"");

            if(aktina>0 && Per>0 && Emb>0 && Og>0){

                System.out.print(i+"");

                System.out.print(" : "+aktina+" , "+Per+" , "+Emb+" , "+Og+" = "+sum);

                System.out.println("");

            }

            //System.out.println("");

            aktina=(long)Math.ceil(Per);

            ++i;

        }

    }

Παρασκευή 17 Σεπτεμβρίου 2021

Arduino Beehive Thingspeak Sms Sim800L 5 Scales Code and Schematic









English manual of the most upgrated and stable version of my program.

For greek manual and greek yt videos see
https://jimakoskx.blogspot.com/2021/01/arduino-beehive-5-scales-dht-thingspeak.html

Arduino Pro Mini Program -8MHz running on about 4-4,1 volts with regulator-
for
5 (max) Beehive Scales sending data to Thingspeak (and/or Sms) via SIM800L module
and having also LCD 2x16 (minimum rows columns)
for adjustements on the below lcd menu .

The program can run 'satisfyinglly enough' 
even with just 3 AA batteries (i use rechargable varta)
but my box can have 4 parallel sets of 3 AA...
And i have made it possible to AUTO POWER-OFF if we detect that we 
are in some LOW VOLTAGE and we dont want to damage our rechargables.
(especially if someone wants lithion batteries)
And ofcource we throw/disassemble the static led of arduino pro mini. 

Here are the results of
PER HOUR reading and update for 4 months with only 3 AA batteries (room temperature)

So finally is Fine !!! with just 3 AA simple Varta batteries (and no need for lithium etc)









 

Important Note

a)
10 pins for 5 possible scales (2 each HX711 amplifier)
2 pins for sim (rx tx)
2 pins for sim latch relay (set reset)
1 pin for power off via reset pin of main latch relay
2 pins for buttons
2 pins for lcd (A4 A5)
1 pin for reading battery
= 20 pins !!!(23456789,10,11,12,13,A0,A1,A2,A3,A4,A5,A6,A7)

Meaning that DHT can NOT be used  if we have 5 scales !
(So except if some trick can be programmed !?!?) 
we can have DHT and 4 scales....
5th scale .... no DHT pin space !!!

b)
I have NOT yet messed up with possible too much  COVARIANCE/fluctuation
of the Weight Measurments Relative to Temperature Changes.
In case that this is a big problem then i thing that this can be 'fixed'
from Thingspeak itself meaning
....analyse correlations and create tables(views)
that trying to show the real weights.(and not the affected from temperature)
After all,,, the DIFFERENCES on weights matter (how much new honey)
and temperatures -most propably-does not flunctuate too much 
for the same time on 2 continues days..


Onother Note
The program is NOT USING RTC even if in the schematics
is 'ready to go' for someone that wants.
This is just for being very simple....even if we may loose 1,2,3,seconds
(depending our adjustement-see menu) 
on every step....(but who cares for those...not me at least)
Most propably someone wants to take the weights every 1 or 2 hours ...
not carrying if the 1st reading of the 1st day lets say come at 9:00
and the 2nd day comes at 9:01 (+1 minute)...
Anyway ....this can be
almost perferctlly be adjusted without RTC ...(see menu)

The simplicity also makes us running at about 4,1 volts
(using an adjustable step up/down converter
personally i am using pololu 2868 or 2869)

in order to have latch relays without some transistor .
(It is almost unlikelly that we can find any relay for ~3 volts with
coil resistance so that the current does not exceed 40 ma for arduino pins)
So its very usable that we can find relays 5 volts
to be used without some Resistor in Base pin.
(directly from arduino pin to set/reset pin of the relay)
A 5 volt relay with coil 125 Ω is in the limit of 40 ma
but accepted if we will run at ~4 volts.
(But you can easy find 5 volt relay with coil bigger than 125)


And those 4.1 volts are ALSO perfect for SIM800L module
and is enough to trigger the 5 volt relays !!
 
 
















(Parenthesis,,,
Beside the 2 momentary buttons for on/off the main latch relay-powering arduino-
and beside the 1 latch button for LCD powering when we are in adjusting mode
(adjusting mode vs coffe mode in our home away from farm/field waiting the measurements)...................
We have 2 buttons attached to arduino pins for lcd adjustements.
But we have 4 functions (2 functions for each button).
-press and release quicker than 666 milliseconds (1st function)
-press and release after 666 milliseconds (2nd function)
So 
Button A quick release = Menu Roll
Button A slow release = Ok (store current settings ..or do something)
Button B quick release = Next digit (from '0' to '1'etc ) in current position of Cursor.
Button B slow release = Next position of Cursor in the number-word that we are editing.
Parenthesis End )





--------------------------
The function of the arduino applicatiion is simple.
In order to adjust some things in lcd mode
we powering up the arduino and we ensure the the latch button of lcd
is pressed so the lcd have power.
Like this the arduino starts and we are in the menu mode.

After that we have make all the adjustements and
we want to leave the arduino in the farm/field with beehives 
we close the arduino and 
we opening again but pressing also the Menu/Ok button 
meaning that if arduino detects that some button (adjusting button)
is pressed by the time that is powering on
then we will not be on the 'LCD LOOP' (to make adjustements)
but on the 'Coffe loop' !!!! meaning we can go home
waiting the measurements to Thingspeak .
So for Coffe mode we just
a)press the Ok button (no release)
b)push the Main Power Button (it is momentary and not latch button)
c)wait 1-2 seconds and release the Ok button
d)Wait (~1 minute max?) to check on Thingspeak
that arduino INDEED send the fresh data...
e)And if we see that thingspeak has just now been updated
we just ensuring lcd is powered OFF (to not loose battery power)
and we are going for home/coffee!!!!....
waiting the measurements from load scales
every 1 (or 2 or 3 ,etc ) ,,, hours (what ever we have adjust)
...to be updated on thingspeak !!!!

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

Onother note.

In case that the project will be used somewhere with TOO much cold (low temperatures)
then ,,,because LCD screens may have problem (braking-loosing their liquids etc)
then...in those cases a simple modification can be fixed so that
the arduino-project box is not permanently attached to the lcd (16x2) screen
but instead there is only a 4 pin available so that the honey producer 
when wants to make adjustements to the snow-cold field using the lcd
just carries with him the lcd and attaching it to the available pins somewhere waiting in box.
This is ...again ...to NOT brake the lcd screen with the very low temperatures
because in lcds's manual says
not good below (if i remember correct zero~0) some degrees celsiu .

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

I am using 2 LATCH relays (2 coils latch relays)

The first latch relay is the one that we are triggering(set pin) to power up the hole application.
And is latch relay (not normal) to be able to trigger the reset pin
somewhere from the program to 'auto' power off the application.
And the second latch relay is the one that we are triggering
from the program to power the sensors and sim800l module.
And is latch  relay (not normal) just to minimize battery power consumption.
(summarized milli-amperes of holding a normal relay triggered
for 30-40 seconds that needs a step to send the data on thingspeak
for lets say 60 days and 12 measurements per day
is.....something....even if is not a big amount....but it is something
especially for someone that wants to use simple AA rechargables batteries
that can be used with safety and in many other applications
instead of lithion batteries................. )

Here are some examples of 2 coils latch relays that can
can be used and handle 2A (as per max current of SIM800L)

Omron G6AK-234P-ST(40)-US  (coil 139 mean ~36 ma)
Omron G6AK-274P-ST(40)-US
Panasonic TQ2-L2-5V                 (coil 178 mean ~28 ma)
Axicom V23079-B1202-B301     (coil 178 mean ~28 ma)
Zettler  Z850P2-5                         (coil 125 mean ~40 ma)

(
Zettler is good (and cheap) to be used as MAIN POWER relay 
because i think can be triggered (directly from battery)
even below 3.5 volts (holding button pushed a little more time)
...even if the purpose of the application is to be able
to power on with 3AA charged good =4-4.3 volts is enough for triggering.
I mean ....if someone wants lithium batteries that have max 3.7 volts
...maybe wants to use a 3 volts relay ....but must change a little the scheme
)

The second relay (sim-sensors) have also 1 more function.
One channel (lets say left side)of its pins is used ok..for sim-sensors.
But the other channel (lets say right side)
is used for connecting the battery
to some analogue pin of arduino and have it as reference
to be able to know the current voltage.
So only when we triggering the relay
(powering up sensors and sim for about 40 seconds)
we are loosing some current
(even if ok...i have Mega-Ohm resistor there)
avoiding have continiuslly some current to flow out !!!

--------------------------------------
How we measure/ translate real voltage?

The time that
sim is powered on and the analogue pin is also connected to battery
(via sensor-sim relay)
we ask sim (with AT+CBC command) on how many volts is running.
Sim is attached to the converted by step up/down voltage.
(left channel-side of relay)
But analogue pin is attached to battery voltage. 
(right channel-side of relay)

And the answer we get is the ACTUAL
(and current because it is flunctuating a little)
output of the step up/down concverter .

(I am adjusting my adjustable step up/down converter
somewhere around 4.1 volts)


Now if the batteries have more voltage than 4.1
(lets say in the beginning 3 AA will have 4.3 volts)
(meaning that the regulator works as step DOWN converter)
then we will NOT find the real voltage but who cares?
(because we want to know the voltage only if its very low
to save our rechargable batteries)
We will thing that the real voltage is just the stepped down voltage
meaning the voltage that sim is working.

But if the batteries have lets say 3.2 volts
(meaning that the regulator works as step UP converter)
then with a simple analogy we are translating the analogue value
and we find the real voltage  !

You can see below that in the beginning we are not seeing the true voltage.
(Line is straight till real voltage becomes lower than converted)


3000 per 2 minutes





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

This is also the reason that just for testing/debugging 
reasons i am binding 2 fields on Thingspeak channel
(you can see in above picture that we have
3.784 real volts and sim is running at 3.9 volts)
The client does not need to know the sim voltage
(the converted voltage is only for debugging/testing)
The client may only need to know the real voltage
(to understand that in the next visit on the farm field with bees
he must have with him a new set of batteries to change if its very low)
Irrelative if arduino will auto power off depending the adjustements.

At the time of writing this,,,
when we are creating a new channel on Thingspeak
(after creating an account )
the channel has 8 fields
(that in the beginning we must enable them all)
(and we must update -from lcd mode-arduino eeprom with the WRITE API KEY)
 
5 fields are for the 5 load scales
1 field is for the combined temperature and humidity
    example 1751 means 17 Celsiu 51 Humidity (who cares for humidity..)
1 field is for the real voltage 
1 field is remaining for programmer to change the code and use it as he wants.
     I personally use the last field to have also the signal strenght  for debug
   (this added in the last upgrate of program...when i also add 1500uf capacitor)
     example field8=263999 means last signal = 26 and 3.9 last sim working
-------------------------------------












 
Before you read menu i am repeating that on every below menu
after adjustments....on each menu
in order to take effect(stored on memory)
we must press Button A slow release = Ok (store current settings)
I am not saying this again in below explanations....

If you dont press OK (slow release of button a) 
and you just roll to next menu ...changes will NOT be applied/stored



Menu#0
- Enter program password and 'press' Ok (slow release of button 'a')
(
that is the same with the PIN of the phone card irrelative if pin usage is enabled
This will not mess with the phone card meaning will not lock the card in case of error.
It is just a precausion....that if we dont afraid of someone mess with the box in the farm
....we can just set the password 1111 (via phone card pin menu)
Just that like this ...in case of stolen we gona loose the money in card...
But who cares...for 10 pre paid euros in this case?..that we will loose the hole beehive scale!
Anyway...just an effort....
)



Menu#1
-Enter in second line  the ... every how many Minutes and Second
 we want the arduino to get-off from the Deep Sleep (many times of 8 seconds deep sleep)
and opens the latch relay that gives power to Sim800L and Sensors
(HX711 from load cells and Dht)
taking the data and sending them to ThingSpeak and or Sms.

In first line (Just a non critical future of the program )
we entering how many hours we want the process to delay.
This is because maybe lets say user wants every 2 hours but wants also not to start immidiatelly.





Menu#2 - Showing and Taring
Showing the current data of load cell sensors and Dht (temperature-humidity)
Here also we can Tare some load cell .
Press Ok to tare the current scale.(if the scale is empty ofcource)
Press Next Cursor to iterate through 5 possible scales
Press Next Digit to just switch on/off if we want to see ALSO
the output of HX711 amplifier ...beside the translation in grams /units
Below we see that scale #0 counts 747 grams (or units dependig your callibration)
and its tare (the amount adjusting the result) is zero.
Also we have temperature 18 Celsiu and 58% humidity. 





Menu#3 -Callibration

Here we are seeing that #0 scale 
is callibrated with 2 standart(known) weights
The 1st is Zero and has average =116897(from HX711)
And 747 is the weight beiing now on scale

The program in this state ,,is iterating on (2 in this case) known weights every ~3 seconds
to be able for someone to just,....see how this scale is callibrated.
- Pressing Button B quick release will iterate manual on next standart weight.
- Pressing Button B slow release will show data for next scale (in this case for scale#1)
- Pressing Button A quick release will go to the next menu (as always in menu)
- Pressing Button A slow release will START the callibration process for the selected scale.
(Beeing on callibration process you can always escape Pressing Button A quick release
if you want to cancel the new callibration....meaning that what ever you do will be memorized on last step of callibration process)



So here is the callibration process if we ..Press Button A slow release.
First of all if not some HX711(and scale) is attached to pins then the program will show on lcd something like 'cant start'...callibration process.(I am not puting some image here)

And if HX711 is installed in this #scale then we will se the below image
(but saying 1 instead of 4 known weights)

So in this step we are selecting how many
known weights we have .
We can select from 1 up to 7 known weights.
The program can hold 8 weights
meaning that ZERO is always the first.

So in this example if we have 4 known weights
(lets say we have 1 kilo,10 kilo,50 kilo and 100 kilo)
then we put number 4 (Pressing Button B quick release)
and after we ensure that NO WEIGHT is on the scale (scale is empty)
(because the firts average that we going to take is the average for ZERO)
we Pressing Button A slow release

When we do this we will see that the program is taking MANY samples
showing us the process that needs about 20 seconds (for each known/standart weight)

In this image the process is at 60/100
the sum of samples =106837
minimum sample=1732
maximum from HX711=1829
This is without scale just with a HX711
meaning dont confuse by 'fake' numbers.









After finish taking the average from zero the program showing us the next picture
asking us to enter the grams of NEXT weight that we have.
(Dont confuse with the below image that has 2/3 weights)
The below image is 2/3 meaning that in previus step we entered 2 (TWO) known weights.
So it just took the average from zero (meaning the 1/ of 3) and
now is asking to enter the grams(or whatever unit we choose)
of the 2/3 (meaning the first weight we have in posession)
So we entering using Button B how many grams is the weight that we want to take its average.
And we put this weight on scale and  Pressing Button A slow
Now the program will take the average of this weight in about 20 seconds 
showing us the process.
Next steps is the same ..meaning that in each step we
entering the grams of the weight
put this weight on scale and press 'OK' to take the average.







When the last step of callibration (3/3 in above image/example )
is completed 
then the program will store on memory the new data
and will go one menu back = the Menu#2 Showing and Taring
to be able to a)empty the scale and b)take its TARE .(see menu#3)
And like this the callibration is finished and we can test/experiment.

Note.
No need to put the weights in order meaning 
(lets say we have 1 kilo,10 kilo,50 kilo and 100 kilo
no need to put first the 1 kilo second the 10 kilo etc
even if this is better for us to not confuse)
The program will use a simple algorithm in the end to sort them out.
But the program will not allow us to put the same grams(weights) 2 times.

And onother note about callibration even if i have it on code (comments)
Just for simple users...
A scale is giving a number(not grams-weight) throught HX711 amplifier.
And this number is fluctuating so we must take many samples for some simple average.
So in order to callibrate some scale we need at least 1 known/standart weight.
Lets say we have 1 kilo available...knowing that this is 1 kilo.
Then the simplest callibration is like this.
Take the average on zero ... lets say AV_0=12345
Then put the 1 kilo (W_1) and take the average ...lets say AV_1=34567
Hold them on memory
Now...we are ready and when we put a UN-knowng weight on scale 
the program is doing the following
Take the current average lets say AV_X=98765

1000 grams is giving 34567-12345 
x grams  when 98765-12345 ??
x=(AV_X-AV_0) *( W_1/  [AV_1-AV_0]   )

x= (98765-12345) * (1000 grams /   (34567-12345)      )
x= 84420 *1000 / 22222 = 3798 grams

And from this x just decrease the tare that we have adjust on previus menu.

If some programmer wants to correct a little the code ...then the code is free to be corrected
and i would be happy to see a comment on the post ...
Anyway ...and because may some scales not be ANALOG (exactly)
to the weights thats why we have 7 available standart weights insetad of just 1 (beyond zero)
Meaning that a 'professional callibration (as i have read on net)
may be better if we have the averages of
0%,1%,10%,25%,50%,75%100%,125% of the scale capability.
(percents above are just an example)
Thats why in my program i ve choosed to be able to store 8 standart weights for each scale.
Thats all for callibration.....
Ah...and units depending on what you have put and entered.
If you entered grams the result will be grams...if you entered pounds results will be pounds etc.







Menu#4 -Enabling -Disabling






In my program (all menus) 
On means Enabled and Of (Off) means Disabled
Just that because we are working with arduino and lcd and 2 buttons
i have made it like this ...On /Of
So when cursor is on the n of the word On
pressing Button B (quick release) will change it to f make it Of=disabled
and vice versa....
So from this menu disable scales that you have not installed in order
to not send data to Thingspeak/or sms 




Menu#5 PIN of phone card=program password also






In this menu ,in the beginning ,,,the cursor is on the n/f of CallOn/f
If its On the in case that you have adjust in later menu to be send SMS
then instead of sending SMS(spending money)
will make a simple call (for just testing debuging)
(Dont askme why this menu is here...ask your programmer or program it as you like !!!)
Ok.
After this if you press Button B slow release then the cursor
will move at first 1 of 11 Dht type.
I have not test onother type but the library
says that supports 4 types of DHT (temperature-humidity sensor)
(11,12,21,22,,,,with 22 as i know being the better but more expensive)
So if you have the cheap (1-2 dollars!) DHT 11...jsu leave like this and procced.
-----
Now the most imporatnt function of this menu is the PIN of your phone card.
Thats why i put it after the 2 not important futures (to not be easy to change it by mistake)

First of all ,,,if someone likes and test this project ...
before you put your card in the SIM800L
put it in your phone  and make its PIN to be 1111.
And also make it to not need PIN....
Just for safety...
Then put your card in SIM800L ...learn the program etc experiment ...
...and only after those ...........you....
can hide some privacy by putting some other PIN on card(using your phone)
and the same time change this PIN here in the lcd menu....
And of cource if you change the PIN and you want the program to use PIN
so enable it a)from your phone card using your phone
and b)from here on lcd menu

Hope that all this is clear and understandable.
So if 
Need PIN Of ...then the program will ignore the stored PIN
But if 
NeedPIN On ... then the program will try to 'login' to phone card by using the stored PIN
So ...when testing ...have your PUK  for any case....(your mistake or my mistake !!heheheh)
---
And also this PIN that you have enterd here has 1 more simple function.
This is the following.
Irrelative if the Need Pin is On or Off
this pin will be used also as the password for Menu#0
The password of the program lets say.

When we opening the program (menu#0) will show us
1111
asking us (silently) the password.
If you are testing or not afraid anything stranger/thief/curiosity etc on your farm/field
just have your Pin setted as 1111
to be able to quickly enter the basic memnu.

But if lets say someone wants to protect from some curius human that will go and mess
with your arduino box ....and you selected to change the PIN
then...you just need some time more ijn menu#0 to enter tha main lcd program.
I am boring to explain more....on this....and also if a thief go to take your arduino box and phone card
all those are not going to protect you ...so....i am leaving to you or your programmer to fix those.
Meaning that
Program Password Future =PIN
is just an ...effort....




Menu#6




20 and 13 seconds......Leave them as it is...Its there form an old version.
Basically....and maybe (because its not tested)
IF you want to use SMS and your signal is not good (far away from civilization etc)
you may change 13 to bigger if having problems and SMS is not coming....
(because may power at SIM800L be OFF/down (from relay) before SIM800L manage to send the sms...
But i thing i have corrected this on the new code....just not test it)
Anyway.....




Menu#7  SMS enable disable and to whitch Phone numbers






Just in the end before release i add the future to be able to send SMS
in 2 instead of just 1 honey producers....
Use On to enable sending SMS on the selected phone number.
Have it both Of ............if you dont want SMS
(meaning using only the cheaper and new future of Thingspeak)
And if you have some On here ...then remember that from 
Menu#5 we can set CallOn to just make call instead of sending SMS.
So if you really want SMS ....Make here some On and real phone number
and ensure that Menu#5 is CallOf



Menu#8 Enabling Thingspeak (Php)









In the first line select PhpOf
if you want only SMS
or select PhpOn(default)
if you have an account on Thingspeak
and you dont want spending money on SMS.
This is the main future of my program.
Using Thingspeak instead of SMS.


Just because who knows what will happen on Thingspeak.....
i just gave some efforts to be easy to change to onother php server
...if somene knows how to create and maintain such a server....
Anyway..most propably...have it as PhpOn and let me go for some coffee!!!

And in second line....  i make it possible
to be able from LCD change the type of SIM800L

that we are using.
because green sim800L have reversed pins from red sim800L
and ..just for testing witch is better
and also be easy for some programmer to test this project..
(lest say)
i made this sumenu.
And because i finally use the red module with the helical antenna
the defaul here is GreenOf
But if you have the Green SIM800L
make this as GreenOn ,
hold ok (Button A slow release)
and you must power Off the application in order to see the effects.
put your Green on circuit and now if you power on ..it must be ok...
 (like this no need to compile the program just to make experiments on what SIM800L is better etc)

Just for some programmer to quick understand here (before goes to read the code)
on setup()
the program will read from permanent memory
witch pins will be used for the RX and Tx of sim800L
and create the Serial object accordingly....






Menu#9 Your API key on Thingspeak must be entered here






'api_key=' is editable just fro ...as i ve said in case of different php server
but if you use Thingspeak (and some settings didnt changed by thingspeak programmers)
just be patient (with 2 buttons---thats life)
and enter your API KEY of your channel on thingspeak.
In above example my api key is =7i2nqsk2l0aqfw44

Am saying this because you can enter small lettes (insetad of capitals that you will find on your channel)
I hope thats ok !!!!



Menu#10 Your APN of your internet cell phone card provider






Please find your APN from your provider and enter it here.
Here in greece and with my vodafone card....apn is 'internet.vodafone.gr'
Also make note that i am not supporting username and passwords in case that some apn needs them.
Usually username and password no need....







Menu#11






If you are using thingspeak server leave it as it is.

If you are programmer and /or want to create your own php server
just notice that 
a)even if server is SSL meaning httpS
here we entering http (without s)
because if i am not wrong sim800l is not supporting ssl....
b)notice also that after .com we are not storing the slash (/)
.....i am just adding this on code....(dont ask me why !!!)




Menu#12







Also here if you are using Thingspeak leave it as it is.

A simple explanations for php programmers (of the above unused settings)
is the following
to make an entry on thingspeak 
lets say we want to make an antry for field 1
(lets say that on field 1 of our thingspeak channel we are storing the weight from our first scale)
So if first scale (#0on the arduino program) is counting 12 kilos =12000 grams
then we want to make an entry 12000 for field 1.
And we doing that by a simple calling (from sim800l or from our browser)
the following url/command.


GET https://api.thingspeak.com/update?api_key=36DAONID3Q0ZPFS2&field1=12000
 if we calling from SIM800L
(WITHOUT S after http..!! correction...Sim will not work if you put this S !!!!)
(corrected this explanation(not some code)---8/Oct/2022)
------

or just (without GET)

https://api.thingspeak.com/update?api_key=36DAONID3Q0ZPFS2&field1=12000
if we calling from our browser

So i hope you understand all above settings.
here the most important is the API KEY of some (my) channel 
36DAONID3Q0ZPFS2

and ....this menu#12 is some words(querry) that needs to be added in the line
for the specific php server of thingspeak.

So if you use thingspeak ..just leave it as it is !!!!








Menu#13 Testing and Signaling








Press Button A slow release (Ok) to just test here your settings.
If you have setted only for thingspeak then the program
will take the measurements from sensors
and make an update on thingspeak.
(Test your thingspeak channel from your browser when this function return)
If you have adjust some SMS on some phone...then you will (you must) also
take a new SMS(or take a call if you have set CallOn)

So this is to test from LCD.
Whatever do here will be doing this every 1 or how many hours+minutes you have set in Menu#1
So...if we are ok here(lcd mode) ....we propably be ok in the continues mode (sms-coffee mode)





----


And the second line of this menu is an effort to be able for the user to find a good spot 
on the farm/field ..
meaning a spot with a good signal.

To do this 
Press Button B slow release (as rolling the cursor)
and the programn wil start showing the signal every 3 seconds.
So walk around your farm to see the differences on signal.
If you find a good signal and you like this spot
again
Press Button B slow release (as rolling the cursor)
to return to basic menu.

Just remember(if i am correct) that even 1 meter above earth ...the signal is different.
meaning the purpose of tis function is to walk around and check the signal ...but
make sure the signal is ok even in the elevation that you are going to leave your arduino box...

As from SIM800L manual signals are like this

2-9 Marginal
10-14 Ok
15-19 Good
20-30 Excellent
99 Not known or not detectable

Note a)99 is not tested from me
Note b)believe it or not ...i had some returns 31 and may be 33 if i remember correct.

Anyway.....a signal bigger than 10 i thing is fine.
Here in Athens (big cities ,near the cell towers) signal is usuall bigger than ...lets say 23
But outside on farms ...you may have problem(low signal).
sos
Thats why 'we' add on circuit a capacitor bigger than 1500uf 
sos
....to be able even if far away from cell towers having a good signal !!!!!
This capacitor is not in the circuit schematic but you can see it in some (newwer) pictures.
Is a capacitor just touching batteries !!!

'Dont ask me more' ...i just know a little java !!!







In above image we see that now signal is 26 ...





Menu#14  setting power off on some volts





basically this is for lithium batteries....that i am not using.
I am using a set of 3 AA batteries
...and for someone that wants to use RECHARGABLE batteries
we must have some setting that does not allow program to work
if batteries are less than ...lets say 1 volt each.

In the above image/setting we are saying that if the volage
is below 3.456 Volts 
for 2 continues times ..
then the program must AUTO POWER OFF

3.456 divide / 3 in series batteries
~=1.1 volts will be the ceil for our recharhable batteries.....

I am boring to explain more !!!!(have fun propably leaving it like this...
if ofcource you are using sets of THREE in series rechargables AA



Menu#15 







Ok....finally last menu!!!!

Because i wanted the application to be easy for some new arduino programmer
i tried to NOT use RTC.
May someone use RTC in order to be perfect on your timings....be my guest.

But as i have done it ..i thing we can achive 0 or 1 or 2 seconds accuraccy between steps/updates
After all this is not for scientific reasons...is just for farmers.

Anyway I am not using RTC.
And to save battery power 'we' are using the library for power down 8 seconds.
instead of just a delay(some millis) call
ok?
So if i want lest say to make updates on Thingspeak every 1 hour
i am doing this.
1 hour=60 minutes=3600 seconds
So 3600 / 8 seconds =450  (or 3.600.000/ 8000 millis)
And theoritically we must power down 450 times
after each update/sms

But if you are programmer you can understand that is not that easy
because we are saying to ATMega 328 to power down 8 seconds and is taking more.
So the solution is to divide not with 8000 millis but with 
some estimated millis of 8 seconds like 8500(default)
So...to not make tire you (and me)

My current setting is that the 
ESTIMATED MILLIS OF A POWER DOWN = 8974

In the above picture/setting this is 8502.
So from second line we can set this manually.


If our menu#1 setting is every 1 hour
and lets say we took one update at 13:01:00
and the next update at 14:02:00
then we have a delay 1 minute = 60 second

And that means that we slept (power down 8 seconds)
MORE times than needed
because the denominator we used was too small and result became too big 
so our ESTIMATED MILLIS must be increased...



You can adjust this from second line manually
or from first line with the following logic (for this example of 1 minute delay)

-Of means that took update before (lest say 1 minute before expected-14:00)
-On means after.....delay....
(what to do with atm328 low flash,2 buttons and 16x2 lcd?!!!! heheh)

So n or f to adjust this(minus or plus) and after ...
we are entering how many seconds we have difference
In our example 60 seconds,.
(can be 999~=16 minutes ...but see code or make twice ...i just have not test this for big differences)

And we pressing OK(button A slow release)
and we will see that the second line (estimated millis of a 8 seconds power down command)
just changed....

And because all this project is for programmers ...i STOP writing more
for this....and you can read the code (or post a comment-question-if i am alive i will answer-)

FINISH expalining released project 13/12/2021
My box still working fine...(just i dont have real beehive data ...)


END MENU EXPLANATION











The schematic below....

a)
and you need ALSO
a >=1500 uF (electrolytic capacitor-not in below schematics..)
after batteries and before step up/down(most propably)
in order to have GOOD SIGNAL .

b)
3+3 pins on each side of pro mini
(Tx,Rx,Rst + Vin,Grnd,Rst)
ARE NOT connected to breadboard
neither we are using them at all
10 pins for 5 possible scales
2 pins for sim
2 pins for sim relay
1 pin for power off from main relay
2 pins for buttons
2 pins for lcd 
1 pin for reading battery
= 20 pins !!!
Meaning that DHT can be used only if we have less than 5 scales!
There is no DHT in scheme....use some hx711 pin or make your tricks !!!


c)In below image ,,, about in the middle of up left quarter
you can see a black 1x3 holes 'breaking' the orange line.
(By the way orange means the converted output from regulator)
This 1x3 is just a tiny switch that we use in debug/test
meaniing that if we are building via usb we dont want
usb to touch also the batteries...and thats why tiny switch is there.

d)In the scheme i have only 2 molex for batteries (2x3 holes)
but right of them we have space also for 2 more meaning
i can support 4 parallel sets of ...(3 AA batteries personally)

e)You can see that the red line (battery +) coming out from main relay(zettler)
goes not only to step up/down converter (as VIN)
but also to the second channel of sensor relay so that when triggered 
red line is touching analogue pin A6 !

f)Except relays.. all the others ... i have them with header pins
in my...prototype....
So in image below the rounded rectangles of
-arduino
-voltage regulator
-sim800l
-hx811
-lcd
-possible rtc
-and keypad
are not directly on breadboard but via pin headers.
Especially arduino has doubled pin headers by the thinking that
maybe someday i may want to do some ...experiments...and have not only 
the females pins (from the doubled pin headers)
but also arduino has long-normal male-male pins
that those males go to females that females is stable on breadboard.













  










Below you can see the capacitor for signal strenght.
Seems that is on 2nd pin but its on 3rd(from the perspective of photographer)
Anyway the negative pin of capacitor touching the Black-negative of battery
and positive of capacitor is touching Red positive of battery)
In the picture and in reality i have it there (in the pins for Pololu step up/down)
because i just added after releasing the project.
You can place it where ever you want,....




















































Before the code in oder to be able to adjust the DHT to 11 or 12 or 21 or 22 
(even if it is just a simple future of the program and i have test and use only dht 11)
we need to change the private access to public
in the uint8 _type field of dht library
for succesfully compiling

(Or you can just erase the private:  and save the file..i think its ok...)














The Code below .....  has many comments for someone to understand !

Supports SMS to be send every 4 measurements but this
was the purpose when i begun the project Sep 2020.
After we saw that sms is very expensive
i find (2021) the thingspeak solution that is VERY CHEAP
(may be only 10 euros per year...here in Greece-vodafone)
and added also some SMS support ...but i dont use sms ..anyway.

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


//const char strJimakoskx[] PROGMEM = "jimakoskx@all";//ser.println((__FlashStringHelper*)strJimakoskx);}


#include <SoftwareSerial.h>

#include <EEPROM.h>

#include <LiquidCrystal_I2C.h>
/*update June 2022 This library have been changed 
but i think if you install this
https://arduinogetstarted.com/tutorials/arduino-lcd-i2c
 Arduino - LCD I2C | Arduino Tutorial (arduinogetstarted.com)
and change lcd.begin() to lcd.init() then the code will compile..(but i still have not test if runs)
*/

#include <DHT.h>//from Adafruit ..you must change the private aceess of dht._type to public in order to compile (just open dht.h and delete line "private:" and save.)

#include <LowPower.h>///from rocketscream LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); 
//https://github.com/rocketscream/Low-Power




/**

   using sim commands

   //"AT";"OK";"AT+CSQ";"AT+CBC";"ATD+ +";"ATH";"AT+CMGF=1";"AT+CMGS=\""; //+phone +\" + new line+26||27(escape)

   new version allow php/ThingSpeak

   Email not possible because of SPAM killers...etc

   but Thanks to ThingsSpeak https://thingspeak.com/

   and Zygi.com https://zygi.gr/el/install/

   and php lan+gauge

   we can easilly upload data on the web

   //"AT+SAPBR=3,1,\"Contype\",\"GPRS\"";"AT+SAPBR=3,1,\"APN\",\"";//+apn+\" //example AT+SAPBR=3,1,"APN","internet.vodafone.gr"

   //"AT+SAPBR=1,1";"AT+SAPBR=2,1";//this is to test if we get IP

   //"AT+HTTPINIT";"AT+HTTPPARA=\"CID\",1";//AT+HTTPPARA="CID",1

   //"AT+HTTPPARA=\"URL\",\"";//+url+\" //example AT+HTTPPARA="URL","http://miliohm.com/miliohmSIM800L.php"

   //AT+HTTPACTION max responce is ~5 seconds if normal query

   //"AT+HTTPACTION=0";//GET action

   //"AT+HTTPREAD";//read what get


  //designed for Arduino Pro Mini with A6,A7 pins

  //Hx711 normal operation < 1.5mA, power down < 1uA ,Operation supply voltage range: 2.6 ~ 5.5V

  //DHT11 Measure 0.3mA Standby 60μA ,3 to 5V,,, time data, to be read twice in a row, but not recommended repeatedly r ead sensors, each sensor

  reading interval of more than 5 seconds to obtain accurate data.


  SIM800L typical power consumption in sleep mode is 0.7 mA

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

  Lets compute for a normal operation of 6 readings per day (every 4 hours)

  and lets assume that a reading/send operation wants 1 minute(1.5 if we have sms also)

  (If signal is ok ..then we need about 30 seconds for sending data to ThingSpeak..)


  case a,Latch Sensor relay ,best solution

  case b,Simple Sensor relay like RAYEX ELEC RS-05 (tested ,EMF can be easilly managed)

    that has coil 167 Ω so typical (in 5 volts-but we run at 4) has 30 mA consumption

    So in 60 readings (opening relay for 1 minute) we will burn 30mA

    That is 30 mA per 10 days (TEN DAYS) !!!!!!

  case c , No sensor relay at all

    then just SIM800 will burn (minimum) 0.7*24 hours=16 mA per day

    adding a little more (just for be sure) we have 20 mA per 1 day !!!...so having relay is better

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

  That is why we need some sensors relay especially if we are going to operate with some readings per day.


  But if someone wants to operate lets say every 5 minutes then

  it is better (beyond the best choice of having LATCH RELAY)... to NOT have normal relay(non latching).

  Because every 5 minutes means 12 readings per 1 hour , 144 readings(relay opening) per day.

  And 144 openings per day mean about 72 mA per Day.....So it is best to have SIM on sleep mode burning only 16mA per 1 day !




  //Hx711 needs 1.5 mA *5 =7.5    +0.3mA for Dht      =7.8mA < 40 mA than Arduino Pin can give

  //But in order to have only one relay i will give this 7.8mA to sim relay

  //So..only one relay for sim800l ,,,, 5 load sensors and dht

  //have in mind that if no use latch relays or if use only one latch relay we can have 6 scales.....on a pro mini !!!!

  //Dht will be disabled if we use ALL FIVE SCALES because we dont have pins in my 2 LATCH version....

  //If indeed have 5 load scales ...then we are big honey producers and not weather/scientific station !!!


   http://www.gammon.com.au/power

  Below 1.8V - cannot operate

  Below 4 MHz - use at least 1.8V

  Between 4 MHz and 10 MHz you require: Volts = 1.8 + ((M - 4) * 0.15)

  Between 10 Mhz and 20 MHz you require: Volts = 2.7 + ((M - 10) * 0.18)

  You can supply an absolute maximum of 5.5V

  From 1.8V to 2.7V: M = 4 + ((V - 1.8) / 0.15)

  From 2.7V to 4.5V: M = 10 + ((V - 2.7) / 0.18)


  For 16Mhz ->Volts = 2.7 +(6*0.18)= 3.78 =3.8 Volts Minimum

  At 4.0 working also the cheap relays TQ2-L2-5V PANASONIC(min 3.75) ,Axicom  V23079B1201B301 (min 3.75)


  So if want 3.5 (to be able to run SIM800L at lowest plus some ) we need run BELOW Mhz = 10+0.8/0.18=14.445 so we need 3.3 Version of pro mini

  But the problem is that in 3.3 a)We cant? run LCD b)I cant find relay that can operate without transistor.(Meaning 3 Volt relays usually wants more than 40mA to trigger)

  Omron(Latching) G6AK-274P-ST-US4.5 (20x10x9)has  112 coil resistance and can be triggered at 70% *4.5= 3.15 Volts but not possible to find  this on retail.and also dangerous if apply 4.5 volt because 4.5/112 =41 mA...but safe if 4.4

  Omron(Latching) G6AK-274P-ST-US5 has  139 coil resistance and can be triggered at 70% *5=3.5 !!! on the limit (in case we want esp32 that has max=3.6)

  And the cheap Zettler AZ850P2–5 (14x9x5) has 125 coil resistance that is on the limit of 40mA if apply 5 Volt (5/125=40) but safe if apply less .So 3.5/125=28 mA!!! good !!


  Conclusion that with G6AK-274P-ST-US5 we can regulate at 3.55(i choose 4 volts) that is

  a)enough for sim800l

  b)low enough for arduino to not loose power

  c)easy to operate also with esp32(in case someone wants esp32)

  d)Can operate safelly also at maximum of 10 Volts .

  But....in the case of LCD with I2C we need more volts

  ..at least 4...with potensiometer of i2c full.....or just have one more Pololu regulator CHIPE 5 euros ,special for this .


  Note that 3.3 pro mini version has 4.8(5) volts on Vcc when power from usb ...meaning in case of debugging we dont have problem to feed lcd from vcc.


  But i will use 8Mhz Pro mini powered at 4.0 that is ok also for lcd.and with 5 volt relays.!!!!!

  And of-cource eliminate the led from Arduino!!!

*/










#define LATCH_SENSORS_RELAY

#define LATCH_MAIN_RELAY




//#define DEBUG

//#define DEBUGFULL //lower priority showings?!?...

//#define DEBUG_SIM_ANSWER /showing what sim answering on varius commands




//#define USE_DEFINED_MILLIS_PER_READING //uncomment this to debug quicker using the below value

unsigned long millisPerReading = 120000ul ; //2 minutes


byte currMenu = 1; //=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

const byte menuCount = 16;//update this if you add more menus

bool menuUnlocked = 0; //0 on release allows menu_1 even if locked for quick change batteries and adjust the delay hours....



//find your own address if not 0x27

LiquidCrystal_I2C lcd(0x27, 16, 2);//i need a minimum 16 columns and 2 rows lcd.

String lcd0;

String lcd1;

void lcdEmptyStrings01() {

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

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

}

void lcdClearToStrings01() {

  lcd.clear();

  lcd.print(lcd0);

  lcd.setCursor(0, 1);

  lcd.print(lcd1);

}

void lcdQuickPrint(const char str[], unsigned long millToDelay) {

  lcd.clear();

  lcd.print(str);

  delay(millToDelay);

}




bool ModeLcd;//LCD MODE(administration) vs SMS MODE(running basic purpose)




//Pin 13 (led) may cant go on some DT because has resistor and with the pullup resistor will create divider....

//Basically i wanted 13 to be to MainRelayPowerOff pin but this is not possible due to the fact that default bootloader uses 13

//on startup so the relay will be triggered and even if the solution is just to hold poer On button a little more

//i dont like it (streching the power latch relay

//And even if i can change the bootloader ...i also dont like it for open to public version.

//But if you like ,,,change the bootloader and assign 13 to (best solution) PinRelayMainPowerRESET

//and just having a small switch for debugging cases ....







//5 scales

const uint8_t PinHxDt[] = {11, A2, 6, 2, 8}; //INPUT_PULLUP

const uint8_t PinHxSck[] = {10, 13, 5, 3, 9}; //OUTPUT (13 is problem in case we want power down on Hx711 SCL)


const uint8_t PinDht = 8; //(,or 9 but better 8 because is pulled up from scale!?! )only if 5th scale not working !!!! automatically by library INPUT_PULLUP & OUTPUT


//WARNING : green sim800l module(the smallest/nicest and without the fucking capacitor that needs remove (or a trick or one capacitor in our circuit) to not reset my arduino )

// has REVERSED its Tx Rx pins....meaning Grnd,Rx,Tx,Rst,Vcc ....but RED module has Grnd,Tx,Rx,Rst,Vcc,Antenna !!!!!!!!! SOS SOS SOS

//so...you may reverse the assigned numbers below at PinSimTx,PinSimRx ...!!!!!!!!!!!!!!!!!!!!!

//t7,R4 Red ,,,,, t4,r7 green

uint8_t PinSimTx = 7; // our Rx ,SoftwareSerial INPUT_PULLUP

uint8_t PinSimRx = 4; // our Tx ,SoftwareSerial OUTPUT

const uint8_t PinRelaySensorsSet = A1;  

const uint8_t PinRelaySensorsRESET = A0; 


const uint8_t PinButMenuOk = 12; // INPUT_PULLUP.Pressing this button on power on enter as sms mode.else lcd mode for adminlcd switch must be open ion this case).

const uint8_t PinButRollEdit = A7; //A7 external pulled up






#ifdef LATCH_MAIN_RELAY

const uint8_t PinRelayMainPowerRESET = A3; // OUTPUT  for the case of LATCH MAIN RELAY

const uint8_t PinPowerRead = A6; // INPUT for reading battery for the case of LATCH MAIN RELAY

int powerAnalogReading;

int powerReadingTranslated0;

byte continiusllyTranslatedBelowThreshold = 0;

#endif



void blinc(int times = 1) {

  digitalWrite(13, HIGH);

  delay(10);

  digitalWrite(13, LOW);

  --times;

  while (times > 0) {

    delay(490);

    digitalWrite(13, HIGH);

    delay(10);

    digitalWrite(13, LOW);

    --times;



  }

}




#ifdef DEBUG

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

#endif


void setPinModes() {

  pinMode(PinButMenuOk, INPUT_PULLUP);


  resetPinModes();



  pinMode(PinRelaySensorsSet, OUTPUT);

  pinMode(PinRelaySensorsRESET, OUTPUT);


#ifdef LATCH_MAIN_RELAY

  pinMode(PinRelayMainPowerRESET, OUTPUT);

#endif

}





const byte millisForLatch = 25; //more than enough for omron(5 ms),zettlet(2 ms),panasonic (4 ms (without reset diode!!!)),



//this is for the RED module that has a big capacitor and provokes reseting if not have this trick

//The green module (the smallest) that has REVERSE Tx and Rx pins has no problem

//and we can skip this 3000 delay...but i am leaving this because .....i thing must be initial HIGH in any case for communication

/*

   But i finally removed this capacitor with a plier !!..and all working well...at least for me

  void avoidResetBySimByLoadingSimCapacitorViaComPins() {

  pinMode(PinSimTx, OUTPUT);

  //pinMode(PinSimRx, OUTPUT);//already output

  delay(10);

  digitalWrite(PinSimTx, HIGH); //1)1st line of SALVATION CODE

  digitalWrite(PinSimRx, HIGH); //2)2nd line of SALVATION CODE

  //3)3rd line of SALVATION CODE

  delay(3000);//just for SIM VCC to clamp at 2.24 volt !!! critical for when relay triggered(1000 is ok ...but...)


  }

As of last upgrate May 2021 i was STUPID removing capacitor from red module
because we need in either case a >1500 uf capacitor to have good SIGNAL SOS!!!
So dont remove any caacitor and just add one on your circuit !!!

*/



void relaySensorsTrigger() {


  //avoidResetBySimByLoadingSimCapacitorViaComPins();fuck this line trick...



#ifndef LATCH_SENSORS_RELAY

  digitalWrite(PinRelaySensorsSet, HIGH);

#endif


#ifdef LATCH_SENSORS_RELAY  //now saving power !!!

  digitalWrite(PinRelaySensorsSet, HIGH);

  delay(millisForLatch);

  digitalWrite(PinRelaySensorsSet, LOW);

#endif


  //delay(100);

  //pinMode(PinSimTx, INPUT_PULLUP);//back to pullup ..just in case for not using resetPinModes()


}






void relaySensorsUnTrigger() {

#ifndef LATCH_SENSORS_RELAY

  digitalWrite(PinRelaySensorsSet, LOW);

#endif


#ifdef LATCH_SENSORS_RELAY  //now saved ,, power !!!

  digitalWrite(PinRelaySensorsRESET, HIGH);

  delay(millisForLatch);

  digitalWrite(PinRelaySensorsRESET, LOW);

#endif

}



#ifdef LATCH_MAIN_RELAY

void relayMain_POWER_OFF() {

  digitalWrite(PinRelayMainPowerRESET, HIGH);

  delay(millisForLatch);//must not go here if switch for main reset allows !!!

  digitalWrite(PinRelayMainPowerRESET, LOW);///but having this in case we have the switch off...!!!!for debugging!

}

#endif



//DONT PRINT FLOATS TO SAVE FLASH memory !!!

DHT dht(PinDht, 11);

float t , h ;

void dhtRead() {

  t = dht.readTemperature();

  h = dht.readHumidity();

}






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

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

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

// MAIN STORAGE LOGIC

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

const byte ScalesCanHandle = 5;

const byte SensationsPerSms = 4;//4 readings before send sms.If sms length>150...could adjust this according free sram but now we are limited

const byte StoredWeightsLength = ScalesCanHandle * SensationsPerSms * 2;

long StoredWeights[ StoredWeightsLength ] ;//long is ok for ThingSpeak but in sms better send as divided by 100 or at least 10 to save some chars in case using 5 scales

char temperature[SensationsPerSms];

char humidity[SensationsPerSms];


const char DISABLED_CHAR = -2;

const char ERROR_CHAR = -1;

void clearStoredWeights() {

  int i = 0;

  while (i < StoredWeightsLength) {

    StoredWeights[i] = DISABLED_CHAR;

    ++i;

  }

}


//using sram on Callibration

int iTemp32, iTemp33, iTemp34;

long tempStandart[8];

long tempAverage[8];

void sortInsertionTempStandartsAndAveragesByStandarts(int RequestedStandarts) {

  int i,  j;

  long key, followKey;

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

    key = tempStandart[i];

    followKey = tempAverage[i];

    j = i - 1;

    while (j >= 0 && tempStandart[j] > key) {

      tempStandart[j + 1] = tempStandart[j];

      tempAverage[j + 1] = tempAverage[j];

      j = j - 1;

    }

    tempStandart[j + 1] = key;

    tempAverage[j + 1] = followKey;

  }

}



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

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

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

//WEIGHT LOAD SCALE SENSOR XH711 LOGIC

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

//AVOIDING use library for Flash economy saving 146 FLASH +73 SRAM bytes and i copied the algorithm from https://github.com/bogde/HX711

/*#include <HX711.h>

  HX711 scale0;

  HX711 scale1;

  HX711 scale2;

  HX711 scale3;

  HX711 scale4;

*/


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

//SOS not float...loosing FLASH...use float on translating

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

byte currScale;

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



//pulses=gain , 1=ChannelA@128 , 2=ChannelB@32 , 3=ChannelA@64

bool readWeight(byte DOUT, byte PD_SCK) {

  lastReading = 0;

  lastGrams = ERROR_CHAR;

  uint8_t filler = 0;


  while (digitalRead(DOUT) == HIGH) {

    ++filler;//waiting xh711 for signal

    delay(1);

    if (filler > 250) { //if no signal after 250 millis....go away

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

      return false;

    }

  }



  //filler = 0x00;//normal use of this variable...

  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 128 amplification,only one pulse (2 for 32 Channel B and 3 for 64 Channel A)

  digitalWrite(PD_SCK, HIGH);//25 th pulse

  digitalWrite(PD_SCK, LOW);



  //////back to interrupts

  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

  if (data2 & 0x80) {

    filler = 0xFF;

  } else {

    filler = 0x00;

  }

  // 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;

  return lastReading != 0 ; //most likelly must not be zero

}



/*

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

*/

void translateByScale(byte scale, bool useTareAlso) {

  //ser.print(F("translateByScale :  "));ser.println(scale);


  byte LoadsUsing = romReadScaleStandartsUsing(scale);

  //if (LoadsUsing < 2) {//ser.println(F("Not callibrated ,leaving new users to callibrate and understand hx711"));}

  if (LoadsUsing > 1) { //1st is for zero average and we need at least 1 more average to calculate




    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);



    //Now i must be in proper position

    avpreOrForZero = romReadScaleAverage(scale, 0);

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

    float floatLastGrams = lastReading;

    floatLastGrams -= avpreOrForZero;

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

    floatLastGrams /= (av - avpreOrForZero);

    if (useTareAlso) {

      floatLastGrams -= romReadScaleGramsDif(scale);

    }

    lastGrams = (long)floatLastGrams;

  }


}


bool readWeightOld(byte scale, bool useTareAlso, bool translate) {

  bool out = readWeight(PinHxDt[scale], PinHxSck[scale]);

  if (out && translate) {

    translateByScale(scale, useTareAlso);

  }

  return out;

}

bool readWeight(byte scale, bool useTareAlso, bool translate,byte times=1) {

  bool out = readWeight(PinHxDt[scale], PinHxSck[scale]);

  if (out) {

    //first time succesfully reads

    if(times>1){

      ///take average.....to lastReading


      float sums=lastReading;

      float divider=1;

      while(times>1){

        delay(10);

        out = readWeight(PinHxDt[scale], PinHxSck[scale]);//may override the final return....here

        if(out){

          divider+=1;

          sums+=lastReading;

          #ifdef DEBUG

          ser.println(lastReading);

          #endif

        }

        //else not check the failures....i am boring

        --times;

      }


      sums/=divider;

      lastReading=sums;

      

    }

    if(translate){

      translateByScale(scale, useTareAlso);

    }

  }

  return out;

}

bool readCurrScaleWeightOnly() {

  return readWeight(currScale, false, false);

}


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

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

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






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

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

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




void romReadToStringTrimmed(int address, int addressEnd, const String & output) {

  char chNotSpace;

  while (address < addressEnd) {

    chNotSpace = (char)EEPROM.read(address);

    if (chNotSpace != ' ') {

      output += chNotSpace;

    }

    ++address;

  }

}

void romReadToString(int address, int addressEnd, const String & output) {

  while (address < addressEnd) {

    output += (char)EEPROM.read(address);

    ++address;

  }

}

void romUpdateFromString(int address, int addressEnd, const String & input, int inputIndex, bool fillWithSpaces) {

  int len = input.length();

  int adL = addressEnd;

  int adDif = adL - address;

  if (len < adDif) {

    adL = address + len;

  }

  while (address < adL) {

    EEPROM.update(address, (byte)input.charAt(inputIndex));

    ++address;

    ++inputIndex;

  }

  if (fillWithSpaces) {

    while (address < addressEnd) {

      EEPROM.update(address, 32);

      ++address;

    }

  }

}

long romReadLong(int address) {//saving some Flash not using EEPROM.put,get

  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);

}

//[-32768,32767]

int romReadInt(int address) {

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

  int out = b;  out <<= 8;

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

  out += b;

  return out;

}

void romUpdateInt(int address, int out) {

  address += 1;

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

  out >>= 8;

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

}




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

const byte programID = 231;

byte romReadProgramId() {//some kind of insurance to default know if must flash EEPROM (on new installations)

  return EEPROM.read(0);

}

//void romUpdateProgramId(){EEPROM.update(0,programID);}

//bool checkProgramId(){return romReadProgramId()==programID;}

// A D D R E S S S = [1,5)

void romReadSimPin(const String & output) {//4 digits pin is also the password for program

  romReadToString(1, 5, output);

}

void romUpdateSimPin(const String & input, int inputIndex) {

  romUpdateFromString(1, 5, input, inputIndex, false);

}

// A D D R E S S S = 5

byte romReadPinEnabled() {//have this disabled when test but most propably enabled (from your phone) before put sim on SIM800L

  return EEPROM.read(5);

}

void romUpdatePinEnabled(byte val) {

  EEPROM.update(5, val);

}

// A D D R E S S S = 6

byte romReadSimSecWaitAfterPowerOn() {//old version using this but know i am reading sim answers for registering to network....most propably!

  return EEPROM.read(6);

}

void romUpdateSimSecWaitAfterPowerOn(byte SecondsSimDelayWaitingNetwork) {

  EEPROM.update(6 , SecondsSimDelayWaitingNetwork);

}

// A D D R E S S S = 7

byte romReadSimSecWaitBeforePowerOff() {//this kind of using but not so important

  return EEPROM.read(7);

}

void romUpdateSimSecWaitBeforePowerOff(byte SecondsSimDelayBeforePowerOff) {

  EEPROM.update(7 , SecondsSimDelayBeforePowerOff);

}


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


// A D D R E S S S = 8

byte romReadSmsEnabled() {//0 disabled,1 primary only ,2 secondary only,3 primary+secondary

  return EEPROM.read(8);

}

void romUpdateSmsEnabled(byte val) {

  EEPROM.update(8, val);

}

// A D D R E S S S = [9,21)

void romReadSmsPrimaryNumber(const String & output) {//the phone number sending the sms

  romReadToString(9, 21, output);

}

void romUpdateSmsPrimaryNumber(const String & input, int inputIndex) {

  romUpdateFromString(9, 21, input, inputIndex, false);

}

// A D D R E S S S = [21,33)

void romReadSmsSecondaryNumber(const String & output) {//secondary phone number sending the sms

  romReadToString(21, 33, output);

}

void romUpdateSmsSecondaryNumber(const String & input, int inputIndex) {

  romUpdateFromString(21, 33, input, inputIndex, false);

}


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


// A D D R E S S S = 33

byte romReadDhtType() {//11,12,21,22,,,just in case testing with chip dht11 and in future want expensive and accurate dht22 (5 euros)

  return EEPROM.read(33);

}

void romUpdateDhtType(byte DhtType) {

  EEPROM.update(33 , DhtType);

}

// A D D R E S S S = 34

byte romReadDhtEnabled() {//better disable this if you dont put dht

  return EEPROM.read(34);

}

void romUpdateDhtEnabled(byte DhtEnabled) {

  EEPROM.update(34 , DhtEnabled);

}


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



// A D D R E S S S = 35

byte romReadHourDelayBeforeSMSwhile() {//this is for as much possible to adjust the readings IN the day

  return EEPROM.read(35);//example.I am on field 8:00.If normal then i will be taking readings 8,14,20,2

  //but 20:00 may summer some bees are still out...(lets say) and may want 10,16,22,4

  //so i can quickly put 2 hours delay ....about...

}

void romUpdateHourDelayBeforeSMSwhile(byte HourDelay) {

  EEPROM.update(35 , HourDelay);

}

// A D D R E S S S = 36

byte romReadHoursPerReading() {

  return EEPROM.read(36);

}

void romUpdateHoursPerReading(byte HoursPerDay) {

  EEPROM.update(36 , HoursPerDay);

}


// A D D R E S S S = 37

byte romReadCallInsteadSms() {//0 means disabled..1 means call on each reading(if php on and not sending sms....just added this in the end)

  return EEPROM.read(37);

}

void romUpdateCallInsteadSms(byte CallOn) {

  EEPROM.update(37 , CallOn);

}


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

// A D D R E S S S = 38

byte romReadPhpEnabled() {//new version use ThingSpeak but may also be used in your own php somewhere in net

  return EEPROM.read(38);

}

void romUpdatePhpEnabled(byte val) {

  EEPROM.update(38, val);

}


//ThingSpeakKey[30],Apn[30],PhpServer[30],PhpQuery[30]

//total=120 chars must be available on EEPROM

//Note 1)php server NO NEED to include final '/'

//Note 2)php server not working on httpS (ssl) on sim800L.But working if you have it simple as http even if real server is https

//Note 3)I am not supporting username and password for APN....most APN have empty those

//Note 4)In thingspeak server fields following update? have names 'field1= up to 'field8='. I am not supporting changing those names ...

//in case thingspeak change or build your own php server.Could make this also for 'api_key=' ...but anyway....now 'api_key=' is editable....

//Note 5)I am supporting editing anly in small letters....should work fine also for api_key.Because will be more hard to roll on +26 ab chars with a simple button...

//So when on lcd rolling and reach 'A' i am going to 'a'

//post example

//https://api.thingspeak.com/update?api_key=7I2NQSK2L0AQEW44&field1=0

//must be WITHOUT S !!!

//http://api.thingspeak.com/update?api_key=7I2NQSK2L0AQEW44&field1=0

void romUpdateSpaceToEmailFields() {

  int address = 39;

  int addressEnd = 159;

  while (address < addressEnd) {

    EEPROM.update(address, 32); //' '=32 Space

    ++address;

  }

}


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

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


void romReadThingSpeakKey(const String & output) {//api_key=7I2NQSK2L0AQEW44

  romReadToStringTrimmed(39, 69, output);

}

void romUpdateThingSpeakKey(const String & input, int inputIndex) {

  romUpdateFromString(39, 69, input, inputIndex, true);

}

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

void romReadApn(const String & output) {//internet.vodafone.gr

  romReadToStringTrimmed(70, 100, output);

}

void romUpdateApn(const String & input, int inputIndex) {

  romUpdateFromString(70, 100, input, inputIndex, true);

}

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

void romReadPhpServer(const String & output) {//http://api.thingspeak.com

  romReadToStringTrimmed(101, 131, output);

}

void romUpdatePhpServer(const String & input, int inputIndex) {

  romUpdateFromString(101, 131, input, inputIndex, true);

}

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

void romReadPhpQuery(const String & output) {//update?

  romReadToStringTrimmed(132, 162, output);

}

void romUpdatePhpQuery(const String & input, int inputIndex) {

  romUpdateFromString(132, 162, input, inputIndex, true);

}


// A D D R E S S S = 162

int romReadEstimatedMillisOf8Seconds() {//default 8500.(8000,9000) the millis of one time LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF) cycle

  return romReadInt(162);

}

void romUpdateEstimatedMillisOf8Seconds(unsigned int PositiveMillisOf8Seconds) {

  romUpdateInt(162, PositiveMillisOf8Seconds);

}


// A D D R E S S S = 164

int romReadBatteryThreshold() {//if connecting battery (via Mega Resistor) to some analog pin

  return romReadInt(164);//then have this value (directly not translated to volts) to know that if read below this

}//we must Power Off Arduino (if we have a latching relay version)

void romUpdateBatteryThreshold(unsigned int PositiveBelowWillTurnOfFromLatchRelay) {

  romUpdateInt(164, PositiveBelowWillTurnOfFromLatchRelay);

}


// A D D R E S S S = 166

int romReadUseGreenSimAndMustChangePins() {//old romReadMaxSimulatedDays now used for Green On card (meaning change pins on runtime)

  return romReadInt(166);

}

void romUpdateUseGreenSimAndMustChangePins(unsigned int PositiveAboveWillReverseThePinoutSim) {

  romUpdateInt(166, PositiveAboveWillReverseThePinoutSim);

}

// A D D R E S S S = 168

byte romReadMinutesPerReading() {

  return EEPROM.read(168);

}

void romUpdateMinutesPerReading(byte MinutesPerDay) {

  EEPROM.update(168 , MinutesPerDay);

}


// A D D R E S S S = 169

byte romReadBelowBatteryThresholdAcceptableTimes() {

  return EEPROM.read(169);

}

void romUpdateBelowBatteryThresholdAcceptableTimes(byte TimesAcceptableFoundBelowThreshold) {

  EEPROM.update(169 , TimesAcceptableFoundBelowThreshold);

}



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

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


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

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

// FREE A D D R E S S S = [170,210)

//leaving some space ...for future even not believe



////////// SCALEs ////A D D R E S S S = 210 ////////////////////////////////////

//each scale has

//1 byte for if is enabled on sms mode .(in lcd always showing)

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

//1 long (TARE)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 + 1 + 4 + 32 + 32 = 70 bytes on rom

// Scale 0 : A D D R E S S S = 210  +70

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

// Scale 2 : A D D R E S S S = 350 + 70

// Scale 3 : A D D R E S S S = 420 + 70

// Scale 4 : A D D R E S S S = 490 +70

//free at 560 eeprom

//byte temperatureCallibrated????

byte romReadScaleEnabled(int scale) {

  return EEPROM.read(210 + scale * 70);

}

byte romReadScaleStandartsUsing(int scale) {

  return EEPROM.read(211 + scale * 70);

}

long romReadScaleGramsDif(int scale) {//tare ...in grams(translated reading)


  return romReadLong(212 + scale * 70);

}

long romReadScaleStandart(int scale , int standart) {//12074

  return romReadLong(standart * 4 + 216 + scale * 70);

}

long romReadScaleAverage(int scale , int standart) {

  return romReadLong(standart * 4 + 248 + scale * 70);

}


void romUpdateScaleEnabled(int scale, byte val) {

  EEPROM.update(210 + scale * 70 , val);

}

void romUpdateScaleStandartsUsing(int scale, byte val) {

  EEPROM.update(211 + scale * 70 , val);

}

void romUpdateScaleGramsDif(int scale, long val) {

  romUpdateLong(212 + scale * 70, val);

}

void romUpdateScaleStandart(int scale , byte standart, long val) {

  romUpdateLong(standart * 4 + 216 + scale * 70, val);

}

void romUpdateScaleAverage(int scale , int standart, long val) {

  romUpdateLong(standart * 4 + 248 + scale * 70, val);

}



// FREE A D D R E S S S = 560


bool romMustFlush() {

  if (EEPROM.read(0) != programID) {

    return true;

  }

  byte b = 0;

  while (b < ScalesCanHandle) {

    if (romReadScaleStandartsUsing(b) > 8) {

      return true;//for sure

    }

    ++b;

  }

  return false;//most propably!!!



}

void romFlash() {//reset to defaults

  int addr = 0;

  EEPROM.update(addr, programID);

  ++addr;


#ifdef DEBUG

  while (addr < 31) {

    ser.println(F("Flushing"));//have this to be seen in case of..error.(happens once!)

    ++addr;

  }

#endif


  String str;

  str += F("1111"); romUpdateSimPin(str, 0); str.remove(0, str.length());

  romUpdatePinEnabled(0);//false

  romUpdateSimSecWaitAfterPowerOn(20);//20 seconds MAX wait after sim power on in order to have network.

  romUpdateSimSecWaitBeforePowerOff(13);//13 seconds wait lets say after we send sms

  romUpdateSmsEnabled(0);//NONE phone enabled to ensure new users save money not sending sms

  str += F("306949496595"); romUpdateSmsPrimaryNumber(str, 0); str.remove(0, str.length());

  str += F("306949496595"); romUpdateSmsSecondaryNumber(str, 0); str.remove(0, str.length());

  romUpdateDhtType(11);

  romUpdateDhtEnabled(1);

  romUpdateHourDelayBeforeSMSwhile(0);//0.Directly start day-looping

  romUpdateHoursPerReading(6);//4 readings per day default...1 sms per day...

  romUpdateMinutesPerReading(0);

  romUpdateCallInsteadSms(1);//Always ensure....calls instead of sms to save money...for new users....Change this by lcd mode if you want spend money

  romUpdatePhpEnabled(1);//enabled...new future.You must have or create a channle on thingspeak.http://api.thingspeak.com

  romUpdateSpaceToEmailFields();

  str += F("api_key=7I2NQSK2L0AQEW44"); romUpdateThingSpeakKey(str, 0); str.remove(0, str.length());

  str += F("internet.vodafone.gr"); romUpdateApn(str, 0); str.remove(0, str.length());

  str += F("http://api.thingspeak.com"); romUpdatePhpServer(str, 0); str.remove(0, str.length());

  str += F("update?"); romUpdatePhpQuery(str, 0); str.remove(0, str.length());



  //To adjust estimated millis of 8 seconds here is an example

  //If was 8772

  //and between readings of 2 hours (7.200.000 millis)

  //i had a difference of MINUS -4 minutes(240.000 millis)

  //then

  //a)7.200.000 / 8772 = 820 times we powered down for '8' seconds

  //b)Sharing out the difference to the times ... 240.000 / 820 = 293 diff millis per each time that we power down

  //c)And because we are MINUS 4 minutes the new value (estimated) is 8772 - 292 = 8479 !!!!!

  romUpdateEstimatedMillisOf8Seconds(8500);


  //For a 16 Mhz Pro mini we must have at least 3.78 Voltage.

  //So i will run at about 3.9 volts from 1,2,3! or 4! parallel sets of 3 AA rechargables.

  //Each set starting fully at about 4.35 volts and must not go below ~1.2 volt/cell so totally 3.6 volts.

  //For the range 4.35 till 3.9 we need a step down converter

  //For the range 3.6 till 3.9 we need a step up converter

  //Pololu 2868 and 2869 is fine (step-up/down) .(2868 has also auto shut down when below some voltage)

  //running at 3.9 will read 1023

  //           3.6       x?(=944 = 1023*3.6/3.9)

  //x~=940

  //In case that i use also Pololu Regulator 2868 that has also a second potensiometer for cut-off

  //then i will prefer to adjust this cut-off a little lower than 3.6(latch relay method for power off)

  //in order to most propably power-off from the latch relay and totally close application.

  //But having also a regulator with cut-off future like 2868...is more safest in case that....latch relay fails to work.

  //All this to be easy for someone to go on lithium batteries that need more CARE than Varta NIMH 2100 mA

  romUpdateBatteryThreshold(3450);//this is For MAIN POWER VIA LATCH RELAY VERSION,Cut Off below 3.45 volt

  romUpdateBelowBatteryThresholdAcceptableTimes(2);

  //new future trying to change si pins if using green card

  romUpdateUseGreenSimAndMustChangePins(0);


  byte sc = 0;

  while (sc < ScalesCanHandle) {

    romUpdateScaleEnabled(sc, sc % 2 == 0);

    romUpdateScaleStandartsUsing(sc, 0);//All scales are NOT callibrated on default

    romUpdateScaleGramsDif(sc, sc);//for debug....tares will be 0,4

    byte j = 0;

    while (j < 8) {

      romUpdateScaleStandart(sc, j, j * sc);//just have those ...for debug

      romUpdateScaleAverage(sc, j, -(j * sc));

      ++j;

    }

    ++sc;

  }



}

void romCheckToFlush() {

  if (romMustFlush()) {

    romFlash();

  } //else {ser.println("EEPROM good");}

}


void showeeprom() {

#ifdef DEBUG

  String str;

  ser.println(romReadProgramId());

  romReadSimPin(str); ser.println(str); str.remove(0, str.length());

  ser.println(romReadPinEnabled());

  ser.println(romReadSimSecWaitAfterPowerOn());

  ser.println(romReadSimSecWaitBeforePowerOff());

  romReadSmsPrimaryNumber(str); ser.println(str); str.remove(0, str.length());

  ser.println(romReadSmsEnabled());

  romReadSmsSecondaryNumber(str); ser.println(str); str.remove(0, str.length());

  ser.println(romReadDhtType());

  ser.println(romReadDhtEnabled());

  ser.println(romReadHourDelayBeforeSMSwhile());

  ser.println(romReadHoursPerReading());

  ser.println(romReadMinutesPerReading());

  ser.println(romReadCallInsteadSms());

  ser.println(romReadPhpEnabled());

  romReadThingSpeakKey(str); ser.println(str); str.remove(0, str.length());

  romReadApn(str); ser.println(str); str.remove(0, str.length());

  romReadPhpServer(str); ser.println(str); str.remove(0, str.length());

  romReadPhpQuery(str); ser.println(str); str.remove(0, str.length());


  ser.println(romReadEstimatedMillisOf8Seconds());

  ser.println(romReadBatteryThreshold());

  ser.println(romReadBelowBatteryThresholdAcceptableTimes());

  ser.println(romReadMaxSimulatedDays());

  byte sc = 0;

  while (sc < ScalesCanHandle) {

    ser.print(romReadScaleEnabled(sc)); ser.print(' ');

    ser.print(romReadScaleStandartsUsing(sc)); ser.print(' ');

    ser.print(romReadScaleGramsDif(sc)); ser.println(' ');

    byte j = 8;// 8 !! NO WHILE ...dont need too much debug!(yet)

    while (j < 8) {

      ser.print(romReadScaleStandart(sc, j)); ser.print(':'); ser.println(romReadScaleAverage(sc, j));

      ++j;

    }

    ++sc;

  }


#endif

}




//SoftwareSerial(ARDrxPin, ARDtxPin, inverse_logic)//INPUT_PULLUP,OUTPUT

SoftwareSerial* sim;


void resetPinModes() {

  byte b = 0;

  while (b < 5) {

    pinMode(PinHxDt[b], INPUT_PULLUP);

    pinMode(PinHxSck[b], OUTPUT);

    //digitalWrite(PinHxSck[b], LOW); //enable Hx711 in case we use HIGH on release...

    ++b;

  }

  pinMode(PinSimTx, INPUT_PULLUP);

  pinMode(PinSimRx, OUTPUT);

  digitalWrite(PinSimRx, HIGH); //critical....to be as when SoftwareSerial initilised !!!


}

void releasePinModesForPowerSaving() {


  byte b = 0;

  while (b < 5) {

    pinMode(PinHxDt[b], OUTPUT);

    //pinMode(PinHxSck[b], OUTPUT);//already output

    digitalWrite(PinHxDt[b], LOW);

    digitalWrite(PinHxSck[b], LOW); //HIGH);//ensuring Hx711 is in power down mode in all cases !??!! Problem if 13 is SCL.

    /*When PD_SCK pin changes from low to high

      and stays at high for longer than 60μs, HX711

      enters power down mode (Fig.3). When internal

      regulator is used for HX711 and the external

      transducer, both HX711 and the transducer will be

      powered down. When PD_SCK returns to low,

      chip will reset and enter normal operation mode.

      After a reset or power-down event, input

      selection is default to Channel A with a gain of

      128.*/

    ++b;

  }

  pinMode(PinSimTx, OUTPUT);

  //pinMode(PinSimRx, OUTPUT);//already output

  digitalWrite(PinSimTx, LOW);

  digitalWrite(PinSimRx, LOW);


}





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

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

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

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

  long out = 0;

  int maxlen = str.length();

  char cha;

  len += from;

  if (len > maxlen) {

    len = maxlen;

  }

  while (from < len) {

    cha = str.charAt(from); //skipping high zeros

    if (cha != '0') {

      break;

    }++from;

  }

  while (from < len) {

    cha = str.charAt(from);

    if (cha >= '0' && cha <= '9') {

      cha -= 48;

      out += cha;

      out *= 10;

    }

    else {

      break;

    }++from;

  } out /= 10;

  return out;

}


byte MenuOkPressed, MenuOkPrevPressed;

byte RollEditPressed, RollEditPrevPressed;


//12,A7 just Input with external pull up resistor buttons.Change if using other pins

void readButtons() {

  MenuOkPressed = !digitalRead(PinButMenuOk);

  RollEditPressed = analogRead(PinButRollEdit);

  //ser.print(MenuOkPressed);ser.print(" , ");ser.println(RollEditPressed);


  if (RollEditPressed > 126) {

    RollEditPressed = 0;

  } else {

    RollEditPressed = 1;

  }

  //ser.print(MenuOkPressed);ser.print(" , ");ser.println(RollEditPressed);


}

void storeButtons() {

  MenuOkPrevPressed = MenuOkPressed;

  RollEditPrevPressed = RollEditPressed;

}

//releasing below this time millis(666) then Ok button mean next menu and Roll button mean next char position

//else Ok mean store or act (depending menu) and Roll button rolls to next DIGIT (from '4' to '5'etc)

const unsigned long smallDiff = 666;//use 555 is better(easier editing especially on php -apn long strings) but 666 is 'safer'...


unsigned long lastMenuOkPressed;

unsigned long lastMenuOkReleased;

unsigned long lastRollEditPressed;

unsigned long lastRollEditReleased;


byte OkActionType;//0 no action,1 simpleAction (without pressing the other button),2 special Action (pressing both buttons)


byte lcdModeEditing;//0 lcd0 only,1 lcd1 only ,2 both ,3 none

byte lcdRowEditing;//0 or 1.Need lcd 16x2 at least

byte lcdCheckOnOff;//'n' or 'f' trick to enable disable

char lcdCharMin, lcdCharMax;//[0,9] for numbers or else for letters...skipping big letters

byte lcdColumnEditing;//[0,15]

byte lcd0Inc, lcd0Min, lcd0Max;//how much to increase when hold button more than 555 and release

byte lcd1Inc, lcd1Min, lcd1Max;//min,max the current limits i allow editing

void done() {//usually going to next menu...on Oks (Oks means hold more than 555 millis the Ok button)...else Ok button is used for rolling menu that does not do changes


  OkActionType = 0;//this is for loop break

  lcdQuickPrint("done", 666);

  nextMenu();

}


void nextMenu() {

  //ser.println("nextMenu");

  ++currMenu;

  if (menuUnlocked) {

    currMenu %= menuCount;

    if (currMenu == 0) {

      ++currMenu;//skip again entering password

    }

  }

  else {

    currMenu %= 2;

  }

  switchingPrepare();

}

void prevMenu() {

  //ser.println("prevMenu");

  if (menuUnlocked) { //only on unlocked menu have prev....to save some Flash

    if (currMenu < 2) {

      currMenu = menuCount;

    }

    --currMenu;

    switchingPrepare();

  }

}


void rollDigitPosition() {

  if (lcdModeEditing < 3) {

    if (lcdRowEditing == 0) {

      lcdColumnEditing += lcd0Inc;

      if (lcdColumnEditing > lcd0Max) {

        if (lcdModeEditing == 2) {

          lcdColumnEditing = lcd1Min;

          lcdRowEditing = 1;

        }

        else {

          lcdColumnEditing = lcd0Min;

        }

      }

    }

    else if (lcdRowEditing == 1) {

      lcdColumnEditing += lcd1Inc;

      if (lcdColumnEditing > lcd1Max) {

        if (lcdModeEditing == 2) {

          lcdColumnEditing = lcd0Min;

          lcdRowEditing = 0;

        }

        else {

          lcdColumnEditing = lcd1Min;

        }

      }

    }

    lcd.setCursor(lcdColumnEditing, lcdRowEditing);

  }


}

void rollDigitChar() {

  if (lcdModeEditing < 3) {

    if (lcdRowEditing > 0) {

      char cha = lcd1.charAt(lcdColumnEditing);

      checkChar(cha);

      lcd1.setCharAt(lcdColumnEditing, cha);

    }

    else {

      char cha = lcd0.charAt(lcdColumnEditing);

      checkChar(cha);

      lcd0.setCharAt(lcdColumnEditing, cha);

    }

    lcdClearToStrings01();

    lcd.setCursor(lcdColumnEditing, lcdRowEditing);

  }

}






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

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

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

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

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




void checkChar(char & cha) {

  if (lcdCheckOnOff && (cha == 'n' || cha == 'f')) {

    if (cha == 'n') {

      cha = 'f';

    }

    else if (cha == 'f') {

      cha = 'n';

    }

  }

  else if (cha < 124) { //'~'=126 trick char to not turn...

    ++cha;


    if (cha == 'A') {

      cha = '_';//i had it 'a' but may undersore need

    }

    if (cha > lcdCharMax) {

      cha = lcdCharMin;

    }

  }

}


byte checkToRoll() {

  if (RollEditPressed != RollEditPrevPressed) {

    if (RollEditPressed) {

      lastRollEditPressed = millis();

      return 1;

    }

    else {

      lastRollEditReleased = millis();

      if (lastRollEditReleased - lastRollEditPressed < smallDiff) {

        rollDigitChar();

        return 2;

      }

      else {

        rollDigitPosition();

        return 3;

      }

    }

  }

  return 0;

}

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

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

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

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

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
















void prepare0PasswordPinRequesting() {

  //ser.println("PasswordPinRequesting");

  //i am not showing in lcd "Enter password 1111"...in case of some stranger...user must know

  lcd0 += "1111";

  lcdModeEditing = 0;

  lcdRowEditing = 0;

  lcdColumnEditing = 0;

  lcdCharMin = '0';

  lcdCharMax = '9';

  lcdCheckOnOff = 0;

  lcd0Inc = 1;

  //lcd1Inc = 1;

  lcd0Min = 0;

  //lcd1Min = 0;

  lcd0Max = 3;

  //lcd1Max = 1;

  lcd.blink();

}

void loop0PasswordPinRequesting() {

  if (OkActionType) {

    String currPin;

    romReadSimPin(currPin);

    int i = 0;


    while (i < 4) {

      if (lcd0.charAt(i) != currPin.charAt(i)) {

        //ser.println("wrong");I am not showing on lcd that is wrong....in case some stranger....

        OkActionType = 0; //for discart the ok released

        return;

      }

      ++i;

    }

    menuUnlocked++;

    done();

  }

  else {

    checkToRoll();

  }

}


void prepare1SetHoursDelayAndSimulation() {

  //ser.println("SetHoursDelayAndSimulation");


  byte hoursDelay = romReadHourDelayBeforeSMSwhile();

  if (hoursDelay < 10) {

    lcd0 += '0';

  }

  lcd0 += hoursDelay;

  lcd0 += F(" HoursBefStart");


  lcd1 += F("/hs ");

  byte hoursSim = romReadHoursPerReading();

  if (hoursSim < 10) {

    lcd1 += '0';

  }

  lcd1 += hoursSim;

  lcd1 += F("|");

  byte minSim = romReadMinutesPerReading();

  if (minSim < 10) {

    lcd1 += '0';

  }

  lcd1 += minSim;

  lcd1 += F(" Minutes");


  lcdModeEditing = 2;

  lcdRowEditing = 0;

  lcdColumnEditing = 0;

  lcdCharMin = '0';

  lcdCharMax = '9';

  lcdCheckOnOff = 0;

  lcd0Inc = 1;

  lcd1Inc = 1;

  lcd0Min = 0;

  lcd1Min = 4;

  lcd0Max = 1;

  lcd1Max = 8;


  lcd.blink();

}

void loop1SetHoursDelayAndSimulation() {

  if (OkActionType) {

    //byte newHoursDelay = parselong0p(lcd0, 0, 2);

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

    //ser.print(newHoursDelay); ser.print(" , "); ser.println(newHoursSimulation);

    romUpdateHourDelayBeforeSMSwhile(parselong(lcd0, 0, 2));//not checking errors ..not nothing.Just primitive!

    romUpdateHoursPerReading(parselong(lcd1, 4, 2));//need flash !!!

    romUpdateMinutesPerReading(parselong(lcd1, 7, 2));//need flash !!!

    done();

  }

  else {

    checkToRoll();

  }

}



byte loopCounter;//usually showing every 256/3....but can change

bool showingReadingsAlso = false;

void checkRollOnTaringAndCallibratingMenus() {

  if (RollEditPressed != RollEditPrevPressed) {

    if (RollEditPressed) {

      lastRollEditPressed = millis();

    }

    else {

      lastRollEditReleased = millis();

      if (lastRollEditReleased - lastRollEditPressed < smallDiff) {

        //ser.println("show readings on/off");// and also util to tell to refresh simplest way in case of lets say utilise as simple scale !!!;

        showingReadingsAlso = !showingReadingsAlso;

        loopCounter = 0;//clear counter to directly take reading

      }

      else {

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

        ++currScale;

        currScale %= ScalesCanHandle;

        loopCounter = 0;//on next scale clear counter to directly take reading

        iTemp32 = 0;//only this different that we need on callibration

      }

    }

  }

}




void prepare2ShowingTaringSensors() {//roll light next scale,heavy ok tare.heavy roll readings view=!readings view

  //ser.println("ShowingTaringSensors");

  loopCounter = 0;

}

void loop2ShowingTaringSensors() {

  if (OkActionType) {

    //long newAver=takeAverage();

    //romUpdateScaleAverage();

    readWeight(currScale, false, true); //not taring but translating

    //long newTareValue=(long)lastGrams;

    romUpdateScaleGramsDif(currScale, lastGrams);

    //ser.print(currScale); ser.print(" scale new Tare :");ser.println(newTareValue);

    //now the GRAMS must shown to ZEROOOOOOOOOOOOOOOOOO

    OkActionType = 0;

    loopCounter = 0;

  }

  else {

    checkRollOnTaringAndCallibratingMenus();

  }


  if (loopCounter == 0) { //about every 10 seconds

    //ser.println(currScale+String("Reading curr scale "));

    lcd.clear();

    if (readWeight(currScale, true, true)) {

      lcd.print(lastGrams);

    }

    else {

      lcd.print("error");

    }

    if (showingReadingsAlso) {

      lcd.print('<');

      lcd.print(lastReading);

    }

    lcd.setCursor(0, 1);

    lcd.print(currScale);

    lcd.print('#');

    lcd.print(romReadScaleGramsDif(currScale));

    if (romReadDhtEnabled()) {

      lcd.print(",");

      dhtRead();//i know is stupid showing dht here but...what to do!?

      lcd.print((int)t);//WARNING printing floats consume FLASH

      lcd.print('C');

      lcd.print((int)h);//so i am casting.Dont care for 20,63 degrees and humidity .just 20

      lcd.print('%');

    }

  }


  ++loopCounter;


}





















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

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

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

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

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

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

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

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

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

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

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

// CALLIBRATION MENU



void refreshCallibratingSensors() {

  lcdEmptyStrings01();

  iTemp33 = romReadScaleStandartsUsing(currScale);

  if (iTemp33 > 0) {

    readWeight(currScale, true, true);


    lcd0 += iTemp33;

    lcd0 += '/';

    lcd0 += (iTemp32 + 1);

    lcd0 += '#';

    lcd0 += currScale;

    lcd0 += ' ';

    lcd0 += lastGrams;



    lcd1 += romReadScaleAverage(currScale, iTemp32);

    lcd1 += '=';

    lcd1 += romReadScaleStandart(currScale, iTemp32);

  }

  else {

    lcd0 += '#';

    lcd0 += currScale;

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

  }

  lcdClearToStrings01();


}

void prepare3CallibratingSensors() {

  //ser.println("CallibratingSensors");

  lcdModeEditing = 1;

  lcdRowEditing = 0;

  lcdCheckOnOff = 0;

  lcd0Inc = 1;

  //currScale = 0; //scale to be as it was on showing-taring

  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

  refreshCallibratingSensors();

}

void loop3CallibratingSensors() {

  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: ser.println("diaole");

  }

}

void LoopCallibratingSensors_Step_0() {

  if (OkActionType) {

    //ser.print(currScale); ser.println(" scale callibration starting");


    if (readCurrScaleWeightOnly()) {

      iTemp34++;


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

      byte i = 0;

      while (i < 8) {

        tempStandart[i] = 0;

        tempAverage[i] = 0;

        ++i;

      }



      lcdCharMin = '1';

      lcdCharMax = '7';

      lcd0Min = 0;

      lcd0Max = 0;

      lcdColumnEditing = 0;


      lcdEmptyStrings01();

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

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

      lcdClearToStrings01();

      lcd.blink();

      lcd.setCursor(0, 0);


    }

    else {

      //ser.println("cant start");//callibration

      lcdQuickPrint("cant start", smallDiff);

    }

    OkActionType = 0;

    loopCounter = 0;

    return;

  }

  else {

    checkRollOnTaringAndCallibratingMenus();

  }


  if (loopCounter % 100 == 0) {

    refreshCallibratingSensors();

    ++iTemp32;

    iTemp32 %= iTemp33;

  }


  ++loopCounter;



}

void resetlcd0fprcallstep() {

  lcdEmptyStrings01();

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

  lcd0 += (iTemp32 + 1);

  lcd0 += '/';

  lcd0 += iTemp33;

  lcd0 += ':';

  lcd0 += F("000000 grams");

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

  lcdColumnEditing = 10 - iTemp32;

  if (lcdColumnEditing < 4) {

    lcdColumnEditing = 4; //reming user to ...put bigger weights even if we are sorting at the end...

  }


  lcdClearToStrings01();

  lcd.setCursor(lcdColumnEditing, 0);

}


void printCallibrationAverageProcess(long & minv, long & maxv) {

  lcd.clear(); lcd.print(iter); lcd.print('/'); lcd.print(100); lcd.print(' '); lcd.print((long)h);//..dont print floats.saving flash

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

}

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(readWeight(currScale,false,false)){

      t=lastReading;

    }*/

  readCurrScaleWeightOnly(); //warming up....


  iter = 0;

  h = 0; //total sum..using h from humidity for sram

  byte errors = 0;

  byte goods = 0;

  byte samples = 100;

  long minv = 2147483646;//not really need this but ok....

  long maxv = -2147483647;

  while (iter < samples) {

    if (iter % 10 == 0) {

      printCallibrationAverageProcess(minv, maxv);

    }

    //ser.println(iter);

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


    if (readCurrScaleWeightOnly()) {

      //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;

    }

    else {

      ++errors;

    }

    ++iter;

  }


  if (goods > 0) {

    h /= goods;

  }


  //printCallibrationAverageProcess(minv, maxv);delay(2345);//may want to see min-max !!! saved 80 Flash bytes


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

  //if (errors > 0) {//ser.println("Dont care!please upgrade!Average with errors");}


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

}

void LoopCallibratingSensors_Step_1() {

  if (OkActionType) {

    //ser.println("Procced to step 2");

    iTemp34++;


    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


    tempStandart[0] = 0;

    tempAverage[0] = takeAverage(); //the



    iTemp32++;


    lcdCharMin = '0';

    lcdCharMax = '9';

    lcd0Min = 4;

    lcd0Max = 9;

    lcdColumnEditing = 9;


    resetlcd0fprcallstep();


    OkActionType = 0;


  }

  else {

    checkToRoll();

  }

}



void LoopCallibratingSensors_Step_2() {

  if (OkActionType) {

    long gramsKnowned = parselong(lcd0, 4, 6);



    //#ifdef DEBUG

    //ser.println(lcd0);

    //ser.println(gramsKnowned);

    //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 < iTemp32) {

      if (gramsKnowned == tempStandart[i]) {

        break;

      }++i;

    }

    if (i < iTemp32) {

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

      lcdQuickPrint("WeightAlready In", smallDiff);

      resetlcd0fprcallstep();

    }

    else {

      //taking known weights average here

      tempStandart[iTemp32] = gramsKnowned;

      tempAverage[iTemp32] = takeAverage();



      iTemp32++;//increasing to next known weight

      if (iTemp32 < iTemp33) {

        resetlcd0fprcallstep();

      }

      else {

        iTemp34++; //skipping step if finish taking averages of all known weights...and going to finilize this callibration

      }

    }


    OkActionType = 0;

  }

  else {

    checkToRoll();

  }



}



void LoopCallibratingSensors_Step_3() {

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


  int i = 0; //iTemp33 standarts using.Also because unsigned long some; NOT going to zero...have this as =0 ion case.....(learning to initilise always on before loop)


  /*#ifdef DEBUG

    while (i < iTemp33) {

      ser.print(i);

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

      ser.print(tempStandart[i]);

      ser.print(F(" <- "));

      ser.println(tempAverage[i]);

      ++i;

    }

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

    #endif

  */

  sortInsertionTempStandartsAndAveragesByStandarts(iTemp33);

  /*

    #ifdef DEBUG

    i = 0;

    while (i < 8) {//show all eight-8 values after sort

      ser.print(i);

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

      ser.print(tempStandart[i]);

      ser.print(F(" <- "));

      ser.println(tempAverage[i]);

      ++i;

    }

    #endif

  */

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

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

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

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

  //lcdQuickPrintAndRefresh(F("updating EEPROM"),smallDiff);

  //ser.println("updating");

  romUpdateScaleStandartsUsing(currScale, iTemp33);

  i = 0;// QUESTION SOS +50 Flash if comment this ?????amazing!!!(jn case i have un-commented the sorting print out above)

  while (i < 8) {

    romUpdateScaleStandart(currScale, i, tempStandart[i]);

    romUpdateScaleAverage(currScale, i, tempAverage[i]);

    ++i;

  }

  /*if (!romReadScaleEnabled(currScale)) {

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

    romUpdateScaleEnabled(currScale, true);

    //lcdQuickPrintAndRefresh(F("ScaleTurned On"),smallDiff);

    //ser.println("scale turned on");

    }*/

  romUpdateScaleEnabled(currScale, true);//above check replaced with this single line foir saving 22 flash

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

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

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

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




  currMenu -= 2; //showing weights and get ready to tare if want

  done();



}




// END END E N D CALLIBRATION MENU

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

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

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

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

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

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

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

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

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

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

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








void prepare4OnOffSensorsAndDht() {

  //ser.println("OnOffSensorsAndDht");

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

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

  byte b = 0, bb = 1;

  String * str = &lcd0;

  while (b < 5) { //-74 flash with this while

    if (romReadScaleEnabled(b)) {

      str->setCharAt(bb, 'n');

    }

    ++b;

    bb += 5;

    if (b == 3) {

      str = &lcd1;

      bb = 1;

    }

  }

  if (romReadDhtEnabled()) {

    lcd1.setCharAt(11, 'n');

  }

  lcdModeEditing = 2;

  lcdRowEditing = 0;

  lcdColumnEditing = 1;

  //lcdCharMin='0';

  //lcdCharMax='9';

  lcdCheckOnOff = 1;

  lcd0Inc = 5;

  lcd1Inc = 5;

  lcd0Min = 1;

  lcd1Min = 1;

  lcd0Max = 11;

  lcd1Max = 11;


  lcd.blink();

}

void loop4OnOffSensorsAndDht() {

  if (OkActionType) {

    byte b = 0, bb = 1;

    String * str = &lcd0;

    while (b < 5) { //-64 flash with this while

      romUpdateScaleEnabled(b, (str->charAt(bb) == 'n'));

      ++b;

      bb += 5;

      if (b == 3) {

        str = &lcd1;

        bb = 1;

      }

    }

    romUpdateDhtEnabled((lcd1.charAt(11) == 'n'));

    done();

  }

  else {

    checkToRoll();

  }


}


void prepare5SetDhtTypeAndPin() {

  //ser.println("SetDhtTypeDaysOffPin");

  //lcd0+="CallOn|11DhtType";

  //lcd1+="NeedPIN On|1111";


  lcd0 += F("CallOf");//this is not used for the moment....

  if (romReadCallInsteadSms()) {

    lcd0.setCharAt(5, 'n');

  }

  lcd0 += '|';


  byte dhtType = romReadDhtType();

  if (dhtType < 10) {

    lcd0 += '0';

  }

  lcd0 += dhtType;

  lcd0 += F("DhtType");



  lcd1 += F("NeedPIN Of|");

  if (romReadPinEnabled()) {

    lcd1.setCharAt(9, 'n');

  }

  romReadSimPin(lcd1);




  lcdModeEditing = 2;

  lcdRowEditing = 0;

  lcdColumnEditing = 5;

  lcdCharMin = '0'; //for dht we must do check and accept only '1' and '2'

  lcdCharMax = '9';

  lcdCheckOnOff = 1;

  lcd0Inc = 1;

  lcd1Inc = 1;

  lcd0Min = 5;

  lcd1Min = 9;

  lcd0Max = 8;

  lcd1Max = 14;


  lcd.blink();

}

void loop5SetDhtTypeAndPin() {

  if (OkActionType) {


    romUpdateCallInsteadSms(lcd0.charAt(5) == 'n');

    byte dhtType = parselong(lcd0, 7, 2);

    if (dhtType == 11 || dhtType == 12 || dhtType == 21 || dhtType == 22 ) {

      romUpdateDhtType(dhtType);

      //WARNING....must have change to public from library...in order to compile

      dht._type = dhtType; //no need begin() again ?...no need!

    }

    romUpdatePinEnabled(lcd1.charAt(9) == 'n');

    romUpdateSimPin(lcd1, 11);


    done();

  }

  else {

    checkToRoll();

  }

}



void prepare6SetSimSeconds() {

  //ser.println("SetSimSecondsNeedPin");

  byte secOn = romReadSimSecWaitAfterPowerOn();//WARNING new version dont use this .....

  if (secOn < 10) {

    lcd0 += '0';

  }

  lcd0 += secOn;

  lcd0 += F(" SecWait@SimOn");


  byte secOff = romReadSimSecWaitBeforePowerOff();

  if (secOff < 10) {

    lcd1 += '0';

  }

  lcd1 += secOff;

  lcd1 += F(" SecWait@SimOf");



  lcdModeEditing = 2;

  lcdRowEditing = 0;

  lcdColumnEditing = 0;

  lcdCharMin = '0';

  lcdCharMax = '9';

  lcdCheckOnOff = 0;

  lcd0Inc = 1;

  lcd1Inc = 1;

  lcd0Min = 0;

  lcd1Min = 0;

  lcd0Max = 1;

  lcd1Max = 1;

  lcd.blink();


}

void loop6SetSimSeconds() {

  if (OkActionType) {

    //byte secOn = parselong0p(lcd0, 0, 2);//-4 Flash bytes direct on arguments

    //byte secOff = parselong0p(lcd1, 0, 2);//-4 Flash bytes

    romUpdateSimSecWaitAfterPowerOn(parselong(lcd0, 0, 2));

    romUpdateSimSecWaitBeforePowerOff(parselong(lcd1, 0, 2));

    done();

  }

  else {

    checkToRoll();

  }

}




void prepare7SetSmsPhonesAndEnabled() {

  //ser.println("SetPhonesAndEnabled");


  lcd0 += F("aOf|");//a mean primary phone number

  romReadSmsPrimaryNumber(lcd0);

  lcd1 += F("bOf|");//b mean secondary phone number

  romReadSmsSecondaryNumber(lcd1);

  byte smsEnabledFlag = romReadSmsEnabled();

  if (smsEnabledFlag % 2 == 1) {

    lcd0.setCharAt(2, 'n');

  }

  if (smsEnabledFlag > 1) {

    lcd1.setCharAt(2, 'n');

  }


  lcdModeEditing = 2;

  lcdRowEditing = 0;

  lcdColumnEditing = 2;

  lcdCharMin = '0';

  lcdCharMax = '9';

  lcdCheckOnOff = 1;

  lcd0Inc = 1;

  lcd1Inc = 1;

  lcd0Min = 2;

  lcd1Min = 2;

  lcd0Max = 15;

  lcd1Max = 15;

  lcd.blink();

}

void loop7SetSmsPhonesAndEnabled() {

  if (OkActionType) {


    byte smsEnabledFlag = lcd1.charAt(2) == 'n';

    smsEnabledFlag <<= 1;

    smsEnabledFlag += lcd0.charAt(2) == 'n';


    romUpdateSmsEnabled(smsEnabledFlag);

    romUpdateSmsPrimaryNumber(lcd0, 4);

    romUpdateSmsSecondaryNumber(lcd1, 4);


    if (smsEnabledFlag == 0) {

      if (!romReadSmsEnabled()) { //ser.println("php turned on");

        romUpdatePhpEnabled(1);

      }

    }


    done();

  }

  else {

    checkToRoll();

  }

}


void prepare8SetPhpEnabled() {

  //ser.println("SignalingAndBattery");

  //digitalWrite(PinRelaySim,HIGH);

  lcd0 += F("PhpOf");

  if (romReadPhpEnabled()) {

    lcd0.setCharAt(4, 'n');

  }

  lcd1 += F("GreenOf");//new future green or red sim800L modules 

  if (romReadUseGreenSimAndMustChangePins()){

    lcd1.setCharAt(6, 'n');//On means we use red module (that has helical antenna)

  }

  lcdModeEditing = 2;

  lcdRowEditing = 0;

  lcdColumnEditing = 4;

  //lcdCharMin = '0';

  //lcdCharMax = '9';

  lcdCheckOnOff = 1;

  lcd0Inc = 1;

  lcd1Inc = 1;

  lcd0Min = 4;

  lcd1Min = 6;

  lcd0Max = 4;

  lcd1Max = 6;

  lcd.blink();


}

void loop8SetPhpEnabled() {

  if (OkActionType) {

    //ser.println(lcd0.charAt(4) == 'n');

    romUpdatePhpEnabled(lcd0.charAt(4) == 'n');

    romUpdateUseGreenSimAndMustChangePins(lcd1.charAt(6) == 'n');

    if (!romReadPhpEnabled()) { //ser.println("php setted off");

      if (romReadSmsEnabled() == 0) { //ser.println("1st sms turned on");

        romUpdateSmsEnabled(1);//must do something on works !!!aytomatically changing!

        currMenu -= 2;

      }

    }


    done();

  }

  else {

    checkToRoll();

  }

}









//this function will be used editing long strings (apikey,apm,server,query)

//str30Chars is trimmed(with ' ' spaces from romRead....

void prepareStr30(char ch0, char ch1, const String & str30Chars) {

  lcd0 += ch0;

  lcd0 += ch1;

  lcd0 += "              ";//14 spaces

  lcd1 += "                ";//+16 spaces =30 max length for ....key,apn,server,querry strings

  byte len = str30Chars.length();

  byte index = 0;

  byte maxl = 14;

  byte at = 2;

  if (len < maxl) {

    maxl = len;

  }

  while (index < maxl) {//copying first 14 to lcd0 String

    lcd0.setCharAt(at, str30Chars.charAt(index));

    //ser.print(at+String(":"));ser.println((int)lcd0.charAt(at));

    ++index;

    ++at;

  }

  at = 0;

  while (index < len) {//copying 16 more to lcd1 String

    lcd1.setCharAt(at, str30Chars.charAt(index));

    //ser.print(at+String(":"));ser.println((int)lcd1.charAt(at));

    ++index;

    ++at;

  }

  //ser.println(lcd0.length()+lcd0);

  //ser.println(lcd1.length()+lcd1);

  lcdModeEditing = 2;

  lcdRowEditing = 0;

  lcdColumnEditing = 2;

  lcdCharMin = ' ';

  lcdCharMax = 'z';//[A,Z]U[Z,'_') is hacked ..only small letters can write .No need those 4 chars [\]^ after Z

  lcdCheckOnOff = 0;

  lcd0Inc = 1;

  lcd1Inc = 1;

  lcd0Min = 2;

  lcd1Min = 0;

  lcd0Max = 15;

  lcd1Max = 15;

  lcd.blink();

}

void fillChars14Fromlcd0And16Fromlcd1(const String & str30) {

  byte at = 2;

  while (at < 16) {

    str30 += lcd0.charAt(at);

    ++at;

  }

  at = 0;

  while (at < 16) {

    str30 += lcd1.charAt(at);

    ++at;

  }


}

void prepare9SetThingsKey() {

  //ser.println("SetPhpEnabledThingsKey");

  String ThingsKey;

  romReadThingSpeakKey(ThingsKey);

  prepareStr30('K', ':', ThingsKey);//prompt 'K' letter stands for KEY


}

void loop9SetThingsKey() {

  if (OkActionType) {

    String ThingsKey;

    fillChars14Fromlcd0And16Fromlcd1(ThingsKey);

    romUpdateThingSpeakKey(ThingsKey, 0);//can be empty if you build your own php server?!!?

    done();

  }

  else {

    checkToRoll();

  }

}


void prepare10SetApn() {//12916-13202=286 Flash

  //ser.println("SetApn");

  String apn;

  romReadApn(apn);

  prepareStr30('A', ':', apn);//prompt 'A' letter stands for APN


}

void loop10SetApn() {

  if (OkActionType) {

    String apn;

    fillChars14Fromlcd0And16Fromlcd1(apn);

    romUpdateApn(apn, 0);

    //currMenu -= 2;//debugging

    done();

  }

  else {

    checkToRoll();

  }

}



//WARNING...use http and not https even if the real server is ssl (https).

//SIM800L not working for https (or i dont know)(neither AT+EMAILSSL=1 command working for me)

void prepare11SetPhpServer() {

  //ser.println("SetPhpServer");

  String phpserver;

  romReadPhpServer(phpserver);

  prepareStr30('S', ':', phpserver);//prompt 'S' letter stands for SERVER

}

void loop11SetPhpServer() {

  if (OkActionType) {

    String phpserver;

    fillChars14Fromlcd0And16Fromlcd1(phpserver);

    romUpdatePhpServer(phpserver, 0);

    //currMenu -= 2;

    done();

  }

  else {

    checkToRoll();

  }

}


void prepare12SetPhpQuery() {

  //ser.println("SetPhpQuery");

  String PhpQuery;

  romReadPhpQuery(PhpQuery);

  prepareStr30('Q', ':', PhpQuery);//prompt 'Q' letter stands for QUERRY

}

void loop12SetPhpQuery() {

  if (OkActionType) {

    String PhpQuery;

    fillChars14Fromlcd0And16Fromlcd1(PhpQuery);

    romUpdatePhpQuery(PhpQuery, 0);

    //currMenu -= 2;

    done();

  }

  else {

    checkToRoll();

  }

}
















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

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

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

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

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

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

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

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

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

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

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




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

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

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

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

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

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

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

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

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

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

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

// TEST SIM MENU - SIGNALING - CALL , SMS , PHP (ThingSpeak)




String simAnswer;//warning ....

bool foundOK, foundeRRor;

int lastCharIndex;

int lastNumberRead;

void readSimAnswer(unsigned long maxTime, char charSearchingLast, bool breakOuterOnCharfound = false) {

  foundeRRor = false;

  foundOK = false;

  lastCharIndex = 0;

  lastNumberRead = 0;

  simAnswer.remove(0, simAnswer.length());//warning = better to have control on each call so to be empty AFTER use.But leave this...just for flash

  char chCurr, chPrev;

  unsigned long timeStartRead = millis();

  unsigned long timeEndRead = timeStartRead + maxTime;

  byte protectRamMax = 30;

  byte counts = 0;

  while (millis() < timeEndRead) {

    while (sim->available()) {

      chCurr = (char)sim->read();

#ifdef DEBUG_SIM_ANSWER

      ser.print(chCurr);

#endif

      if (chCurr == 'K' && chPrev == 'O') {

        foundOK = true;

        timeEndRead = 0; //to return immidiatelly reading only what is NOW available

      }

      else if (chCurr == 'R' && chPrev == 'R') {

        foundeRRor = true;

        timeEndRead = 0;

      }

      chPrev = chCurr;

      ++counts;

      if (counts > protectRamMax) { ///do nothing...just keep to find OK,Error.No further surch for requested char

      }

      else {

        if (chCurr == charSearchingLast) {

          lastCharIndex = simAnswer.length();

          if (breakOuterOnCharfound) {

            //ser.println("will break");

            //delay(10);

            timeEndRead = 0;

          }

        }

        simAnswer += chCurr;

        if (counts > 10) { //this is because httpAction answer like +HTTPACTION: 0,200,2 sometimes comes ? as V⸮QAJr⸮$bj⸮

          //but i am calling this function from readSimAnswer(1000 * romReadSimSecWaitBeforePowerOff(), ',',true);

          timeEndRead = 0;//so to not wait the ...13 seconds ...loosing time....but have comment here for someone wants to play with sim and then erase this if

          //example the answer of battery +CBC: 0,58,3867 may not work if iot lates to give next chars any from this ceil=7

          //or in signal...so MIGHT ...in rare case be wrong and must be erased

        }

      }

    }

    delay(100);

  }


}


void readSimToSerial(unsigned long maxTime) {

  foundOK = false;

  foundeRRor = false;

  unsigned long timeStartRead = millis();

  unsigned long timeEndRead = timeStartRead + maxTime;

  char chCurr, chPrev;

  while (millis() < timeEndRead) {

    while (sim->available()) {

      chCurr = (char)sim->read();

#ifdef DEBUG_SIM_ANSWER

      ser.print(chCurr);

#endif

      if (chCurr == 'K' && chPrev == 'O') {

        foundOK = true;

        timeEndRead = 0; //to return immidiatelly reading only what is NOW available

      }

      else if (chCurr == 'R' && chPrev == 'R') {

        foundeRRor = true;

        timeEndRead = 0;

      }

      chPrev = chCurr;

    }

    delay(100);

  }


}

/*

  void readSimToSerialDontCheckDontBreak(unsigned long maxTime) {

  unsigned long timeStartRead = millis();

  unsigned long timeEndRead = timeStartRead + maxTime;

  char chCurr;

  while (millis() < timeEndRead) {

    while (sim.available()) {

      chCurr = (char)sim.read();

  #ifdef DEBUG_SIM_ANSWER

      ser.print(chCurr);

  #endif

    }

    delay(100);

  }


  }*/

byte printSpace;//trick to be visible when refresh signal

void lcdPrintLastSignalRead() {

  lcd.setCursor(0, 0);

  if (printSpace % 2 == 0) {

    lcd.print(' ');//to be visible when refresh

  }

  ++printSpace;

  lcd.print(lastNumberRead);

  lcd.print(F(" Signal "));

}


/*

 * 2-9 Marginal

 * 10-14 OK

 * 15-19 Good

 * 20-30 Excellent

 * 99 Not known or not detectable

 */

void execAndReadATSignal() {

  sim->println(F("AT+CSQ"));//Max ResponseTime -?

  delay(100);

  readSimAnswer(1000, ':');

  //ser.println(simAnswer);

  if (foundOK && lastCharIndex > 0) {

    lastNumberRead = parselong(simAnswer, lastCharIndex + 2, simAnswer.length());

  }

}

int lastSimBatteryRead;

void execAndReadATBattery() {

  sim->println(F("AT+CBC"));//Max ResponseTime -?

  delay(100);

  readSimAnswer(1000, ',');

  lastSimBatteryRead = -1;

  if (foundOK && lastCharIndex > 0) {

    lastNumberRead = parselong(simAnswer, lastCharIndex + 1, simAnswer.length());

    lastSimBatteryRead = lastNumberRead;

  }

}

bool execAndReadATNetworkRegistration() {

  lastNumberRead = 2; //unregistered

  sim->println(F("AT+CREG?"));//Max ResponseTime -?

  delay(100);

  readSimAnswer(1000, ',');

  if (foundOK && lastCharIndex > 0) {

    lastNumberRead = parselong(simAnswer, lastCharIndex + 1, simAnswer.length());

  }

#ifdef DEBUG

  //ser.println(simAnswer);

  //ser.println(lastNumberRead);

#endif

  return lastNumberRead == 1;//now registered


}



/*

  Note: A HEX string such as "00 49 49 49 49 FF FF FF FF" will be sent out through serial

  port at the baud rate of 115200 immediately after SIM800 Series is powered on. The string

  Smart Machine Smart Decision

  SIM800 Series_AT Command Manual_V1.09 23 2015-08-03

  shall be ignored since it is used for synchronization with PC tool. Only enter AT Command

  through serial port after SIM800 Series is powered on and Unsolicited Result Code "RDY"

  is received from serial port. If auto-bauding is enabled, the Unsolicited Result Codes

  "RDY" and so on are not indicated when you start up the ME, and the "AT" prefix, or

  "at" prefix must be set at the beginning of each command line.

*/

bool openSimAndHasNetwork() {


  unsigned long maxEndTime = romReadSimSecWaitAfterPowerOn();

  //ser.println(maxEndTime);

  if (maxEndTime < 11) {//minimum seconds time for network registration including below delay

    maxEndTime = 11;

  }

  maxEndTime *= 1000;

  maxEndTime += millis();

  delay(6000);//minimum time for BASIC power up.Dont make questions to sim with no reason


  /*foundOK = false;

    while (!foundOK) {

    sim.println("AT");

    readSimToSerial(500);//WAITING for synchronisation and RDY

    if (millis() > maxEndTime) {

      break;

    }

    }

    if (foundOK) {*/

  foundOK = false;

  while (!foundOK) {

    if (execAndReadATNetworkRegistration()) {

#ifdef DEBUGFULL

      ser.println(F("sim ready"));

#endif

      return true;

    }

    foundOK = false;

    if (millis() > maxEndTime) {

      break;

    }

    delay(3000);//so  1st question 6 sec,2nd 9 sec,3rd 12 sec...etc

    //ser.println("One more try to find network");

  }

  //}

  return false;

}


void unlockSimWithPinIfNeed() {

  if (romReadPinEnabled()) {

    String pin;

    romReadSimPin(pin);

    sim->print(F("AT+CPIN="));//Max ResponseTime 5s

    sim->println(pin);

    delay(100);

    readSimToSerial(5000);

    /*not enough memory

      if(foundOk){ser.println("Pin OK");}

      else{ser.println("Pin NOT good");} //WARNING new programmers (sep 2021 reviewing code) 

    */

  }


  //readSimToSerialDontCheckDontBreak(4000); //delaying 4 sec just for in case sim sending data like Pin ready,sms ready,call ready

}

bool openSimHasNetAndPinOk() {

  if (openSimAndHasNetwork()) {

    unlockSimWithPinIfNeed();

  }

  return foundOK;

}


void lcdPrintFailed() {

  lcd.print(F(" Failed"));

}

bool nowSignaling;


void prepare13Test() {

  loopCounter = 0;

  nowSignaling = false;

  lcd0 += F("OkToTestPhpSms");

  lcd1 += F("RollForSignaling");

  lcdModeEditing = 3;//none


}

void loop13Test() {

  if (OkActionType) {

    //ser.println("TestPinBattCallSmsPhp");

    lcd.clear();

    lcd.print(F("working SIM800L"));

    work();

    currMenu -= 1;

    done();

  }

  else {

    if (checkToRoll() > 1) {

      //ser.println("Signaling on/off");

      if (nowSignaling) {

        currMenu -= 1;

        done();

        nowSignaling = false;

      }

      else {

        lcd.clear();

        nowSignaling = true;

        loopCounter = 0;

      }

    }

  }


  if (nowSignaling && (loopCounter % 86 == 0)) { //every ~5 sec

    //ser.println("signaling");

    execAndReadATSignal();

    lcdPrintLastSignalRead();

  }

  ++loopCounter;


}



//Volt<3450|1Times

void prepare14SetCutOff() {//24450,1020

  lcd0 += F("Volt<3450|1Times");//3.45 volt to 3 AA batteries ...meaning Power off to save rechargable batteries !!!


  int val = romReadBelowBatteryThresholdAcceptableTimes();

  int index = 10;

  lcd0.setCharAt(index, (val % 10) + 48);

  val = romReadBatteryThreshold();

  index = 8;

  while (index > 4) {

    lcd0.setCharAt(index, (val % 10) + 48);

    val /= 10;

    --index;

  }


  /*

    val = romReadMaxSimulatedDays();

    lcd1 += F("9999 MaxDays OFF");

    index = 3;

    while (index >= 0) {

    lcd1.setCharAt(index, (val % 10) + 48);

    val /= 10;

    --index;

    }*/


  lcdModeEditing = 1;

  lcdRowEditing = 0;

  lcdColumnEditing = 5;

  lcdCharMin = '0';

  lcdCharMax = '9';

  lcdCheckOnOff = 0;

  lcd0Inc = 1;

  //lcd1Inc = 1;

  lcd0Min = 5;

  //lcd1Min = 0;

  lcd0Max = 10;

  //lcd1Max = 3;


  lcd.blink();

  loopCounter = 0;


}

void loop14SetCutOff() {

  if (OkActionType) {

    romUpdateBatteryThreshold(parselong(lcd0, 5, 4));

    romUpdateBelowBatteryThresholdAcceptableTimes(parselong(lcd0, 10, 1));

    //romUpdateMaxSimulatedDays(parselong(lcd1, 0, 4));

    --currMenu;

    done();

  }

  else {

    checkToRoll();

  }


  if (loopCounter == 0 || loopCounter == 127) {

    execAndReadATBattery();

    readPowerAndTranslateAccordingSimsPower();

    lcd.setCursor(0, 1);

    lcd.print(lastSimBatteryRead);

    lcd.print(' ');

    lcd.print(powerAnalogReading);

    lcd.print(' ');

    lcd.print(powerReadingTranslated0);

    lcd.setCursor(lcdColumnEditing, 0);


  }

  ++loopCounter;

}











void prepare15SetEstimated() {

  lcd0 += F("-Of|000 secAdj");


  int val = romReadEstimatedMillisOf8Seconds();

  lcd1 += F("8453 Millis8Sec");

  int index = 3;

  while (index >= 0) {

    lcd1.setCharAt(index, (val % 10) + 48);

    val /= 10;

    --index;

  }


  lcdModeEditing = 2;

  lcdRowEditing = 0;

  lcdColumnEditing = 2;

  lcdCharMin = '0';

  lcdCharMax = '9';

  lcdCheckOnOff = 1;

  lcd0Inc = 1;

  lcd1Inc = 1;

  lcd0Min = 2;

  lcd1Min = 0;

  lcd0Max = 6;

  lcd1Max = 3;


  lcd.blink();

}

void loop15SetEstimated() {

  if (OkActionType) {

    int val = romReadEstimatedMillisOf8Seconds();

    unsigned long est8 = parselong(lcd1, 0, 4);

    if (val != est8) {//only one change at a time ...this menu !

      if (est8 > 8000 && est8 < 10000) {

        romUpdateEstimatedMillisOf8Seconds(est8);

      }

    }

    else { //checking to adjust estimated 8 seconds by curr diff!!!!

      unsigned long difSeconds = parselong(lcd0, 4, 3);

      if (difSeconds > 0) {


        unsigned long hs = romReadHoursPerReading();

        byte ms = romReadMinutesPerReading();

        est8 = romReadEstimatedMillisOf8Seconds();

        if ( (hs > 0 || ms > 0) && est8 > 8000) {


          bool minus = lcd0.charAt(2) == 'n';


          /*#ifdef DEBUG

                    ser.print(F("Adjusting 8 sec estim ")); ser.print(est8);

                    ser.print(F(" hs ")); ser.print(hs);

                    ser.print(F(" ms ")); ser.println(ms);

                    ser.print(F("Seconds diff ")); ser.print(difSeconds);

                    if (minus) {

                      ser.println(F(" Sooner"));

                    }

                    else {

                      ser.println(F(" Later"));

                    }

            #endif*/


          hs *= 60; //minutes

          hs += ms; //total minutes per reading

          hs *= 60; //total seconds per reading

          hs *= 1000; //total millis per reading

          long powerdowns = hs / est8;

          difSeconds *= 1000;

          unsigned long difSharedToPowerDownTimes = difSeconds / powerdowns;

          if (minus) {

            est8 -= difSharedToPowerDownTimes;

          }

          else {

            est8 += difSharedToPowerDownTimes;

          }



#ifdef DEBUG

          ser.print(F("Millis/Reading ")); ser.println(hs);

          ser.print(F("Downs ")); ser.println(powerdowns);

          ser.print(F("Sharing millis on each power down")); ser.println(difSharedToPowerDownTimes);

          ser.print(F("New estimated 8 seconds ")); ser.println(est8);

#endif



          if (est8 > 8000 && est8 < 10000) {

            romUpdateEstimatedMillisOf8Seconds(est8);

          }


        }

      }//else no change

    }

    --currMenu;

    done();

  }

  else {

    checkToRoll();

  }

}



void switchingPrepare() {

  lcdModeEditing = 3; //none

  lcdEmptyStrings01();

  lcd.noBlink();

  lcd.clear();


  switch (currMenu) {

    case 0: prepare0PasswordPinRequesting(); break;

    case 1: prepare1SetHoursDelayAndSimulation(); break;


    case 2: prepare2ShowingTaringSensors(); break;

    case 3: prepare3CallibratingSensors(); break;


    case 4: prepare4OnOffSensorsAndDht(); break;

    case 5: prepare5SetDhtTypeAndPin(); break;

    ///SIM

    case 6: prepare6SetSimSeconds(); break;

    case 7: prepare7SetSmsPhonesAndEnabled(); break;


    case 8: prepare8SetPhpEnabled(); break;

    case 9: prepare9SetThingsKey(); break;

    case 10: prepare10SetApn(); break;

    case 11: prepare11SetPhpServer(); break;

    case 12: prepare12SetPhpQuery(); break;


    case 13: prepare13Test(); break;

    case 14: prepare14SetCutOff(); break;

    case 15: prepare15SetEstimated(); break;



  }

  lcdClearToStrings01();

  lcd.setCursor(lcdColumnEditing, lcdRowEditing);


}

void switchingLoop() {

  switch (currMenu) {

    case 0: loop0PasswordPinRequesting(); break;

    case 1: loop1SetHoursDelayAndSimulation(); break;

    case 2: loop2ShowingTaringSensors(); break;

    case 3: loop3CallibratingSensors(); break;

    case 4: loop4OnOffSensorsAndDht(); break;

    case 5: loop5SetDhtTypeAndPin(); break;

    case 6: loop6SetSimSeconds(); break;

    case 7: loop7SetSmsPhonesAndEnabled(); break;


    case 8: loop8SetPhpEnabled(); break;

    case 9: loop9SetThingsKey(); break;

    case 10: loop10SetApn(); break;

    case 11: loop11SetPhpServer(); break;

    case 12: loop12SetPhpQuery(); break;


    case 13: loop13Test(); break;

    case 14: loop14SetCutOff(); break;

    case 15: loop15SetEstimated(); break;

  }

}




void loopLcd() {

  readButtons();


  if (MenuOkPressed != MenuOkPrevPressed) {

    //ser.println(MenuOkPressed);

    if (MenuOkPressed) {

      lastMenuOkPressed = millis();

    }

    else {

      lastMenuOkReleased = millis();

      if (lastMenuOkReleased - lastMenuOkPressed < smallDiff) {

        if (RollEditPressed) {

          prevMenu();

        }

        else {

          nextMenu();

        }

      }

      else {

        //ser.println("OK");

        OkActionType = 1;

      }

    }

  }


  switchingLoop();

  storeButtons();

  delay(40);

}



void setup() {


#ifdef DEBUG

  ser.begin(9600);

#endif


  pinMode(PinButMenuOk,INPUT_PULLUP);

  ModeLcd = digitalRead(PinButMenuOk);

  //ModeLcd=1;//for debug

  //blinc(2);//can leave the button now....in case of SMS mode...I understood that we want SMS mode..just for debug


  String jimakos("jimakoskx@");

  if(romReadUseGreenSimAndMustChangePins()){

    byte temp=PinSimRx;

    PinSimRx=PinSimTx;

    PinSimTx=temp;

    jimakos+="green";

  }

  else{

    jimakos+="red";

  }

  sim=new SoftwareSerial(PinSimTx,PinSimRx);

  setPinModes();


  //WHY?????????????????????????????

  //sim.begin(9600);//???if i have it here does not begining....


  if (ModeLcd) {


    romCheckToFlush();


  }

  else { //Sms mode


  }



#ifdef DEBUGFULL

  showeeprom();//1000+ flash bytes this!

#endif



  dht._type = romReadDhtType(); //was initiated as type=11

  if (ModeLcd || romReadDhtEnabled()) { //saving some power? on sms not dht enabled?....

    dht.begin();//library setting pinMode here!...this will also be called when we changing TYPE.No need to  be called every time that we are opening the Sensor Relays!

  }

  

  sim->begin(9600);//???here is ok...



  clearStoredWeights();//to DISABLED_CHAR instead of zero. This will also be used on lcd mode testing.


  //lcd.begin();//for debug


  if (ModeLcd) {

#ifdef DEBUG

    ser.println(F("\nMODE LCD\n"));

#endif

    lcd.begin();

    lcd.print(jimakos);

    relaySensorsTrigger();

    openSimHasNetAndPinOk();

    nextMenu();


  }

  else {

#ifdef DEBUG

    ser.println(F("\nMODE SMS\n"));

#endif


#ifdef LATCH_SENSORS_RELAY //if normal relay then for sure we are not triggered after reset

    relaySensorsUnTrigger();//this is to be sure we are not triggered when coming here after lcd mode

    delay(100);

#endif


    setupSms();

  }



}


void loop() {

  if (ModeLcd) {

    loopLcd();

  }

  else {

    loopSms();

  }


}


unsigned long mustDelayOrSleep;//this is only for debug

unsigned long timesPowerDown8Seconds;//this is only for debug

unsigned long millisRemainForNormalDelay;//this is only for debug


int currDay;  // int..32.768 days MY batteries!!!? or even only byte 255 days to keep batteries?

unsigned long estimatedMillisOf8SecondsPowerDown = 8500; //you must adjust this

#define USE_LOW_POWER

void myDelay(unsigned long someTime) {//finally this function release from RTC need...good enough



#ifdef USE_LOW_POWER

  timesPowerDown8Seconds = someTime / estimatedMillisOf8SecondsPowerDown;

  millisRemainForNormalDelay = someTime % estimatedMillisOf8SecondsPowerDown;

  unsigned long times = timesPowerDown8Seconds; //hold this to be able to show it after for debug

  //WARNING .This delay must be before while because???

  //is not working fine if after

  delay(millisRemainForNormalDelay);

  while (times > 0) {

    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);

    --times;

  }

  //delay(millisRemainForNormalDelay);//not working here

#endif








#ifndef USE_LOW_POWER

  delay(someTime);

#endif


}




void checkToSendInitialSmsInCaseOfMustDelaySomeHoursBeforeStart() {

  mustDelayOrSleep = romReadHourDelayBeforeSMSwhile();

  if (mustDelayOrSleep > 0) {

    mustDelayOrSleep *= 3600000ul;

    work();//this is the initial sms/call for user to know that is ok...sms mode

    iter = 0;

    currDay = 0;

    myDelay(mustDelayOrSleep);//does not deduct the time taken(half-2 minutes) to do the initial actions above....

  }

}



void setupSms() {



#ifndef USE_DEFINED_MILLIS_PER_READING

  int minutes = romReadHoursPerReading();

  minutes *= 60;

  minutes += romReadMinutesPerReading();

  if (minutes < 2) { //..just checking.....

    minutes = 360;//6 hours default?

  }


  millisPerReading = minutes;

  millisPerReading *= 60; //seconds

  millisPerReading *= 1000;

#endif //else just use what i have in the beginning of the file (for debugging)






  unsigned long estimcheck = romReadEstimatedMillisOf8Seconds();

  if (estimcheck > 8000 && estimcheck < 10000) { //just a checking !!!...doubled?

    estimatedMillisOf8SecondsPowerDown = estimcheck;

    //ser.println(estimatedMillisOf8SecondsPowerDown);

  }//else{}//not acceptable ..just using default






  //#ifdef DEBUGFULL

  //timesPowerDown8Seconds = millisPerReading / estimatedMillisOf8SecondsPowerDown;


  //ser.print("hours "); ser.println(romReadHoursPerReading());

  //ser.print("minutes "); ser.println(romReadMinutesPerReading());

  //ser.print("reads per millis "); ser.println(millisPerReading);

  //ser.print("/estim "); ser.print(estimatedMillisOf8SecondsPowerDown);

  //ser.print(" = downtimes "); ser.println(timesPowerDown8Seconds );

  //ser.print("sms every sec"); ser.println( (4 * millisPerReading) / 1000);

  //#endif


  checkToSendInitialSmsInCaseOfMustDelaySomeHoursBeforeStart();


}

void loopSms() {

  workingStep();

}



void workingStep() {

  //1.hold time

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


  //2.work

  work();


  //some commands before call ending millis() for more accuracy

#ifdef DEBUG

  //ser.flush();

#endif



  //usually php and sms taking about 2 minutes so hourMillis(declared) for debug must be at least 20.000 that * 6 = 2 minutes debug each reading

  //last call on millis()....quick end this function.....

  //3.compute how much we must sleep-delay

  //mustDelayOrSleep = millisPerReading ;

  //mustDelayOrSleep += startMillis;

  //mustDelayOrSleep -= millis();///this may be negative =very large if workingTime take more than millisPerReading

  //correction of 3.step above to not sleep negative(for ever)


  unsigned long timeTooked=millis();

  timeTooked-=startMillis;//time tooked to work

  if(timeTooked<millisPerReading){

    mustDelayOrSleep=millisPerReading;

    mustDelayOrSleep-=timeTooked;

    myDelay(mustDelayOrSleep);

  }

  //else{dont sleep ....danger to loop and eat battery..thats why i had the old.3...with o check}

  

  

  


}//end simulateReadingWithPowerConsumingDelayAndNoRTC



//Critical out because if low we must turn off application from latch relay(main power)

//because sometimes gives me very low....reminds me the issue with the atmega328 buildin(internal) power measuring function!!

//analogReference()????????

void readPowerAndTranslateAccordingSimsPower() {

  int times = 5;

  int val = analogRead(PinPowerRead);

  int maxVal = analogRead(PinPowerRead); //discart first reading

  while (times > 0) {

    val = analogRead(PinPowerRead);

    if (val > maxVal) {

      maxVal = val;

    }

    delay(10);

    --times;

  }

  powerAnalogReading = maxVal;

  //#ifdef DEBUGFULL

  //ser.print(F("Battery Analog :")); ser.println(powerAnalogReading);

  //#endif


  if (lastSimBatteryRead > 0) {

    // 1023 -> lastSimBatteryRead

    //maxVal ->x?

    //x=lastSimBatteryRead*maxVal /1023;

    float fff = lastSimBatteryRead;

    fff *= powerAnalogReading;

    fff /= 1023;

    powerReadingTranslated0 = fff;

    //#ifdef DEBUGFULL

    //ser.print(F("Translated to Volts :")); ser.println(powerReadingTranslated0);

    //#endif

  }

  else {

    powerReadingTranslated0 = maxVal;

  }

}


/*from ...google....as for batteries

   NiMH cells start at about 1.5 V right when fully charged,

   drop to about 1.2 V most of their discharge life,

   and are pretty much empty at 900 mV.

   Stopping there is usually safe. 800 mV is where you definitely want to stop to avoid damage

*/

/*for step up/down pololu converter

   The output voltage can be up to 3% higher than normal when there is little or no load on the regulator.

   The output voltage can also drop depending on the current draw,

   especially when the regulator is boosting a lower voltage to a higher one (stepping up),

   although it should remain within 5% of the set voltage.

   5% of 4 volts mean 3.8 !!!! ..normal !!!

   a)So below 3800 we may have problem on pololu

   b)between 3800 and 3900 we DONT want to count the analog power because it will give us falt result

   c)Only

*/

bool checkToExitFromLowPower() { //in case no latch main power relay we can just take intoo account sim voltage?!!!

  bool mustExitFromLowPower = false;

  if (lastSimBatteryRead > 0) {//if reading sim answer was succesfull


    if (powerReadingTranslated0 < romReadBatteryThreshold()) {

      ++continiusllyTranslatedBelowThreshold;//now for sure is 1 and will exit if times=0

      mustExitFromLowPower = continiusllyTranslatedBelowThreshold > romReadBelowBatteryThresholdAcceptableTimes();

    }

    else {

      continiusllyTranslatedBelowThreshold = 0;

      //return false;

    }


  }

  //else{return false;}

  return mustExitFromLowPower;

}




void powerOff() {


#ifdef DEBUG

  blinc(5);

#endif



  relayMain_POWER_OFF();

}




void work() {


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

#ifdef DEBUG

  //no possible to update this before ..like not slept but sleeping...!!!!!!!!!!..because printings must be here in work to not loose time

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

  ser.print(F("EXACTLY ? "));

  ser.print(timesPowerDown8Seconds);

  ser.print(F(" *~8 + "));

  ser.print(millisRemainForNormalDelay);

  ser.print(F(" = "));

  ser.print(mustDelayOrSleep);

  ser.print(F(" iter:")); ser.print(iter); ser.print('/'); ser.println(currDay);

#endif


  if (!ModeLcd) {


    resetPinModes();

    relaySensorsOpenNice();//now sensors and sim is powered

    delay(100);

    //ser.println("resetPinModes()/relaySensorsOpenNice()");

  }



  storeIterReadings();//BASIC JOB !!!



  if (!ModeLcd) {

    //execute safety CFUN=1 in case relays does not close?

    openSimHasNetAndPinOk();

    //ser.println("openSimHasNetAndPinOk()");

  }//else already have opened on setup


  //this will read battery from SIM800L but in case i have a pololu step up-down will be always at 4.3 Volts....

  //this is very nice because i can compute dynamically (without contant reference to running voltage)...and can play with pololu VOUT

  //prefering VOUT low just to be able to open relays ,run SIM (3.4-4.4),,,and lcd can be seen.

  delay(1000);//to stabilize -a little- step up converter?NOT SURE that this have effect on voltage teardrops in diagram

  execAndReadATBattery();//ser.print("Battery Voltage");

#ifdef LATCH_MAIN_RELAY

  delay(1000);//to stabilize -a little- step up converter?NOT SURE that this have effect on voltage teardrops in diagram

  readPowerAndTranslateAccordingSimsPower();

  if (ModeLcd) {

    lcd.setCursor(0, 1);

    lcd.print(lastSimBatteryRead);

    lcd.print(' ');

    lcd.print(powerAnalogReading);

    lcd.print(' ');

    lcd.print(powerReadingTranslated0);

  }

#endif


  if (romReadPhpEnabled()) {

    sendPhpData();

  }

  if (iter == 1) {

    byte smsMode = romReadSmsEnabled();

    sendDailySms(smsMode);//will check if zero....do nothing

    ++currDay;

  }



  if (!ModeLcd) {




#ifdef LATCH_MAIN_RELAY

    if (checkToExitFromLowPower()) {

      powerOff();

    }

#endif





    relaySensorsUnTrigger();

    releasePinModesForPowerSaving();

    //execute safety CFUN=0  command  in case relays does not close?

    //or even better AT+CPOWD=[0 or 1] but this need 1 pin to wake up!!!

    //ser.println("relaySensorsUnTrigger()/releasePinModes()");

  }



#ifdef DEBUG

  ser.println();

#endif



}// end work



void relaySensorsOpenNice() {

  relaySensorsTrigger();

  unsigned long sl = 1000;

  if (romReadDhtEnabled()) {

    sl += 2000; //Dht wants some time....Here we can also take some HX711 readings?

  }

  delay(sl);//now must be ready for sensing

}

void storeIterReadings() {

  char chi;

  if (romReadDhtEnabled()) {

    dhtRead();

    if (isnan(t)) {

      chi = ERROR_CHAR;

#ifdef DEBUG

      ser.print('?');

#endif

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

    else {

      chi = t;

#ifdef DEBUG

      ser.print((int)chi); ser.print(' ');

#endif

    }

    temperature[iter] = chi;


    if (isnan(h)) {

      chi = ERROR_CHAR;

#ifdef DEBUG

      ser.print('?');

#endif

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

    else {

      chi = h;

#ifdef DEBUG

      ser.print((int)chi); ser.print(' ');

#endif

    }

    humidity[iter] = chi;

  }

  else {

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

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


  }




  chi = 0;

  int posOfFreshReading = iter * ScalesCanHandle;

  int half = SensationsPerSms * ScalesCanHandle + posOfFreshReading;

  while (chi < ScalesCanHandle) {

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

    if (romReadScaleEnabled(chi)) {

      if (readWeight(chi, true, true)) {

        StoredWeights[posOfFreshReading] = lastGrams;

#ifdef DEBUG

        ser.print(StoredWeights[posOfFreshReading]); ser.print('<'); ser.print(lastReading); ser.print(',');

#endif

      }

      else {

        StoredWeights[posOfFreshReading] = ERROR_CHAR;

#ifdef DEBUG

        ser.print('?');

#endif

      }

    }

    else {

      StoredWeights[posOfFreshReading] = DISABLED_CHAR;

    }



    ++half;

    ++posOfFreshReading;

    ++chi;

  }


#ifdef DEBUG

  ser.println();

#endif


  ++iter;

  iter %= SensationsPerSms;

}


byte success;

#define SIGNAL_ALSO

long lastSignal;

void sendPhpData() {

  //ser.println("sendPhpData()");

  success = 0; //state 0 beginning


  #ifdef SIGNAL_ALSO

  execAndReadATSignal();

  lastSignal=lastNumberRead;

  #endif



  String stri;

  romReadApn(stri);

  simGetIP(stri);

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

  if (foundOK) {

    ++success;//state 2 ip obtained

    sim->println(F("AT+HTTPINIT"));

    delay(100);

    readSimToSerial(3000);

    if (foundOK) {

      ++success;//state 3 OK AT+HTTPINIT


      sim->println(F("AT+HTTPPARA=\"CID\",1"));

      delay(100);

      readSimToSerial(3000);

      if (foundOK) {

        ++success;//state 4 OK AT+HTTPPARA

        simMakeHttpPhpGetSession(stri);

      }

      else {

        //ser.println("Cant set http params");

      }

    }

    else {

      //ser.println("Cant init HTTP");

    }

    sim->println(F("AT+HTTPTERM"));

    delay(100);

    readSimToSerial(3000);

  }

  else {

    //ser.println("Cant get IP");

  }

  //}

  //else {

  //ser.println("Cant open sim");

  //}

  sim->println(F("AT+SAPBR=0,1"));//close bearer ip connection

  delay(100);

  readSimToSerial(65000);


#ifdef DEBUG

  ser.print("php:"); ser.print(success);

#endif


}




bool simGetIP(String & apnWithoutPassword) {

  bool out = false;

  //Step 1                              //9.2.1 AT+SAPBR Bearer Settings for Applications Based on IP


  //For me working without "AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\"";//AT+SAPBR=3,1,"Contype","GPRS"....fix this in case...


  sim->print(F("AT+SAPBR=3,1,\"APN\",\"")); //Max ResponseTimeWhen <cmd_type> is 1, 85 secondsWhen <cmd_type> is 0, 65 seconds

  sim->print(apnWithoutPassword);        //+SAPBR: (0-4),(1-3), "ConParamTag","ConParamValue"

  sim->println('\"');                    //AT+SAPBR=<cmd_type>,<cid>[,<ConParamTag>,<ConParamValue>],<cid> Bearer profile identifier

  //<cmd_type>0 Close bearer,1 Open bearer,2 Query bearer,3 Set bearer parameters,4 Get bearer parameters

  delay(3000);                        //"APN" Access point name string: max 64chars,"USER"max32 chars,"PWD"max 32 chars

  readSimToSerial(85000); //SOS ...85 sec max !!!


  //Step 2

  if (foundOK) {

    sim->println(F("AT+SAPBR=1,1"));//allocating IP address here

    delay(3000);

    readSimToSerial(85000); //SOS ...85 sec max !!!

    out = foundOK;

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

#ifdef DEBUGFULL

    if (foundOK) {

      sim->println(F("AT+SAPBR=2,1"));//showing IP address for debug

      delay(100);

      readSimToSerial(5000);

    }

#endif

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


  }


  return out;


}

void simPrintPhpField(byte fieldId, long val) {

  sim->print(F("&field"));

  sim->print(fieldId);

  sim->print('=');

  sim->print(val);

}

void simMakeHttpPhpGetSession(String &stri) {


  romReadPhpServer(stri);

  sim->print(F("AT+HTTPPARA=\"URL\",\""));//Max ResponseTime-

  sim->print(stri);

  sim->print('/');



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

  romReadPhpQuery(stri);

  sim->print(stri);


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

  romReadThingSpeakKey(stri);

  sim->print(stri);


  stri.remove(0, stri.length()); //clearing




  byte tempiter = iter;

  if (tempiter > 0) {

    tempiter--;

  }

  else {

    tempiter = SensationsPerSms - 1; //last

  }

  //ser.print("php_itering ");ser.println(tempiter);


  int posOfFreshReading = tempiter * ScalesCanHandle;

  int i = 0;

  while (i < ScalesCanHandle) {

    if (romReadScaleEnabled(i)) {

      simPrintPhpField(i + 1, StoredWeights[posOfFreshReading]);//&field=[1,5] scale[0,4]

    }

    ++posOfFreshReading;

    ++i;

  }

  if (romReadDhtEnabled()) {

    ++i;

    //combining temp and hum....

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

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


    int cc = temperature[tempiter];

    int hh = humidity[tempiter];


    if (hh > 99) {

      hh = 99;

    }

    if (cc < 0) {

      hh = -hh;

    }

    int th = cc;

    th *= 100;

    th += hh; //if 100 will add 1 celsiu and seems as zero!

    simPrintPhpField(i, th);//&field=6 (temperature & humidity)


    //replaced from above to just have one empty space on ThingSpeak.....

    //simPrintPhpField(i, temperature[tempiter]);//&field=6 (temperature)

    //++i;

    //simPrintPhpField(i, humidity[tempiter]);//&field=7 (humidity)

  }


  long newFutureSendingSignalAlso=lastSimBatteryRead;

  #ifdef SIGNAL_ALSO

  newFutureSendingSignalAlso=lastSignal;

  newFutureSendingSignalAlso*=10000l;

  newFutureSendingSignalAlso+=lastSimBatteryRead;

  #endif

  

  simPrintPhpField(8, newFutureSendingSignalAlso);//&field=8 (running voltage)

#ifdef LATCH_MAIN_RELAY

  simPrintPhpField(7, powerReadingTranslated0);//true / translated voltage

  //simPrintPhpField(5,PowerReading);//temp for debug..

#endif



  sim->print('\"');

  sim->println();//AT+HTTPPARA...//Max ResponseTime-


  delay(100);

  sim->println();

  readSimToSerial(2000);


  sim->println(F("AT+HTTPACTION=0"));//get action Max ResponseTimeAbout 5 seconds in test, dependence on network status and the size of request website

  delay(100);

  readSimToSerial(5000);//this OK may followed by HTTP answer like +HTTPACTION: 0,200,2 from wgere we can read if 200 succesfull

  if (foundOK) {


    readSimAnswer(1000 * romReadSimSecWaitBeforePowerOff(), ',', true); //HTTPACTION: 0,603,0 or HTTPACTION: 0,200,0




    ++success;//state 5 OK get action

    /*

      sim.println("AT+HTTPREAD");//this no need in 'our' case ...

      delay(100);

      readSimToSerial(4000);

    */

#ifdef DEBUG_SIM_ANSWER

    ser.println();//this will flush also what previuslly read..

#endif


  }


}


void sendDailySms(byte smsMode) {

  if (smsMode > 0) {

    success = 0; //state 0 beginning



    String phone;

    if (smsMode % 2 == 1) {

      //ser.println("sms to Primary");

      romReadSmsPrimaryNumber(phone);

      smsStoredWeightsAt(phone);

    }

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

    smsMode >>= 1;

    if (smsMode % 2 == 1) {

      //ser.println("sms to Secondary");

      romReadSmsSecondaryNumber(phone);

      smsStoredWeightsAt(phone);

    }

    //}

#ifdef DEBUG

    ser.print(F(" sms:")); ser.print(success);//zero if call instead of sms....

#endif

  }

}


void makeCallAndHungup(String & phone, unsigned long hungupAfter) {

  sim->print(F("ATD+ +"));//make a call//Max Response Time 20s

  sim->print(phone);//to this phone....command

  sim->println(';');

  //readSimToSerial(20000);

  //ser.println(foundOK);

  delay(hungupAfter);//call for some seconds.Basically is NOT some seconds...must fix but no care for this...

  sim->println(F("ATH"));//Max Response Time 20s ALSO !!!!!!!!!!!!!!!!

  delay(1000);


}

void smsStoredWeightsAt(String & phone) {

#ifdef DEBUG

  ser.print(' '); ser.print(phone);

#endif



  if (romReadCallInsteadSms()) {

    makeCallAndHungup(phone, 12000); //call for 12 seconds.Basically is NOT 12 seconds...must fix but no care for this...

  }

  else {



    //opening sms dialog

    sim->println(F("AT+CMGF=1"));//Max ResponseTime-

    delay(100);

    readSimToSerial(2000);


    //AT+CMGS Max Response Time 60s

    sim->print(F("AT+CMGS=\"")); sim->print(phone); sim->println('\"');

    delay(100);


    //sim.println("sms data here");

    smsStoredWeights();


    //closing sms dialog

    sim->write(26);

    sim->println();


    delay(1000);

    readSimToSerial(60000);

    if (foundOK) {


#ifdef DEBUG

      ser.print(F(" ok "));

#endif

      ++success;

    }

  }



}

/*(example of sms)

   2 (2nd day)(the readings NOW when iter =0)

   19C72%

   154-1(154 grams -1 from yesterday same time reading)


   -6 (the readings -6 hours from now,iter was 3)

   20C75#

   154-1


   -12(the readings -12 hours from now,iter was 2)

   20C75#

   154-1


   -18(the readings -18 hours from now,iter was 1)

   20C74#

   155-0

*/

void smsStoredWeights() {

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

  int posOfFreshReading;

  long prev, neo;

  bool dhtEnabled = romReadDhtEnabled();

  sim->println(currDay);


  while (i < SensationsPerSms) {

    if (i > 0) {

      sim->println(timThe);

    }


    --index; if (index < 0) {

      index += SensationsPerSms;

    }

    posOfFreshReading = index * ScalesCanHandle;


    if (dhtEnabled) {

      neo = (int)temperature[index];

      if (neo >= 0) {

        sim->print(neo); sim->print('C');

      }

      neo = (int)humidity[index];

      if (neo >= 0) {

        sim->print(neo); sim->print('%');

        //++prev;

      }


      sim->println();

    }



    currScale = 0;

    while (currScale < ScalesCanHandle) {

      neo = StoredWeights[posOfFreshReading];

      if (neo != DISABLED_CHAR && neo != ERROR_CHAR) {


        //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('#');}



        sim->print(neo);

        prev = StoredWeights[posOfFreshReading + half];

        if (prev != DISABLED_CHAR && prev != ERROR_CHAR) {

          neo -= prev;

          if (neo >= 0) {

            sim->print('+');

          }


          //neo/=10;//char roundingDirection;?...not useing

          sim->print(neo);

        }

        else {

          sim->print('?'); ///previus was error or disabled?...(disabled?!!!)

        }

        sim->println();

      }//else not showing offcource on sms

      ++posOfFreshReading;

      ++currScale;

    }


    ++i;

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

    if (i < SensationsPerSms) {

      sim->println();//1 line char more for easy read sms from human between readings

    }

  }

}