Dec 5, 2016

DieselPunk Cellphone - Walnut Case, Powered by Adafruit Feather Fona:Software


Software

The excellent Adafruit SSD1306/Adafruit_GFX libraries would normally be my go-to display tools: fast and easy-to-use. These libraries can use significant amounts of memory, for good reasons. With so much going on, this project needed to reduce SRAM and program memory. Instead of the Adafruit libs, the project uses u8glib, which thrifty on both kinds of memory. There's a great explanation of the basics of using this lib at Henry's Bench.

Unfortunately, u8glib is no longer being developed by the maintainer. A new u8g2 lib is available. However, after making necessary changes to use the u8g2 lib, the DieselPunk Cellphone sketch uses too much program memory and will not compile. Therefore, I'm only supplying code to work with the u8glib. 

A brief tour of the the code - the sketch does the following:
  • Sends displays to the SSD1306 using the u8glib library
    • Splash Screen first up
  • Connects to the carrier network
  • Controls the NeoPixel Jewel
    • ...and the flashlight function
  • Dials, Answers and Hang Up calls
    • Pick from Favorite numbers you specify
  • Plays FM Radio stations from a list you supply
Splash Screen
Create a bitmap with Inkscape/GIMP or your favorite drawing program. Export to a *.png format.
  • Open your picture in GIMP
  • Click Colors/Invert for the SD1306, if needed
    • white was "pixel on" for me
  • Image/Scale Image to your screen dimensions. 
    • Mine worked well at  80W x 64H pixels
  • Image/Mode/Indexed, black & white (1-bit) palette
  • Export as XBM (.xbm extension)
  • Open the XBM file in a text editor, such as gedit in Linux or notepad in Windows.
  • Copy contents of xbm file into your Arduino sketch - load into PROGMEM.
  • Adjust the starting cursor position in your software's bitmap display to center the bitmap on screen.
Code
In the following code block, there isn't much error checking going on. The code will hang with "Looking for Service" displayed from the msgNetService call,  if it can't connect. Takes 10-20 seconds to connect on T-Mobile, for me.
 u8g.firstPage();   
  do {  
   msgConnect();  
  } while( u8g.nextPage() );  
   delay(2000);  
  // Check FONA is there  
  fonaSS.begin(9600); // if you're using software serial  
  // See if the FONA is responding  
  if (! fona.begin(fonaSS)) {  
   Serial.println(F("Couldn't find FONA :("));  
   while (1);  // <=== Will hang here if Fona unresponsive
  }  
  // Check we can connect to the network  
  while (fona.getNetworkStatus() != 1) {  //  <=== Will hang here if no network!
   u8g.firstPage();   
  do {  
   msgNetService();  
  } while( u8g.nextPage() );  
  }  
   u8g.firstPage();   
  do {  
   msgNetConnect();  
  } while( u8g.nextPage() );  
   delay(2000);  

When connected,  "Connected to Network!" text will display for two seconds from msgNetConnect function. After that, the main menu will display. 

Most of the time the sketch will just process the loop/main menu display, waiting for a keypress. Updated network signal strength [getRSSI] and battery level [getBattery], along with time and date, will update every 10 seconds. You may see the screen flicker or update slowly if the level updates coincide with screen refresh.
 void loop()   
 {   
   // don't hog the processor!  
   if (millis() - lastDisplay > 10000)  
   {  
   dbi = getRSSI(i_8);  
   vbat = getBattery(i_16);  
   cellDate();  
   cellTime();  
   lastDisplay = millis();  
   }  
   u8g.firstPage();   
  do {  
   dispMenu(dbi, vbat);  
   keyPress();   
  } while( u8g.nextPage() );  
 }  
When a keypress event does happen, the key will trigger one of the following actions:

  • Flashlight function ('*' from main menu) toggles all LEDs on  the Jewel full power/off; off is still 3 middle LEDs on, backlighting the speaker bezel
  • Answer incoming call ('#' from main menu)
  • Enter a number to Dial from the keypad (menu choice '1')
  • Select a stored Favorite to Dial (menu choice '2')
  • Select an FM radio station (menu choice '3') to play

 void keyPress()  
 {  
  char key = keypad.getKey();  
  // NeoPixel Flashlight  
  if (key == '*')  
  { flashlight_on = !flashlight_on;  
   flashlight();   
   return;  
   }  
   // Answer an incoming call  
  if (key == '#')  
  { answerPhone();  
   return;  
   }  
 // convert a single character to int  
  int ikey = key - '0';  
  switch (ikey) {  
   case 1:  
    callEntry();  
    break;  
   case 2:  
    callFavorites();  
    break;  
   case 3:  
    callFMfavs();  
    break;   
   default:   
    // if nothing else matches, do the default  
    // default is optional  
   break;  
  }  
 }  

The functions called from the menu are basically just displays wrapped in 'while' loops. The loops only exit on a specified appropriate keypress, usually '*' ("Do it!") or '#' ("Cancel/Go Back/Hang Up!"). They'll remain in that code loop until the proper key is pressed. There are on-screen cues to indicate what the available keypress values are.

Here's a code snippet from the Dial function, callEntry:
 while ((key != '*') and (key != '#'))  
   {  
   key = keypad.getKey();  
   if (key)  
   {  
    phoneNumber[i] = key;  
    i++;  
    u8g.firstPage();   
    do {  
     callEntryDisplay(phoneNumber);   
    } while( u8g.nextPage() );  
   }  
   }  





No comments:

Post a Comment