diff --git a/satchat/dec.h b/satchat/dec.h new file mode 100644 index 0000000..560a33c --- /dev/null +++ b/satchat/dec.h @@ -0,0 +1,107 @@ +typedef void (touch_button_function)(const uintptr_t functionData); + +enum menu {MENU_NONE, MENU_MAIN, MENU_SETTINGS, MENU_COORDINATES, MENU_MESSAGES, MENU_KEYBOARD, MENU_INBOX, MENU_OUTBOX, MENU_PRESET_MESSAGES, MENU_COUNT};//MENU_COUNT MUST BE LAST. + +struct menu_context +{ + enum menu menu; + enum menu lastMenu; + bool justChanged; +}; + +struct settings +{ + uint16_t breadcrumbInterval; + bool breadcrumbs; + bool militaryTime; + bool voicedMenus; + bool voicedKeys; + bool instantButtons; + char outgoingNumber[16]; +}; + +struct eeprom//USED FOR FINDING OFFSETS AND DOCUMENTING EEPROM LAYOUT. DO NOT CREATE INSTANCE OF THIS STRUCT. +{ + uint32_t header; + struct settings; + struct message inbox[8]; + struct message outbox[4]; + struct message predefined[4]; +}; + +struct touch_button +{ + uint16_t x; + uint16_t y; + uint16_t w; + uint16_t h; + uint16_t colour; + uint16_t colourPressed; + touch_button_function *function; + uintptr_t functionData; +}; + +struct menu_buttons +{ + struct touch_button *button; + bool *buttonState; + uint8_t buttonCount; +}; + +struct touch_point +{ + union + { + struct + { + uint16_t x; + uint16_t y; + }; + struct + { + uint16_t raw[2]; + }; + }; + bool isPressed; + bool justPressed; + bool justReleased; +}; + +struct date_time +{ + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; +}; + +struct gps +{ + double longitude; + double latitude; + struct date_time timestamp; +}; + +struct message +{ + char number[16]; + struct gps gps; + char text[MESSAGE_LENGTH]; +}; + +struct message_editor +{ + char text[MESSAGE_LENGTH]; + bool shift; + bool caps; +}; + +struct device_state +{ + struct settings settings; + struct menu_context menuContext; + struct touch_point lastTouchPoint; + struct message_editor message; +}; \ No newline at end of file diff --git a/satchat/def.h b/satchat/def.h new file mode 100644 index 0000000..f63313b --- /dev/null +++ b/satchat/def.h @@ -0,0 +1,12 @@ +#define MENU_TEXT_SIZE 3 +#define MENU_TEXT_WIDTH 8 +#define MENU_TEXT_HEIGHT 11 + +//STANDARD SMS MESSAGE LENGTH IS 160 CHARACTERS. +#define MESSAGE_LENGTH 160 + +#define PASTEL_GREEN 0xE77B +#define PASTEL_RED 0xFF3A +#define PASTEL_BLUE 0xDF5E +#define PASTEL_YELLOW 0xFF99 +#define NES_RED 0xFB8D \ No newline at end of file diff --git a/satchat/globals.c b/satchat/globals.c new file mode 100644 index 0000000..a30ad2c --- /dev/null +++ b/satchat/globals.c @@ -0,0 +1,90 @@ +struct device_state deviceState; + +const struct touch_button menuMainButtons[] = +{ + {0, 53, 159, 93, PASTEL_GREEN, NES_RED, tb_set_menu, MENU_MESSAGES}, + {162, 53, 159, 93, PASTEL_RED, NES_RED, tb_set_menu, MENU_KEYBOARD},//TEMPORARY MENU ASSIGNMENT FOR TESTING + {0, 148, 159, 93, PASTEL_BLUE, NES_RED, tb_set_menu, MENU_COORDINATES}, + {162, 148, 159, 93, PASTEL_YELLOW, NES_RED, tb_set_menu, MENU_SETTINGS} +}; +bool menuMainButtonStates[sizeof(menuMainButtons) / sizeof(struct touch_button)]; + +const struct touch_button menuMessagesButtons[] = +{ + {0, 0, 106, 51, ILI9341_WHITE, NES_RED, tb_set_menu, MENU_MAIN}, + {0, 53, 159, 93, PASTEL_GREEN, NES_RED, tb_set_menu, MENU_INBOX}, + {162, 53, 159, 93, PASTEL_RED, NES_RED, tb_set_menu, MENU_PRESET_MESSAGES}, + {0, 148, 159, 93, PASTEL_BLUE, NES_RED, tb_set_menu, MENU_OUTBOX}, + {162, 148, 159, 93, PASTEL_YELLOW, NES_RED, tb_set_menu, MENU_KEYBOARD} +}; +bool menuMessagesButtonStates[sizeof(menuMessagesButtons) / sizeof(struct touch_button)]; + +const struct touch_button menuKeyboardButtons[] = +{ + {0, 0, 106, 51, ILI9341_WHITE, NES_RED, tb_set_menu, MENU_MESSAGES}, + {9, 111, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, '1'}, + {35, 111, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, '2'}, + {61, 111, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, '3'}, + {87, 111, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, '4'}, + {113, 111, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, '5'}, + {139, 111, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, '6'}, + {165, 111, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, '7'}, + {191, 111, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, '8'}, + {217, 111, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, '9'}, + {243, 111, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, '0'}, + {269, 111, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, '-'}, + {295, 111, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, '='}, + {22, 137, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'q'}, + {48, 137, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'w'}, + {74, 137, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'e'}, + {100, 137, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'r'}, + {126, 137, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 't'}, + {152, 137, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'y'}, + {178, 137, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'u'}, + {204, 137, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'i'}, + {230, 137, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'o'}, + {256, 137, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'p'}, + {282, 137, 38, 25, PASTEL_YELLOW, NES_RED, tb_key_backspace, NULL},//BACKSPACE + {1, 163, 32, 25, PASTEL_YELLOW, NES_RED, tb_key_caps, NULL},//CAPS + {35, 163, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'a'}, + {61, 163, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 's'}, + {87, 163, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'd'}, + {113, 163, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'f'}, + {139, 163, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'g'}, + {165, 163, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'h'}, + {191, 163, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'j'}, + {217, 163, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'k'}, + {243, 163, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'l'}, + {269, 163, 25, 25, PASTEL_YELLOW, NES_RED, tb_key_enter, NULL},//ENTER + {22, 189, 25, 25, PASTEL_YELLOW, NES_RED, tb_key_shift, NULL},//SHIFT + {48, 189, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'z'}, + {74, 189, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'x'}, + {100, 189, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'c'}, + {126, 189, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'v'}, + {152, 189, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'b'}, + {178, 189, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'n'}, + {204, 189, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, 'm'}, + {230, 189, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, ','}, + {256, 189, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, '.'}, + {282, 189, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, '/'}, + {35, 215, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, ';'}, + {61, 215, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, '\''}, + {87, 215, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, '`'}, + {113, 215, 129, 25, PASTEL_YELLOW, NES_RED, tb_key, ' '},//SPACE + {243, 215, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, '['}, + {269, 215, 25, 25, PASTEL_YELLOW, NES_RED, tb_key, ']'} +}; +bool menuKeyboardButtonStates[sizeof(menuKeyboardButtons) / sizeof(struct touch_button)]; + +const struct menu_buttons menuButtons[MENU_COUNT] = +{ + {NULL, NULL, 0},//MENU_NONE + {menuMainButtons, menuMainButtonStates, sizeof(menuMainButtons) / sizeof(struct touch_button)};//MENU_MAIN + {NULL, NULL, 0},//MENU_SETTINGS + {NULL, NULL, 0},//MENU_COORDINATES + {menuMessagesButtons, menuMessagesButtonStates, sizeof(menuMessagesButtons) / sizeof(struct touch_button)},//MENU_MESSAGES + {menuKeyboardButtons, menuKeyboardButtonStates, sizeof(menuKeyboardButtons) / sizeof(struct touch_button)},//MENU_KEYBOARD + {NULL, NULL, 0},//MENU_INBOX + {NULL, NULL, 0},//MENU_OUTBOX + {NULL, NULL, 0}//MENU_PRESET_MESSAGES +}; \ No newline at end of file diff --git a/satchat/satchat.ino b/satchat/satchat.ino new file mode 100644 index 0000000..ef70be1 --- /dev/null +++ b/satchat/satchat.ino @@ -0,0 +1,735 @@ +#include //I use a different library because I have a different chip +#include +#include +#include +#include +#include // Core graphics library +#include +#include +#include "TouchScreen.h" +#include +#include +#include + +#include "def.h" +#include "dec.h" +#include "globals.c" +#include "ui.h" +#include "ui.c" + + +// These are the four touchscreen analog pins +#define YP A2 // must be an analog pin, use "An" notation! +#define XM A3 // must be an 00analog pin, use "An" notation! +#define YM 47 // can be any digital pin +#define XP 48 // can be any digital pin + +// This is calibration data for the raw touch data to the screen coordinates +#define TS_MINX 90 +#define TS_MINY 75 +#define TS_MAXX 920 +#define TS_MAXY 900 + +#define MINPRESSURE 10 +#define MAXPRESSURE 1000 + +#define DS13074_CS_PIN 10 // DeadOn RTC Chip-select pin +#define INTERRUPT_PIN 4 // DeadOn RTC SQW/interrupt pin (optional) + +// For better pressure precision, we need to know the resistance +// between X+ and X- Use any multimeter to read it +// For the one we're using, its 300 ohms across the X plate +TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300); + +// The display uses hardware SP +#define TFT_CS 53 +#define TFT_DC 49 +Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC); +long int touchPoint[2]; +int mode = 0; + +//Set up the LSM303 +Adafruit_LSM303_Mag_Unified mag = Adafruit_LSM303_Mag_Unified(12345); +Adafruit_LSM303_Accel_Unified accel = Adafruit_LSM303_Accel_Unified(22345); //the number here is random, but needs to be unique as it is an ID number + +struct DateTime +{ + short hour; + short minute; + short second; + short day; + short month; + short year; +}; +struct GPS +{ + double longitutde; + double latitude; + struct DateTime dateTime; +}; +struct Message +{ + char recipient[13]; + char content[200]; + struct GPS coords; +}; +// struct Settings +// { +// char predefinedMessages[4][200]; +// bool breadcrumbs; +// short breadInterval; +// bool militaryTime; +// char recipient[13]; +// }; +// struct Eeprom +// { +// struct Message inbox; +// struct Message outbox; +// struct Settings settings; +// }eeprom; + +byte factorySettings; + +void setup(void) { + + Serial.begin(9600,SERIAL_8N1); + Serial.println("Device initiallised"); + Serial1.begin(9600,SERIAL_8N1); + Serial2.begin(9600,SERIAL_8N1); + Serial3.begin(9600); //EMIC serial + + Serial3.print("\nX\nS"); //End command (just in case),Stop speaking, prepare to speak. Do not end command until message has been sent; + Serial3.println("Powered up."); //"SPowered up\n" is the entire command and terminates by itself. "S\nPowered up" will not work. + + //set up the emic + //Serial3.println("N0"); //set voice; N0 to N8 + //Serial3.println("V18"); //set volume; V-48 to V18 + //Serial3.println("W75"); //set words per minute. W75 to W600 + + //turn on screen + tft.begin(); + tft.setRotation(3); + tft.fillScreen(ILI9341_BLACK); + + //turn on compass + mag.begin(); + accel.begin(); + + #ifdef INTERRUPT_PIN // If using the SQW pin as an interrupt + pinMode(INTERRUPT_PIN, INPUT_PULLUP); + #endif + rtc.begin(DS13074_CS_PIN); + rtc.update(); + rtc.enableAlarmInterrupt(); + rtc.setAlarm1(0); + //rtc.setTime(0, 40, 2, 5, 26, 9, 19); + + + + + // EEPROM.get(0,eeprom); + // factorySettings = EEPROM.read(0); //the MEGA default for EEPROM is 255; if we have set up the device before it will not be 255 + //if(factorySettings == 255){ //this is a new device; conduct first time set up + // SetupEeprom(); + //} + if(EEPROM.read(offsetof(struct eeprom, header), struct eeprom.header) == 0xFFFFFFFF) SetupEeprom(); + + deviceState.settings.instantButtons = false;//Change to true for instant buttons. + + + //Build the main menu + mainMenu(); +} + +void loop() +{ + process_gui(); + //delay(3500); + // Retrieve a point + TSPoint p = ts.getPoint(); + // Scale from ~0->1000 to tft.width using the calibration #'s + touchPoint[0] = map(p.y, TS_MAXY, TS_MINY, 0, tft.width()); + touchPoint[1] = map(p.x, TS_MINX, TS_MAXX, 0, tft.height()); + + if(mode == 0) { + if(p.z > MINPRESSURE && p.z < MAXPRESSURE){ + if(touchPoint[0] > 160 && touchPoint[1] > 145){ //cyan + settings(); + } else if(touchPoint[0] < 160 && touchPoint[1] > 145){ + coordinates(); + }else if(touchPoint[0] > 160 && touchPoint[1] > 50){ + customMessages(); + }else if(touchPoint[0] < 160 && touchPoint[1] > 50){ + presetMessages(); + } + } + + }else if(mode == 1) { + EEPROM.get(0,eeprom); + if(p.z > MINPRESSURE && p.z < MAXPRESSURE){ + if(touchPoint[0] > 160 && touchPoint[1] > 145){ + SendMessage(4); + } else if(touchPoint[0] < 160 && touchPoint[1] > 145){ + SendMessage(3); + Serial1.println("SMessage Sent"); + delay(1000); + }else if(touchPoint[0] > 160 && touchPoint[1] > 50){ + SendMessage(2); + Serial1.println("SMessage Sent"); + delay(1000); + }else if(touchPoint[0] < 160 && touchPoint[1] > 50){ + SendMessage(1); + Serial1.println("SMessage Sent"); + delay(1000); + }else if(touchPoint[0] < 50 && touchPoint[1] < 50){ + EEPROM.put(0,eeprom); + mainMenu(); + } + } + + }else if(mode == 2) { + EEPROM.get(0,eeprom); + if(p.z > MINPRESSURE && p.z < MAXPRESSURE){ + if(touchPoint[1] > 145){ + //function + }else if(touchPoint[1] > 50){ + tft.fillRect(0, 50, 320, 240, ILI9341_GREEN); + tft.setCursor(5, 55); + tft.setTextColor(0x0000); + tft.setTextSize(1); + tft.print(eeprom.outbox.recipient); + tft.print(" "); + tft.print(eeprom.outbox.coords.dateTime.hour); + tft.print(":"); + tft.print(eeprom.outbox.coords.dateTime.minute); + tft.print(":"); + tft.print(eeprom.outbox.coords.dateTime.second); + tft.print(" "); + tft.print(eeprom.outbox.coords.dateTime.day); + tft.print("/"); + tft.print(eeprom.outbox.coords.dateTime.month); + tft.print("/"); + tft.print(eeprom.outbox.coords.dateTime.year); + //TODO GPS COORDS + tft.print("\n"); + tft.setTextSize(2); + textWrap(eeprom.outbox.content,53,10); + mode = 6; + }else if(touchPoint[0] < 50 && touchPoint[1] < 50){ + EEPROM.put(0,eeprom); + mainMenu(); + } + } + + }else if(mode == 3) { + if(p.z > MINPRESSURE && p.z < MAXPRESSURE){ + if(touchPoint[0] < 50 && touchPoint[1] < 50){ + mainMenu(); + }else if(touchPoint[1] > 50){ + coordinates(); + } + } + + }else if(mode == 4) { //this is settings + EEPROM.get(0,eeprom); + if(p.z > MINPRESSURE && p.z < MAXPRESSURE){ + if(touchPoint[1] > 60 && touchPoint[1] < 110){ + if(eeprom.settings.breadcrumbs == true){ + eeprom.settings.breadcrumbs = false; + } else if (eeprom.settings.breadcrumbs == false){ + eeprom.settings.breadcrumbs = true; + } + settings(); + }else if(touchPoint[1] > 110 && touchPoint[1] < 160){ + if(eeprom.settings.militaryTime == true){ + eeprom.settings.militaryTime = false; + } else if (eeprom.settings.militaryTime == false){ + eeprom.settings.militaryTime = true; + } + settings(); + }else if(touchPoint[1] > 165 && touchPoint[1] < 185){ + //set text function + settings(); //refresh the screen + }else if(touchPoint[1] > 190 && touchPoint[1] < 230){ + if(touchPoint[0] > 240 && eeprom.settings.breadInterval < 15){ + eeprom.settings.breadInterval = eeprom.settings.breadInterval + 1; + }else if (touchPoint[0] < 70 && eeprom.settings.breadInterval > 0){ + eeprom.settings.breadInterval = eeprom.settings.breadInterval - 1; + } + settings(); //refresh the screen + }else if(touchPoint[0] < 50 && touchPoint[1] < 50){ + //before going to menu, we write everything back into eeprom for next time + EEPROM.put(0,eeprom); + mainMenu(); + } + + } + + } else if(mode == 6) { + if(p.z > MINPRESSURE && p.z < MAXPRESSURE){ + if(touchPoint[0] < 50 && touchPoint[1] < 50){ + customMessages(); + } + } + + }//we probably need another screen for the custom texts + + rtc.update(); + + + #ifdef INTERRUPT_PIN + // Interrupt pin is active-low, if it's low, an alarm is triggered + if (!digitalRead(INTERRUPT_PIN)) + { + #endif + // Check rtc.alarm1() to see if alarm 1 triggered the interrupt + if (rtc.alarm1()) + { + drawTime(); + rtc.setAlarm1(0); + } + if (rtc.alarm2()) + { + rtc.setAlarm2(rtc.minute() + 1, rtc.hour()); + } + #ifdef INTERRUPT_PIN + } + #endif +} + +void textWrap(char * msg, int siz,int lin) +{ + int count = 1; + int size = strlen(msg); + char str[size]; + for(int i = 0; i < size; i++) + { + str[i] = msg[i]; + } + + if(strlen(str) > siz) + { + int len = 0; + char delim[] = " "; + + char *tkn = strtok(str, delim); + + while(tkn != NULL) + { + int add = len + strlen(tkn) + 1; + len = len + add; + if(len > siz) + { + tft.print("\n"); + len = 0; + count++; + } + if(count <= lin) + { + tft.print(tkn); + tft.print(" "); + } + + tkn = strtok(NULL,delim); + } + + } +} + +void textWrap2(char * msg) +{ + int count = 1; + int size = strlen(msg); + char str[size]; + for(int i = 0; i < size; i++) + { + str[i] = msg[i]; + } + + if(strlen(str) > 12) + { + int x = 70; + int len = 0; + char delim[] = " "; + + char *tkn = strtok(str, delim); + + while(tkn != NULL) + { + int add = len + strlen(tkn) + 1; + len = len + add; + if(len > 12) + { + tft.print("\n"); + tft.setCursor(165, x); + x = x + 15; + len = 0; + count++; + } + if(count <= 5) + { + tft.print(tkn); + tft.print(" "); + } + + tkn = strtok(NULL,delim); + } + + } +} + +void textWrap3(char * msg) +{ + int count = 1; + int size = strlen(msg); + char str[size]; + for(int i = 0; i < size; i++) + { + str[i] = msg[i]; + } + + if(strlen(str) > 12) + { + int len = 0; + int x = 165; + char delim[] = " "; + + char *tkn = strtok(str, delim); + + while(tkn != NULL) + { + int add = len + strlen(tkn) + 1; + len = len + add; + if(len > 12) + { + tft.print("\n"); + tft.setCursor(165, x); + x = x + 15; + len = 0; + count++; + } + if(count <= 5) + { + tft.print(tkn); + tft.print(" "); + } + + tkn = strtok(NULL,delim); + } + + } +} + +void presetMessages(){ + mode = 1; + Serial3.print("\nX\nS"); + Serial1.println("SPreset messages."); + tft.fillRect(0, 0, 320, 50, ILI9341_WHITE); + drawBack(); + tft.fillRect(0, 50, 160, 135, ILI9341_RED); + tft.fillRect(160, 50, 240, 135, ILI9341_YELLOW); + tft.fillRect(0, 145, 160, 240, ILI9341_GREEN); + tft.fillRect(160, 145, 320, 240, ILI9341_CYAN); + drawTime(); + + tft.setCursor(5, 55); + tft.setTextColor(0x0000); + tft.setTextSize(2); + textWrap(eeprom.settings.predefinedMessages[0],12,5); + tft.setCursor(165, 55); + textWrap2(eeprom.settings.predefinedMessages[1]); + tft.setCursor(5,150); + textWrap(eeprom.settings.predefinedMessages[2],12,5); + tft.setCursor(165,150); + textWrap3(eeprom.settings.predefinedMessages[3]); +} + +void customMessages(){ + mode = 2; + Serial3.print("\nX\nS"); + Serial1.println("SCustom messages."); + tft.fillScreen(ILI9341_BLACK); + tft.fillRect(0, 0, 320, 50, ILI9341_WHITE); + drawBack(); + drawTime(); + + tft.fillRect(0, 50, 320, 135, ILI9341_GREEN); + tft.setCursor(5, 55); + tft.setTextColor(0x0000); + tft.setTextSize(1); + tft.print(eeprom.outbox.recipient); + tft.print(" "); + tft.print(eeprom.outbox.coords.dateTime.hour); + tft.print(":"); + tft.print(eeprom.outbox.coords.dateTime.minute); + tft.print(":"); + tft.print(eeprom.outbox.coords.dateTime.second); + tft.print(" "); + tft.print(eeprom.outbox.coords.dateTime.day); + tft.print("/"); + tft.print(eeprom.outbox.coords.dateTime.month); + tft.print("/"); + tft.print(eeprom.outbox.coords.dateTime.year); + //TODO GPS COORDS + tft.print("\n"); + tft.setTextSize(2); + textWrap(eeprom.outbox.content,53,5); + tft.fillRect(0, 145, 320, 240, ILI9341_CYAN); +} + +void coordinates(){ + mode = 3; + Serial3.print("\nX\nS"); + Serial3.println("Viewing coordinates."); + tft.fillScreen(ILI9341_BLACK); + tft.fillRect(0, 0, 320, 50, ILI9341_WHITE); + drawBack(); + drawTime(); + + tft.setTextColor(ILI9341_WHITE); + tft.setCursor(130, 55); + tft.setTextSize(2); + tft.println("Bearing:"); + tft.setCursor(130, 75); + drawBearing(); + + tft.setCursor(5, 100); + tft.println("coordinates here"); +} + +void settings(){ + mode = 4; + Serial3.print("\nX\nS"); + Serial1.println("SSettings menu."); + tft.fillRect(0, 0, 320, 50, ILI9341_WHITE); + drawBack(); + tft.fillRect(0,50,320,240, ILI9341_CYAN); + tft.fillRect(5,55,310,40, ILI9341_BLACK); + + //checkbox for breadcrumbs + tft.setCursor(10,60); + tft.setTextColor(ILI9341_WHITE); tft.setTextSize(3); + tft.println("Breadcrumbs"); + tft.fillRect(280,60,30,30,ILI9341_CYAN); + if(eeprom.settings.breadcrumbs == true){ + tft.fillRect(285,65,20,20,ILI9341_BLACK); + } + + //checkbox for 24 hour time + tft.fillRect(5,100,310,40, ILI9341_BLACK); + tft.setCursor(10,105); + tft.setTextColor(ILI9341_WHITE); tft.setTextSize(3); + tft.println("24 Hour Clock"); + tft.fillRect(280,105,30,30,ILI9341_CYAN); + if(eeprom.settings.militaryTime == true){ + tft.fillRect(285,110,20,20,ILI9341_BLACK); + } + //text editor + tft.fillRect(5,145,310,40, ILI9341_BLACK); + tft.setCursor(10,150); + tft.setTextColor(ILI9341_WHITE); tft.setTextSize(3); + tft.println("Set texts"); + + //time editor + tft.fillRect(5,190,310,40, ILI9341_BLACK); + tft.setCursor(50,195); + tft.setTextColor(ILI9341_WHITE); tft.setTextSize(3); + tft.println("Interval: "); + tft.setCursor(230,195); + tft.println(eeprom.settings.breadInterval); + tft.fillRect(10,195,30,30,ILI9341_CYAN); + tft.fillTriangle(15,210,30,200,30,220, ILI9341_BLACK); + tft.fillRect(280,195,30,30,ILI9341_CYAN); + tft.fillTriangle(305,210,290,200,290,220, ILI9341_BLACK); + + //update time + drawTime(); +} + +void SOS(){ + Serial3.print("X\nS"); + Serial1.print("SEmergency protocal activated."); + tft.fillScreen(ILI9341_BLACK); + mode = 5; +} + +void mainMenu(){ + mode = 0; + Serial3.print("X\nS"); + Serial1.println("SMain menu."); + tft.fillRect(0, 0, 320, 50, ILI9341_WHITE); + tft.fillRect(0, 50, 160, 135, ILI9341_RED); + tft.fillRect(160, 50, 240, 135, ILI9341_YELLOW); + tft.fillRect(0, 145, 160, 240, ILI9341_GREEN); + tft.fillRect(160, 145, 320, 240, ILI9341_CYAN); + + tft.setCursor(5, 70); + tft.setTextColor(0x0000); + tft.setTextSize(3); + tft.println("Preset"); + tft.setCursor(5, 100); + tft.print("Messages"); + + tft.setCursor(165, 65); + tft.print("Custom"); + tft.setCursor(165,100); + tft.print("Messages"); + + tft.setCursor(5,165); + tft.print("GPS"); + tft.setCursor(5,195); + tft.print("Coords"); + + tft.setCursor(165,180); + tft.print("Settings"); + + drawTime(); +} + + +void SetupEeprom() +{ + strcpy(eeprom.settings.predefinedMessages[0], "All is going well."); + strcpy(eeprom.settings.predefinedMessages[1], "Setting up camp."); + strcpy(eeprom.settings.predefinedMessages[2], "Going for a hike."); + strcpy(eeprom.settings.predefinedMessages[3], "Injured, need help."); + eeprom.settings.breadcrumbs = true; + eeprom.settings.breadInterval = 10; + eeprom.settings.militaryTime = false; + strcpy(eeprom.settings.recipient, "+61432123456"); + + strcpy(eeprom.outbox.recipient,"+61400000000"); + eeprom.outbox.coords.dateTime.year = 2000; + eeprom.outbox.coords.dateTime.month = 1; + eeprom.outbox.coords.dateTime.day = 1; + eeprom.outbox.coords.dateTime.hour = 12; + eeprom.outbox.coords.dateTime.minute = 0; + eeprom.outbox.coords.dateTime.second = 0; + //TODO GPS COORDS + strcpy(eeprom.outbox.content,"No Messages"); + + EEPROM.put(0,eeprom); +} + + + +void drawBearing(){ //all this code is shamelessly stolen + sensors_event_t event; + mag.getEvent(&event); + + float Pi = 3.14159; + + // Calculate the angle of the vector y,x + float heading = (atan2(event.magnetic.y,event.magnetic.x) * 180) / Pi; + + // Normalize to 0-360 + if (heading < 0) + { + heading = 360 + heading; + } + tft.println(heading); +} + +void SendMessage(int opt) +{ + Serial.print(eeprom.settings.recipient); + Serial.print("|"); + Serial.print(rtc.year()); + Serial.print("/"); + Serial.print(rtc.month()); + Serial.print("/"); + Serial.print(rtc.day()); + Serial.print("|"); + Serial.print(rtc.hour()); + Serial.print(":"); + Serial.print(rtc.minute()); + Serial.print(":"); + Serial.print(rtc.second()); + Serial.print("|"); + //TODO GPS COORDS + if(opt == 1) + { + Serial.print(eeprom.settings.predefinedMessages[0]); + strcpy(eeprom.outbox.recipient,eeprom.settings.recipient); + eeprom.outbox.coords.dateTime.year = rtc.year(); + eeprom.outbox.coords.dateTime.month = rtc.month(); + eeprom.outbox.coords.dateTime.day = rtc.day(); + eeprom.outbox.coords.dateTime.hour = rtc.hour(); + eeprom.outbox.coords.dateTime.minute = rtc.minute(); + eeprom.outbox.coords.dateTime.second = rtc.second(); + //TODO GPS COORDS + strcpy(eeprom.outbox.content,eeprom.settings.predefinedMessages[0]); + } else if(opt == 2) + { + Serial.print(eeprom.settings.predefinedMessages[1]); + strcpy(eeprom.outbox.recipient,eeprom.settings.recipient); + eeprom.outbox.coords.dateTime.year = rtc.year(); + eeprom.outbox.coords.dateTime.month = rtc.month(); + eeprom.outbox.coords.dateTime.day = rtc.day(); + eeprom.outbox.coords.dateTime.hour = rtc.hour(); + eeprom.outbox.coords.dateTime.minute = rtc.minute(); + eeprom.outbox.coords.dateTime.second = rtc.second(); + //TODO GPS COORDS + strcpy(eeprom.outbox.content,eeprom.settings.predefinedMessages[1]); + } else if(opt == 3) + { + Serial.print(eeprom.settings.predefinedMessages[2]); + strcpy(eeprom.outbox.recipient,eeprom.settings.recipient); + eeprom.outbox.coords.dateTime.year = rtc.year(); + eeprom.outbox.coords.dateTime.month = rtc.month(); + eeprom.outbox.coords.dateTime.day = rtc.day(); + eeprom.outbox.coords.dateTime.hour = rtc.hour(); + eeprom.outbox.coords.dateTime.minute = rtc.minute(); + eeprom.outbox.coords.dateTime.second = rtc.second(); + //TODO GPS COORDS + strcpy(eeprom.outbox.content,eeprom.settings.predefinedMessages[2]); + } else if(opt == 4) + { + Serial.print(eeprom.settings.predefinedMessages[3]); + strcpy(eeprom.outbox.recipient,eeprom.settings.recipient); + eeprom.outbox.coords.dateTime.year = rtc.year(); + eeprom.outbox.coords.dateTime.month = rtc.month(); + eeprom.outbox.coords.dateTime.day = rtc.day(); + eeprom.outbox.coords.dateTime.hour = rtc.hour(); + eeprom.outbox.coords.dateTime.minute = rtc.minute(); + eeprom.outbox.coords.dateTime.second = rtc.second(); + //TODO GPS COORDS + strcpy(eeprom.outbox.content,eeprom.settings.predefinedMessages[3]); + } + Serial.print("\n"); + tft.setCursor(60, 10); + tft.setTextColor(0x0000); + tft.setTextSize(4); + tft.print("SENT"); + Serial1.println("SMessage Sent"); + delay(1000); + tft.fillRect(60, 0, 100, 50, ILI9341_WHITE); +} + +void drawBack() +{ + tft.fillRect(0, 0, 50, 50, ILI9341_BLACK); + tft.setCursor(3,7); + tft.setTextSize(4); + tft.setTextColor(ILI9341_WHITE); + tft.print("<"); + tft.setCursor(15,7); + tft.setTextSize(4); + tft.setTextColor(ILI9341_WHITE); + tft.print("-"); +} + +//the program forks here + +void drawTime() { + rtc.update(); + tft.fillRect(200, 0, 320, 50, ILI9341_WHITE); + tft.setCursor(200, 10); + tft.setTextColor(0x0000); + tft.setTextSize(4); + tft.print(rtc.hour()); + tft.print(":"); + if(rtc.minute() < 10) + tft.print("0"); + tft.print(rtc.minute()); +} diff --git a/satchat/ui.c b/satchat/ui.c new file mode 100644 index 0000000..075377b --- /dev/null +++ b/satchat/ui.c @@ -0,0 +1,433 @@ +static void process_gui(void) +{ + switch(deviceState.menuContext.menu) + { + case MENU_NONE: + process_menu_none(); + break; + case MENU_MAIN: + process_menu_main(); + break; + case MENU_SETTINGS: + process_menu_settings(); + break; + case MENU_MESSAGES: + process_menu_messages(); + break; + case MENU_COORDINATES: + process_menu_coordinates(); + break; + } +} + +static bool process_touch_buttons(const enum menu menu) +{ + TSPoint p = ts.getPoint(); + struct touch_point tp; + tp.x = map(p.y, TS_MAXY, TS_MINY, 0, tft.width());//THIS LINE AND THE NEXT ARE RATHER CONFUSING. + tp.y = map(p.x, TS_MINX, TS_MAXX, 0, tft.height()); + tp.isPressed = (p.z >= MINPRESSURE && p.z <= MAXPRESSURE); + tp.justPressed = (tp.isPressed && !(lastTouchPoint.isPressed)); + tp.justReleased = (lastTouchPoint.isPressed && !(tp.isPressed)); + + bool buttonInteraction = false; + if(!(tp.justPressed) && deviceState.settings.instantButtons) return buttonInteraction;//RETURN FALSE IF THE SCREEN WAS NOT JUST PRESSED AND INSTANT BUTTONS IS DISABLED. + + struct menu_buttons mb = menuButtons[menu]; + + if(!(deviceState.settings.instantButtons)) + { + for(uint_fast8_t i = 0; i < mb.buttonCount; ++i) + { + if(mb.buttonState[i])//IF THE BUTTON IS IN THE PRESSED STATE. + { + if(point2aabb(tp.x, tp.y, mb.button[i].x, mb.button[i].y, mb.button[i].w, mb.button[i].h)) + { + if(tp.justReleased) + { + if(mb.button[i].function) + { + (*mb.button[i].function)(mb.button[i].functionData); + } + mb.buttonState[i] = false; + buttonInteraction = true; + } + } + else + { + if(tp.justReleased) + { + mb.buttonState[i] = false; + tft.fillRect(mb.button[i].x, mb.button[i].y, mb.button[i].w, mb.button[i].h, mb.button[i].colour); + buttonInteraction = true; + } + } + break; + } + } + } + + for(uint_fast8_t i = 0; i < mb.buttonCount; ++i) + { + if(point2aabb(tp.x, tp.y, mb.button[i].x, mb.button[i].y, mb.button[i].w, mb.button[i].h)) + { + if(deviceState.settings.instantButtons) + { + if(mb.button[i].function) + { + (*mb.button[i].function)(mb.button[i].functionData); + buttonInteraction = true; + } + } + else + { + if(tp.justPressed) + { + mb.buttonState[i] = true; + tft.fillRect(mb.button[i].x, mb.button[i].y, mb.button[i].w, mb.button[i].h, mb.button[i].colourPressed); + buttonInteraction = true; + } + } + break; + } + } + + deviceState.lastTouchPoint = tp; + return buttonInteraction; +} + +inline static void draw_menu_button(const struct menu_buttons *mb, const uint_fast8_t index) +{ + uint16_t colour = (mb->buttonState[index]) ? mb->button[index].buttonColourPressed : mb->button[index].buttonColour; + tft.fillRect(mb->button[index].x, mb->button[index].y, mb->button[index].w, mb->button[index].y, colour); +} + +inline static void draw_menu_buttons(const enum menu menu) +{ + struct menu_buttons mb = menuButtons[menu]; + for(uint_fast8_t i = 0; i < mb.buttonCount; ++i) + { + draw_menu_button(&menuButtons[menu], i); + } +} + +inline static bool point2aabb(uint_fast16_t px, uint_fast16_t py, uint_fast16_t bx, uint_fast16_t by, uint_fast16_t bw, uint_fast16_t bh) +{ + return (bx < px && bx + bw > px && by < py && by + bh > py);//CHECK IF POINT IS WITHIN BOUNDING BOX. +} + +inline static void say(const char *message) +{ + Serial3.print("\nX\nS"); + Serial3.println(message); +} + +static void process_menu_main(void) +{ + if(deviceState.menuContext.justChanged) + { + deviceState.menuContext.justChanged = false; + + tft.fillScreen(0x0000); + draw_menu_buttons(MENU_MAIN); + + if(deviceState.settings.voicedMenus) + { + say("Main menu."); + } + } + process_touch_buttons(MENU_MAIN); +} + +static void process_menu_settings(void) +{ + if(deviceState.menuContext.justChanged) + { + deviceState.menuContext.justChanged = false; + + tft.fillScreen(0x0000); + draw_menu_buttons(MENU_SETTINGS); + + if(deviceState.settings.voicedMenus) + { + say("Settings menu."); + } + } + process_touch_buttons(MENU_SETTINGS); + draw_time(); +} + +static void process_menu_messages(void) +{ + if(deviceState.menuContext.justChanged) + { + deviceState.menuContext.justChanged = false; + + tft.fillScreen(0x0000); + draw_menu_buttons(MENU_MESSAGES); + + if(deviceState.settings.voicedMenus) + { + say("Messages menu."); + } + } + process_touch_buttons(MENU_MESSAGES); +} + +static void process_menu_coordinates(void) +{ + if(deviceState.menuContext.justChanged) + { + deviceState.menuContext.justChanged = false; + + tft.fillScreen(0x0000); + draw_menu_buttons(MENU_COORDINATES); + + if(deviceState.settings.voicedMenus) + { + say("Coordinates menu."); + } + } + process_touch_buttons(MENU_COORDINATES); +} + +static void process_menu_keyboard(void) +{ + if(deviceState.menuContext.justChanged) + { + deviceState.menuContext.justChanged = false; + + tft.fillScreen(0x0000); + draw_menu_buttons(MENU_KEYBOARD); + draw_keyboard_legends(); + + if(deviceState.settings.voicedMenus) + { + say("Keyboard menu."); + } + } + process_touch_buttons(MENU_KEYBOARD); +} + +static void tb_null(const uintptr_t functionData) +{ + ; +} + +static void tb_set_menu(const uintptr_t functionData) +{ + deviceState.menuContext.menuLast = deviceState.menuContext.menu; + deviceState.menuContext.menu = functionData; + deviceState.menuContext.justChanged = true; +} + +static void tb_set_menu_last(const uintptr_t functionData) +{ + enum menu temp = deviceState.menuContext.menuLast; + deviceState.menuContext.menuLast = deviceState.menuContext.menu; + deviceState.menuContext.menu = temp; + deviceState.menuContext.justChanged = true; +} + +static void tb_key(const uintptr_t functionData) +{ + for(uint_fast8_t i = 0; i < MESSAGE_LENGTH; ++i) + { + if(deviceState.message.text[i] == '\0') + { + char c = (char) functionData; + if(deviceState.message.shift) + { + c = shift_key(c); + if(deviceState.message.caps) c = tolower(c); + } + else + { + if(deviceState.message.caps) c = toupper(c); + } + + deviceState.message.text[i] = c; + break; + } + } + if(deviceState.settings.voicedKeys) + { + char legend[2] = {toupper((char) functionData)}; + say(legend); + } +} + +static void tb_key_shift(const uintptr_t functionData) +{ + if(deviceState.message.shift) + { + deviceState.message.shift = false; + } + else + { + deviceState.message.shift = true; + } + if(deviceState.settings.voicedKeys) + { + say("Shift."); + } +} + +static void tb_key_caps(const uintptr_t functionData) +{ + if(deviceState.message.caps) + { + deviceState.message.caps = false; + } + else + { + deviceState.message.caps = true; + } + if(deviceState.settings.voicedKeys) + { + say("Capslock."); + } +} + +static void tb_key_enter(const uintptr_t functionData) +{ + for(uint_fast8_t i = 0; i < MESSAGE_LENGTH; ++i) + { + if(deviceState.message.text[i] == '\0') + { + deviceState.message.text[i] = '\n'; + break; + } + } + if(deviceState.settings.voicedKeys) + { + say("Enter."); + } +} + +static void tb_key_backspace(const uintptr_t functionData) +{ + for(uint_fast8_t i = 1; i < MESSAGE_LENGTH; ++i) + { + if(deviceState.message.text[i] == '\0') + { + deviceState.message.text[i - 1] = '\0'; + break; + } + } + if(deviceState.settings.voicedKeys) + { + say("Backspace."); + } +} + +static void draw_time(void) +{ + rtc.update(); + tft.fillRect(215, 0, 106, 51, ILI9341_WHITE); + tft.setCursor(215, 10); + tft.setTextColor(0x0000); + tft.setTextSize(3); + tft.print(rtc.hour()); + tft.print(":"); + if(rtc.minute() < 10) tft.print("0"); + tft.print(rtc.minute); +} + +inline static void shift_key(const char c) +{ + switch(c) + { + case '1': + c = '!'; + break; + case '2': + c = '@'; + break; + case '3': + c = '#'; + break; + case '4': + c = '$'; + break; + case '5': + c = '%'; + break; + case '6': + c = '^'; + break; + case '7': + c = '&'; + break; + case '8': + c = '*'; + break; + case '9': + c = '('; + break; + case '0': + c = ')'; + break; + case '-': + c = '_'; + break; + case '=': + c = '+'; + break; + case ',': + c = '<'; + break; + case '.': + c = '>'; + break; + case '/': + c = '?'; + break; + case ';': + c = ':'; + break; + case '`': + c = '~'; + break; + case '[': + c = '{'; + break; + case ']': + c = '}'; + break; + case '\'': + c = '\"'; + break; + default: + c = toupper(c); + break; + } +} + +static void draw_keyboard_legends(void) +{ + tft.setTextSize(3); + tft.setTextColor(0x0000); + + struct menu_buttons *mb = menuButtons[MENU_KEYBOARD]; + + for(uint_fast8_t i = 0; i < mb->buttonCount; ++i) + { + if(mb->button[i].function == tb_key) + { + char legend[2] = {mb->button[i].functionData}; + if(deviceState.message.shift) + { + legend[0] = shift_key(legend[0]); + if(deviceState.message.caps) legend[0] = tolower(legend[0]); + } + else + { + if(deviceState.message.caps) legend[0] = toupper(legend[0]); + } + + tft.setCursor(mb->button[i].x + 0000, mb->button[i].y + 0000);//CHANGE OFFSETS TO ALIGN LEGEND. + tft.print(legend); + } + } +} \ No newline at end of file diff --git a/satchat/ui.h b/satchat/ui.h new file mode 100644 index 0000000..540ccbc --- /dev/null +++ b/satchat/ui.h @@ -0,0 +1,20 @@ +static void process_gui(void); +static bool process_touch_buttons(const enum menu menu); +inline static void draw_menu_button(const struct menu_buttons *mb, const uint_fast8_t index); +static void draw_menu_buttons(const enum menu menu); +inline static bool point2aabb(uint_fast16_t px, uint_fast16_t py, uint_fast16_t bx, uint_fast16_t by, uint_fast16_t bw, uint_fast16_t bh); +inline static void say(const char *message); +static void process_menu_main(void); +static void process_menu_settings(void); +static void process_menu_messages(void); +static void process_menu_coordinates(void); +static touch_button_function tb_null; +static touch_button_function tb_set_menu; +static touch_button_function tb_set_menu_last; +static touch_button_function tb_key; +static touch_button_function tb_key_shift; +static touch_button_function tb_key_caps; +static touch_button_function tb_key_enter; +static touch_button_function tb_key_backspace; +static void draw_time(void); +static void draw_keyboard_legends(void); \ No newline at end of file diff --git a/src/atmega2560.c b/src/atmega2560.c deleted file mode 100644 index 5daaf8d..0000000 --- a/src/atmega2560.c +++ /dev/null @@ -1,13 +0,0 @@ -int main(void) -{ - setup(); - while(1) - { - ; - } -} - -static void setup(void) -{ - ; -} diff --git a/src/atmega2560.h b/src/atmega2560.h deleted file mode 100644 index 6d3874f..0000000 --- a/src/atmega2560.h +++ /dev/null @@ -1,2 +0,0 @@ -int main(void); -static void setup(void); diff --git a/src/dec.h b/src/dec.h deleted file mode 100644 index 0fc8169..0000000 --- a/src/dec.h +++ /dev/null @@ -1,35 +0,0 @@ -enum clock_format {CLOCK_12_HOUR, CLOCK_24_HOUR}; -enum connection_status {DISCONNECTED, CONNECTING, CONNECTED}; - -struct timestamp -{ - time_t timestamp;//Arduino provides time_t. -}; - -struct message -{ - struct timestamp timestamp; - uint8_t length; - char message[160];//160 characters. -}; - -struct coords -{ - struct timestamp timestamp; - uint32_t coords;//How will coordinates be stored? -}; - -struct settings -{ - bool reportGPS; - struct timestamp reportInterval;//How often GPS coordinates should be reported/sent. - struct timestamp requestInterval;//How often GPS coordinates should be requested. - enum clock_format clockFormat; -}; - -struct device_state -{ - enum connection_status connectionStatus; - struct timestamp deviceTime; - uint8_t batteryLevel; -}; diff --git a/src/def.h b/src/def.h deleted file mode 100644 index 4da0709..0000000 --- a/src/def.h +++ /dev/null @@ -1,3 +0,0 @@ -#define MAX_INBOX_SIZE 6 -#define MAX_OUTBOX_SIZE 6 -#define MAX_GPS_LOG_SIZE 12 diff --git a/src/globals.c b/src/globals.c deleted file mode 100644 index 5e0c7b5..0000000 --- a/src/globals.c +++ /dev/null @@ -1,4 +0,0 @@ -static struct message currentMessage; -static struct coords currentCoords; -static struct settings settings; -static struct device_state deviceState; diff --git a/src/satchat.c b/src/satchat.c deleted file mode 100644 index c4601d6..0000000 --- a/src/satchat.c +++ /dev/null @@ -1,7 +0,0 @@ -#include -#include -#include "def.h" -#include "dec.h" -#include "globals.c" -#include "atmega2560.h" -#include "atmega2560.c"