Betri is a simple GSM connected battery voltage monitor that will send me messages when my battery gets too low. (it's also Swahili for battery)
In cold weather car batteries lose capacity.
I often forget to turn off my car lights.
During New England summers this isn't an issue, but every winter at least one morning my car battery dies and I end up having to use an emergency charger.
A simple text message should be enough to remind me to go and check out what's causing the drain.
After implementing SMS messages, I plan to integrate Nexmo's voice API with a server to require me to respond to phone calls with an appropriate voice confirmation.
This should force me to wake up if the alerts happen while I'm sleeping. (and they will)
I also plan to integrate it with my on-going project to design a network of sensors that should make my car more connected, with alerts on issues like open windows during inclement weather, or lights being left o
After moving on from the flash issue I tried implementing a simple heartbeat check for the GSM module.
When you send the text "AT" you should get the response, "OK" back*
*The GSM module echos back everything you send, so the full string is really "AT <newline> OK"
What I was getting from the debug port was this:
Attempting to read from SIM800
Read line from SIM800: ???K
I'll start by saying the decision to keep the debug port already proved invaluable. When I tried having the ATTiny send the "AT" response while the GSM module was hooked up to my USB-to-Serial breakout, I was getting "AT <newline> OK" as expected.
That meant the ATTiny was getting exactly the correct response, but something was wrong with the ATTiny's serial receiving routine.
All I had to go on was the fact I was getting the correct number of characters, and the ATTiny was sending "AT" as it should.
The fix turned out to be pretty straight forward though, simply adding the line:
delay(500);
before and after my sleep function gave me what I expected:
Attempting to read from SIM800
Read line from SIM800: AT <newline> OK
...kind of.
There was supposed to be a debug statement that contained what I was sending to the sim module, and sometimes I was still getting garbage.
But it was progress.
TinyDebugSerial and SoftwareSerial are very sensitive to timing because they implement UART without the supporting hardware. Shortening my debug statements (thus having fewer statements overlapping between the two) produced reliable output without sleep statements:
I wanted to have a convenient debug port that I could use to see what was going on when my ATTiny talked to the GSM module.
I went with TinySerialDebug, a transmit-only library and everything was working great.. until I added the SoftwareSerial library:
I was out of flash on my ATTiny85.
Meaning my firmware's code was taking too much space on the constrained ATTiny85.
A flag that disabled all the debug port related code, brought me back under the space limit, but I definitely needed a way to talk to the GSM module (the ATTiny doesn't have hardware UART). And I really wanted a debug port because talking to the GSM module was going to be hard enough to debug without some way to tell what the ATTiny was saying. So I set about solving the space issue without cutting either library.
My first instinct was that SoftwareSerial was too large, since it implements the entire Stream interface that Arduino provides, which has a ton of convenience functions.
I looked up ways to optimize sketch size, but realized, while I did suspect SoftwareSerial was the culprit here, I hadn't actually proven it was what was eating up all my flash space.
Some searching brought me to avr-nm. It's a utility that takes an "elf" file ( the firmware that gets uploaded to the AVR) and spits out something like this:
avr-nm --size-sort -Crtd firmware.elf
00000390 T realloc
00000304 T malloc00000286 T free00000244 T __vector_2
Those are functions defined in my code, and in the libraries that I imported sorted by the amount of space they were taking up.
That screenshot is from after I fixed the issue. Here's one from before I fixed it:
avr-nm --size-sort -Crtd firmware.elf
00000734 T dtoa_prf
00000432 T __ftoa_engine00000390 T realloc
00000304 T malloc
I bolded the two functions that stuck out the most. They stuck out not only because they were taking the most space, but because if dtoa did what I thought it did, converted a double to a string (which is what it does), there was no mission-critical code that should be calling it.
So if I knew that I didn't need to convert numbers to strings for anything related to the end goal of the project, where could I have been calling it?
The watchdogSleep function uses the ATTiny's watchdog timer to "fake" long term sleeping. The watchdog is set to 8 seconds (it's max value), the ATTiny goes to sleep, and the watchdog timer wakes it back up. It repeats this until enough seconds have passed in 8 seconds increments.
That debug statement to let me know that it was going to sleep.
But my debug statement had this line:
debug.print(String(seconds));
It was taking 'seconds', a 'double', and turning it into a 'String'. Doing so was bringing in the 'dtoa' function, which was massive compared to the the serial code (Neither serial library even came in the top 5 functions for memory usage)
To put into perspective how large it was, even changing that line to:
debug.print(String((int)seconds));
(note the added cast to int), was enough to bring me back under the limit!
I ended up adding an overload to the `print` method that took an integer, and changed the type of seconds to an unsigned int. A double never really made sense anyways, a negative value would have left the player sleeping forever, and the resolution was limited to 1 second increments at best.
While going through space optimization guides I also realized how wasteful Arduino's String() could be, so I have one more task to take on before calling the flash issue completely solved, but won't have to worry about hitting the limits of the ATTiny85 just yet.