Skip to content

Instantly share code, notes, and snippets.

@asafmatan
Created March 30, 2023 22:59
Show Gist options
  • Save asafmatan/2565fc9739c097537fb715d66170fe27 to your computer and use it in GitHub Desktop.
Save asafmatan/2565fc9739c097537fb715d66170fe27 to your computer and use it in GitHub Desktop.
Charity Sower
// Written by Asaf Matan
// GPS code is based on
// https://www.hackster.io/SeeedStudio/wio-terminal-gps-ad70e2 Arduino/Wappsto
// example
#include <Adafruit_VCNL4040.h>
#include <SPI.h>
#include "TFT_eSPI.h"
#include <Seeed_FS.h>
#include <SoftwareSerial.h>
#include <TinyGPS++.h>
#include <WiFiClientSecure.h>
#include <WiFiMulti.h>
#include "Free_Fonts.h"
#include "RawImage.h"
#include "SD/Seeed_SD.h"
#include "Wappsto.h"
#include "WappstoValueTypes.h"
#include "wappsto_config.h"
#include "my_secrets.h"
WiFiMulti wifiMulti;
WiFiClientSecure client;
Wappsto wappsto(&client);
#define BUZZER_PIN WIO_BUZZER
Network *myNetwork;
Device *myDevice;
Value *myLatitudeValue;
Value *myLongitudeValue;
DeviceDescription_t myDeviceDescription = {
.name = "Location Device1",
.product = "",
.manufacturer = "",
.description =
"Example sending location shown on map in the Wappsto dashboard",
.version = "1.0",
.serial = "00001",
.protocol = "Json-RPC",
.communication = "WiFi",
};
double myLatitude = MY_LAT;
double myLongitude = MY_LON;
void updateLocation(double valLatitude, double valLongitude) {
myLatitudeValue->report(valLatitude);
myLongitudeValue->report(valLongitude);
}
void refreshLocationCallback(Value *value) {
// Nothing to read from wappsto no nothing to refresh
}
bool skip_wifi = false;
const char *ssid = MY_SSID;
const char *password = MY_PASS;
Adafruit_VCNL4040 vcnl4040 = Adafruit_VCNL4040();
SoftwareSerial mySerial(0, 1); // RX, TX
TinyGPSPlus gps;
TFT_eSPI tft;
TinyGPSCustom ExtLat(gps, "GPGGA", 3); // N for Latitude
TinyGPSCustom ExtLng(gps, "GPGGA", 5); // E for Longitude
const float pi = 3.1415;
int menu = 0, p_menu = 3;
int logging = 0, sat_n = 0;
int coin_counter = 0;
int wappsto_transmited_lines = 0;
String F_name = "db.txt";
String p_hour, p_lat, p_lng, p_alt, p_sat, p_date;
String p_coin_counter;
// for Satellites position
static const int MAX_SATELLITES = 40;
TinyGPSCustom totalGPGSVMessages(gps, "GPGSV",
1); // $GPGSV sentence, first element
TinyGPSCustom messageNumber(gps, "GPGSV",
2); // $GPGSV sentence, second element
TinyGPSCustom satsInView(gps, "GPGSV", 3); // $GPGSV sentence, third element
TinyGPSCustom satNumber[4]; // to be initialized later
TinyGPSCustom elevation[4];
TinyGPSCustom azimuth[4];
TinyGPSCustom snr[4];
struct {
bool active;
int elevation;
int azimuth;
int snr;
int dsp;
} sats[MAX_SATELLITES];
String N_date, hour0;
bool sd;
String lat0, lng0;
void disp_title() {
if (menu == 0) {
tft.setFreeFont(FF0);
tft.fillScreen(TFT_BLACK);
tft.setTextSize(3);
tft.drawString("GPS", 120, 3);
tft.setTextSize(2);
tft.drawString("Date", 30, 42);
tft.drawString("Time", 30, 74);
tft.drawString("LAT", 30, 106);
tft.drawString("LONG", 30, 138);
tft.drawString("ALT", 30, 170);
tft.drawString("Satellites", 30, 202);
if (sd != true) {
tft.drawChar(295, 223, 'S', TFT_WHITE, TFT_RED, 2);
tft.drawChar(307, 223, 'D', TFT_WHITE, TFT_RED, 2);
}
p_hour = " ";
p_lat = " ";
p_lng = " ";
p_alt = " ";
p_sat = " ";
p_date = " ";
} else if (menu == 1) {
tft.setFreeFont(FF0);
tft.fillScreen(TFT_BLACK);
tft.setTextSize(3);
tft.drawString("Stats", 120, 3);
tft.setTextSize(2);
tft.drawString("Coins", 30, 42);
p_coin_counter = " ";
}
}
String doubleToString(double value, int precision) {
char buffer[20];
dtostrf(value, 0, precision, buffer);
return String(buffer);
}
void displayInfo() {
if (gps.location.isValid()) {
lat0 = doubleToString(gps.location.lat(), 6);
lng0 = doubleToString(gps.location.lng(), 6);
} else {
lat0 = "0";
lng0 = "0";
}
if (gps.date.isValid()) {
N_date = String(gps.date.day()) + "/" + String(gps.date.month()) + "/" +
String(gps.date.year());
} else {
N_date = "00/00/0000";
}
if (gps.time.isValid()) {
hour0 = "";
if (gps.time.hour() < 10) hour0 = "0";
hour0 += gps.time.hour();
hour0 += ":";
if (gps.time.minute() < 10) hour0 += "0";
hour0 += gps.time.minute();
hour0 += ":";
if (gps.time.second() < 10) hour0 += "0";
hour0 += gps.time.second();
} else {
hour0 = "00:00:00";
}
}
void displayInfoData(void) {
if (menu == 0) { // menu 0
if (menu != p_menu) {
disp_title();
p_menu = menu;
}
if (N_date != p_date) { // Date
tft.fillRect(100, 42, 120, 16, TFT_BLACK);
tft.drawString(N_date, 100, 42);
p_date = N_date;
}
if (hour0 != p_hour) { // Time
tft.fillRect(100, 74, 120, 16, TFT_BLACK);
tft.drawString(hour0, 100, 74);
p_hour = hour0;
}
if (lat0 != p_lat) { // Latitude
tft.fillRect(100, 106, 132, 16, TFT_BLACK);
tft.drawString(lat0, 100, 106);
p_lat = lat0;
}
if (lng0 != p_lng) { // Longitude
tft.fillRect(100, 138, 156, 16, TFT_BLACK);
tft.drawString(lng0, 100, 138);
p_lng = lng0;
}
if (String(gps.altitude.meters()) != p_alt) { // Altimeter
tft.fillRect(100, 170, 60, 16, TFT_BLACK);
tft.drawString(String(gps.altitude.meters()), 100, 170);
p_alt = String(gps.altitude.meters());
}
if (String(gps.satellites.value()) != p_sat) { // N of Satellites
tft.fillRect(160, 202, 32, 16, TFT_BLACK);
tft.drawString(String(gps.satellites.value()), 160, 202);
p_sat = String(gps.satellites.value());
}
} // end of menu=0
else if (menu == 1) {
if (menu != p_menu) {
disp_title();
p_menu = menu;
}
if (String(coin_counter) != p_coin_counter) { // Coin Counter
tft.fillRect(100, 42, 120, 16, TFT_BLACK);
tft.drawString(String(coin_counter), 100, 42);
p_coin_counter = String(coin_counter);
}
}
}
void playTone(int tone, int duration) {
for (long i = 0; i < duration * 1000L; i += tone * 2) {
digitalWrite(BUZZER_PIN, HIGH);
delayMicroseconds(tone);
digitalWrite(BUZZER_PIN, LOW);
delayMicroseconds(tone);
}
}
void writeIntToSD(const char *filename, int value) {
File file = SD.open(filename, FILE_WRITE);
if (file) {
file.print(value);
file.close();
} else {
Serial.println("Error writing to file");
}
}
int readIntFromSD(const char *filename) {
File file = SD.open(filename, FILE_READ);
int value = 0;
if (file) {
value = file.parseInt();
file.close();
} else {
Serial.println("Error reading from file");
}
return value;
}
String *parseCSV(String data, int &fieldCount) {
String *fields =
new String[data.length()]; // Allocate memory for the maximum
// expected number of fields
fieldCount = 0;
// Split the data string into fields
int lastIndex = 0;
int commaIndex = data.indexOf(',');
while (commaIndex != -1) {
fields[fieldCount] = data.substring(lastIndex, commaIndex);
fieldCount++;
lastIndex = commaIndex + 1;
commaIndex = data.indexOf(',', lastIndex);
}
fields[fieldCount] = data.substring(lastIndex);
fieldCount++;
return fields;
}
void initializeWifi(void) {
Serial.println("Initializing WiFi");
wifiMulti.addAP(ssid, password);
while (wifiMulti.run() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void proximity_handler(void) {
int proximity_value;
proximity_value = vcnl4040.getProximity();
if (proximity_value > 50) {
if (sd == true) {
Serial.println(F_name);
File myFile = SD.open(F_name, FILE_APPEND);
if (myFile) {
myFile.print(N_date);
myFile.print(",");
myFile.print(hour0);
myFile.print(",");
myFile.print(lat0);
myFile.print(",");
myFile.print(lng0);
myFile.print(",");
myFile.println(gps.altitude.meters());
myFile.close();
}
}
Serial.println("Proximity Value:");
Serial.println(proximity_value);
coin_counter += 1;
// Store Coin Counter to SD
writeIntToSD("coin_counter.txt", coin_counter);
delay(1500);
}
}
void wappsto_transmit(void) {
if (digitalRead(WIO_KEY_B) == LOW) {
int line_counter = 0;
// Log to SD card
if (sd == true) {
File myFile = SD.open(F_name, FILE_READ);
if (myFile) {
while (myFile.available()) {
int fieldCount;
String line = myFile.readStringUntil('\n');
if (line.length() > 0) {
Serial.println(line);
String *fields = parseCSV(line, fieldCount);
// Print the fields
for (int i = 0; i < fieldCount; i++) {
Serial.print("Field ");
Serial.print(i);
Serial.print(": ");
Serial.println(fields[i]);
if (i == 2) {
myLatitude = fields[i].toDouble();
} else if (i == 3) {
myLongitude = fields[i].toDouble();
}
}
delete[] fields; // Deallocate the memory used for the fields array
if (line_counter >= wappsto_transmited_lines) {
if (myLatitude > 0) {
Serial.print("Transmited line: ");
Serial.println(line_counter);
updateLocation(myLatitude, myLongitude);
wappsto.dataAvailable();
}
wappsto_transmited_lines++;
}
line_counter++;
}
}
myFile.close();
Serial.print("Updating transmitted lines to: ");
Serial.println(wappsto_transmited_lines);
writeIntToSD("wappsto_transmitted_lines.txt", wappsto_transmited_lines);
} else {
Serial.println("Failed to open file");
}
while (digitalRead(WIO_KEY_B) == LOW) {
}
}
}
}
void setup() {
Serial.begin(57600);
pinMode(WIO_KEY_A, INPUT_PULLUP);
pinMode(WIO_KEY_B, INPUT_PULLUP);
pinMode(WIO_KEY_C, INPUT_PULLUP);
pinMode(WIO_5S_PRESS, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);
if (digitalRead(WIO_5S_PRESS) == HIGH) {
mySerial.begin(9600);
}
// Let things stablize (better than !Serial for this case)
delay(2000);
tft.begin();
tft.setRotation(3);
Serial.print("Initializing SD card...");
if (!SD.begin(SDCARD_SS_PIN, SDCARD_SPI)) {
Serial.println("initialization failed!");
sd = false;
tft.drawChar(295, 223, 'S', TFT_WHITE, TFT_RED, 2);
tft.drawChar(307, 223, 'D', TFT_WHITE, TFT_RED, 2);
} else {
Serial.println("initialization done.");
sd = true;
}
// Display splash screen
tft.setFreeFont(&FreeSerifBoldItalic12pt7b);
tft.fillScreen(TFT_WHITE);
tft.setTextColor(TFT_RED);
tft.drawString("Charity Sower", 90, 10);
drawImage<uint16_t>("coin.bmp", 100, 50);
delay(5000);
tft.fillScreen(TFT_BLACK); // Black background
tft.setTextColor(TFT_YELLOW);
// read the stored integer from the SD card
coin_counter = readIntFromSD("coin_counter.txt");
// for satellites position
// Initialize all the uninitialized TinyGPSCustom objects
for (int i = 0; i < 4; ++i) {
satNumber[i].begin(gps, "GPGSV", 4 + 4 * i); // offsets 4, 8, 12, 16
elevation[i].begin(gps, "GPGSV", 5 + 4 * i); // offsets 5, 9, 13, 17
azimuth[i].begin(gps, "GPGSV", 6 + 4 * i); // offsets 6, 10, 14, 18
snr[i].begin(gps, "GPGSV", 7 + 4 * i); // offsets 7, 11, 15, 19
}
if (!vcnl4040.begin()) {
Serial.println("Couldn't find VCNL4040 chip");
while (1)
;
}
Serial.println("Found VCNL4040 chip");
if (digitalRead(WIO_5S_PRESS) == HIGH) {
Serial.println("Skip Wifi Setup");
skip_wifi = true;
} else {
initializeWifi();
initializeNtp();
wappsto.config(network_uuid, ca, client_crt, client_key, 5, NO_LOGS);
if (wappsto.connect()) {
Serial.println("Connected to Wappsto");
} else {
Serial.println("Could not connect");
}
Serial.println("Create network");
myNetwork = wappsto.createNetwork("Location Example");
Serial.println("Create Device");
myDevice = myNetwork->createDevice(&myDeviceDescription);
Serial.println("Create Default val");
myLatitudeValue = myDevice->createNumberValue(&defaultLatitudeParameter);
myLongitudeValue = myDevice->createNumberValue(&defaultLongitudeParameter);
Serial.println("Register callbacks");
myLatitudeValue->onRefresh(&refreshLocationCallback);
myLongitudeValue->onRefresh(&refreshLocationCallback);
Serial.println("read last entry transmitted to wappsto");
wappsto_transmited_lines = readIntFromSD("wappsto_transmitted_lines.txt");
Serial.print("Last transmitted line:");
Serial.println(wappsto_transmited_lines);
}
}
void loop() {
if (skip_wifi == false) {
Serial.println('.');
wappsto.dataAvailable();
delay(500);
}
while (mySerial.available() > 0) {
char c = mySerial.read();
// Serial.print(c);
gps.encode(c);
}
displayInfo();
displayInfoData();
proximity_handler();
// Screen toggle
if (digitalRead(WIO_KEY_C) == LOW) { // Page(menu) change
menu++;
if (menu > 1) menu = 0;
while (digitalRead(WIO_KEY_C) == LOW) {
}
}
if (coin_counter > 10) {
playTone(500, 1000);
coin_counter = 0;
}
// Handle transmitting of stored coordinates to wappstop incase we are in wifi
// on mode.
wappsto_transmit();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment