summaryrefslogtreecommitdiff
path: root/telelogger
diff options
context:
space:
mode:
Diffstat (limited to 'telelogger')
-rw-r--r--telelogger/telelogger.ino582
-rw-r--r--telelogger/telelogger.layout24
2 files changed, 606 insertions, 0 deletions
diff --git a/telelogger/telelogger.ino b/telelogger/telelogger.ino
new file mode 100644
index 0000000..e2bf1b9
--- /dev/null
+++ b/telelogger/telelogger.ino
@@ -0,0 +1,582 @@
+/*************************************************************************
+* Arduino GPS/OBD-II/G-Force Data Logger
+* Distributed under GPL v2.0
+* Copyright (c) 2013 Stanley Huang <stanleyhuangyc@gmail.com>
+* All rights reserved.
+*************************************************************************/
+
+#include <Arduino.h>
+#include <Wire.h>
+#include <OBD.h>
+#include <SD.h>
+#include <I2Cdev.h>
+#include <MPU9150.h>
+#include "config.h"
+#if USE_SOFTSERIAL
+#include <SoftwareSerial.h>
+#endif
+#include "datalogger.h"
+
+// logger states
+#define STATE_SD_READY 0x1
+#define STATE_OBD_READY 0x2
+#define STATE_GPS_READY 0x4
+#define STATE_MEMS_READY 0x8
+#define STATE_SLEEPING 0x20
+
+static uint32_t lastFileSize = 0;
+static uint16_t fileIndex = 0;
+static uint32_t startTime = 0;
+static uint32_t lastGPSAccess = 0;
+static uint32_t lastGPSTime = 0;
+static uint32_t lastGPSTimeSent = 0;
+static uint32_t dataCount = 0;
+
+static byte pidTier1[]= {PID_RPM, PID_SPEED, PID_ENGINE_LOAD, PID_THROTTLE};
+static byte pidTier2[] = {PID_INTAKE_MAP, PID_MAF_FLOW, PID_TIMING_ADVANCE};
+static byte pidTier3[] = {PID_COOLANT_TEMP, PID_INTAKE_TEMP, PID_AMBIENT_TEMP, PID_FUEL_LEVEL};
+
+static int pidValueTier1[sizeof(pidTier1)];
+static int pidValueTier2[sizeof(pidTier2)];
+static int pidValueTier3[sizeof(pidTier3)];
+
+#if USE_MPU6050 || USE_MPU9150
+static int16_t ax = 0, ay = 0, az = 0;
+static int16_t gx = 0, gy = 0, gz = 0;
+#if USE_MPU9150
+static int16_t mx = 0, my = 0, mz = 0;
+#endif
+static int temp = 0;
+#endif
+
+static GPS_DATA gd = {0};
+
+#define TIER_NUM1 sizeof(pidTier1)
+#define TIER_NUM2 sizeof(pidTier2)
+#define TIER_NUM3 sizeof(pidTier3)
+
+byte pidValue[TIER_NUM1];
+
+#if USE_MPU6050 || USE_MPU9150
+MPU6050 accelgyro;
+static uint32_t lastMemsDataTime = 0;
+#endif
+
+#define sim Serial
+#define GSM_ON 6
+#define GSM_RESET 7
+
+char gpsline[OBD_RECV_BUF_SIZE] = {0};
+
+typedef enum {
+ HTTP_DISABLED = 0,
+ HTTP_READY,
+ HTTP_CONNECTING,
+ HTTP_READING,
+ HTTP_ERROR,
+} HTTP_STATES;
+
+class CGPRS {
+public:
+ CGPRS():httpState(HTTP_DISABLED) {}
+ bool init()
+ {
+ sim.begin(9600);
+ pinMode(GSM_ON, OUTPUT);
+ pinMode(GSM_RESET, OUTPUT);
+
+ if (sendCommand("ATE0")) {
+ digitalWrite(GSM_RESET, HIGH);
+ delay(1000);
+ // generate turn on pulse
+ digitalWrite(GSM_RESET, LOW);
+ delay(100);
+ sendCommand("ATE0");
+ return true;
+ } else {
+ for (byte n = 0; n < 5; n++) {
+ digitalWrite(GSM_ON, HIGH);
+ delay(1000);
+ // generate turn on pulse
+ digitalWrite(GSM_ON, LOW);
+ delay(3000);
+ if (sendCommand("ATE0"))
+ return true;
+ }
+ return false;
+ }
+ }
+ bool setup(const char* apn)
+ {
+ while (sendCommand("AT+CREG?", "+CREG: 0,1", "+CREG: 0,5", 2000) == 0);
+ sendCommand("AT+CSQ");
+ sendCommand("AT+CGATT?");
+
+ if (!sendCommand("AT+SAPBR=3,1,\"Contype\",\"GPRS\""))
+ return false;
+
+ sim.print("AT+SAPBR=3,1,\"APN\",\"");
+ sim.print(apn);
+ sim.println('\"');
+ if (!sendCommand(0))
+ return false;
+
+ while (!sendCommand("AT+SAPBR=1,1"));
+ return true;
+ }
+ bool getOperatorName()
+ {
+ // display operator name
+ if (sendCommand("AT+COPS?", "OK\r", "ERROR\r") == 1) {
+ char *p = strstr(response, ",\"");
+ if (p) {
+ p += 2;
+ char *s = strchr(p, '\"');
+ if (s) *s = 0;
+ strcpy(response, p);
+ return true;
+ }
+ }
+ return false;
+ }
+ void httpUninit()
+ {
+ sendCommand("AT+HTTPTERM");
+ }
+
+ bool httpInit()
+ {
+ if (!sendCommand("AT+HTTPINIT", 10000) || !sendCommand("AT+HTTPPARA=\"CID\",1", 5000)) {
+ httpState = HTTP_DISABLED;
+ return false;
+ }
+ httpState = HTTP_READY;
+ return true;
+ }
+ bool httpConnect(const char* url, const char* args = 0)
+ {
+ // Sets url
+ sim.print("AT+HTTPPARA=\"URL\",\"");
+ sim.print(url);
+ if (args) {
+ sim.print('?');
+ sim.print(args);
+ }
+
+ sim.println('\"');
+ if (sendCommand(0))
+ {
+ // Starts GET action
+ sim.println("AT+HTTPACTION=0");
+ httpState = HTTP_CONNECTING;
+ bytesRecv = 0;
+ checkTimer = millis();
+ } else {
+ httpState = HTTP_ERROR;
+ }
+ return false;
+ }
+ byte httpIsConnected()
+ {
+ byte ret = checkResponse("+HTTPACTION:0,200", "+HTTPACTION:0,6", 10000);
+ if (ret == 1) {
+ return 1;
+ } else if (ret >= 2) {
+ httpState = HTTP_ERROR;
+ }
+ return false;
+ }
+ void httpRead()
+ {
+ sim.println("AT+HTTPREAD");
+ httpState = HTTP_READING;
+ bytesRecv = 0;
+ checkTimer = millis();
+ }
+ bool httpIsRead()
+ {
+ byte ret = checkResponse("+HTTPREAD:", "Error", 10000) == 1;
+ if (ret == 1) {
+ bytesRecv = 0;
+ sendCommand(0);
+ byte n = atoi(response);
+ char *p = strchr(response, '\n');
+ if (p) memmove(response, p + 1, n);
+ response[n] = 0;
+ return 1;
+ } else if (ret >= 2) {
+ httpState = HTTP_ERROR;
+ }
+ return false;
+ }
+ char response[256];
+ byte bytesRecv;
+ byte httpState;
+ uint32_t checkTimer;
+private:
+ byte checkResponse(const char* expected1, const char* expected2 = 0, unsigned int timeout = 2000)
+ {
+ while (sim.available()) {
+ char c = sim.read();
+ if (bytesRecv >= sizeof(response) - 1) {
+ // buffer full, discard first half
+ bytesRecv = sizeof(response) / 2 - 1;
+ memcpy(response, response + sizeof(response) / 2, bytesRecv);
+ }
+ response[bytesRecv++] = c;
+ response[bytesRecv] = 0;
+ if (strstr(response, expected1)) {
+ return 1;
+ }
+ if (expected2 && strstr(response, expected2)) {
+ return 2;
+ }
+ }
+ return (millis() - checkTimer < timeout) ? 0 : 3;
+ }
+ byte sendCommand(const char* cmd, unsigned int timeout = 2000, const char* expected = 0)
+ {
+ if (cmd) {
+ while (sim.available()) sim.read();
+ sim.println(cmd);
+ }
+ uint32_t t = millis();
+ byte n = 0;
+ do {
+ if (sim.available()) {
+ char c = sim.read();
+ if (n >= sizeof(response) - 1) {
+ // buffer full, discard first half
+ n = sizeof(response) / 2 - 1;
+ memcpy(response, response + sizeof(response) / 2, n);
+ }
+ response[n++] = c;
+ response[n] = 0;
+ if (strstr(response, expected ? expected : "OK\r")) {
+ return n;
+ }
+ }
+ } while (millis() - t < timeout);
+ return 0;
+ }
+ byte sendCommand(const char* cmd, const char* expected1, const char* expected2, unsigned int timeout = 2000)
+ {
+ if (cmd) {
+ while (sim.available()) sim.read();
+ sim.println(cmd);
+ }
+ uint32_t t = millis();
+ byte n = 0;
+ do {
+ if (sim.available()) {
+ char c = sim.read();
+ if (n >= sizeof(response) - 1) {
+ // buffer full, discard first half
+ n = sizeof(response) / 2 - 1;
+ memcpy(response, response + sizeof(response) / 2, n);
+ }
+ response[n++] = c;
+ response[n] = 0;
+ if (strstr(response, expected1)) {
+ return 1;
+ }
+ if (strstr(response, expected2)) {
+ return 2;
+ }
+ }
+ } while (millis() - t < timeout);
+ return 0;
+ }
+};
+
+class COBDLogger : public COBDI2C, public CDataLogger
+{
+public:
+ COBDLogger():state(0) {}
+ void setup()
+ {
+ SerialRF.print("#GPRS...");
+ if (gprs.init()) {
+ SerialRF.println("OK");
+ } else {
+ SerialRF.println(gprs.response);
+ }
+
+ SerialRF.print("#OBD..");
+ do {
+ SerialRF.print('.');
+ } while (!init());
+ SerialRF.println("OK");
+
+ state |= STATE_OBD_READY;
+
+#if USE_MPU6050 || USE_MPU9150
+ Wire.begin();
+ accelgyro.initialize();
+ if (accelgyro.testConnection()) state |= STATE_MEMS_READY;
+#endif
+
+ SerialRF.print("#GPS...");
+ if (initGPS()) {
+ SerialRF.println("OK");
+ state |= STATE_GPS_READY;
+ } else {
+ SerialRF.println("N/A");
+ }
+ delay(3000);
+
+#if ENABLE_DATA_LOG
+ uint16_t index = openFile();
+ if (index) {
+ SerialRF.print("#SD File:");
+ SerialRF.println(index);
+ state |= STATE_SD_READY;
+ } else {
+ SerialRF.print("#No SD");
+ state &= ~STATE_SD_READY;
+ }
+#endif
+
+ SerialRF.print("#Network...");
+ if (gprs.setup("connect")) {
+ SerialRF.println("OK");
+ } else {
+ SerialRF.print(gprs.response);
+ }
+ if (gprs.getOperatorName()) {
+ SerialRF.print('#');
+ SerialRF.println(gprs.response);
+ }
+ // init HTTP
+ SerialRF.print("#HTTP...");
+ while (!gprs.httpInit()) {
+ SerialRF.print('.');
+ gprs.httpUninit();
+ delay(1000);
+ }
+ delay(3000);
+ }
+ void loop()
+ {
+ static byte index = 0;
+ static byte index2 = 0;
+ static byte index3 = 0;
+ uint32_t start = millis();
+
+ // poll OBD-II PIDs
+ pidValueTier1[index] = logOBDData(pidTier1[index]);
+ if (++index == TIER_NUM1) {
+ index = 0;
+ if (index2 == TIER_NUM2) {
+ index2 = 0;
+ pidValueTier3[index] = logOBDData(pidTier3[index3]);
+ index3 = (index3 + 1) % TIER_NUM3;
+ } else {
+ pidValueTier2[index2] = logOBDData(pidTier2[index2]);
+ index2++;
+ }
+ }
+
+ if (state & STATE_GPS_READY) {
+ if (millis() - lastGPSAccess > GPS_DATA_INTERVAL) {
+ logGPSData();
+ lastGPSAccess = millis();
+ }
+ }
+
+#if ENABLE_DATA_LOG
+ // flush SD data every 1KB
+ if (dataSize - lastFileSize >= 1024) {
+ flushFile();
+ lastFileSize = dataSize;
+ // display logged data size
+ SerialRF.print("#Log Size:");
+ SerialRF.println(dataSize);
+ }
+#endif
+
+ if (errors >= 2) {
+ reconnect();
+ }
+
+#ifdef OBD_MIN_INTERVAL
+ while (millis() - start < OBD_MIN_INTERVAL) {
+ dataIdleLoop();
+ }
+#endif
+ }
+private:
+ void dataIdleLoop()
+ {
+ switch (gprs.httpState) {
+ case HTTP_READY:
+ {
+ // generate URL
+ char *p = gprs.response;
+ p += sprintf(p, "C=%lu&", ++dataCount);
+ for (byte n = 0; n < sizeof(pidTier1); n++) {
+ p += sprintf(p, "%x=%d&", pidTier1[n], pidValueTier1[n]);
+ }
+ p += sprintf(p, "A=%d,%d,%d&G=%d,%d,%d&M=%d,%d,%d", ax, ay, az, gx, gy, gz, mx, my, mz);
+ //p += sprintf(p, "&T=%d", temp);
+ if (gd.time && gd.time != lastGPSTimeSent) {
+ p += sprintf(p, "&GPS=%lu,%ld,%ld,%d,%d,%d", gd.time, gd.lat, gd.lon, gd.alt / 100, (int)gd.speed, gd.sat);
+ lastGPSTimeSent = gd.time;
+ }
+
+ #if 0
+ char *q = strchr(gpsline, ',');
+ if (q) {
+ char *s = strchr(q, '\r');
+ if (s) *s = 0;
+ strcat(p, q + 1);
+ }
+ #endif
+ gprs.httpConnect("http://home.mediacoder.net.au:8000/tick", gprs.response);
+ }
+ break;
+ case HTTP_CONNECTING:
+ if (gprs.httpIsConnected()) {
+ gprs.httpRead();
+ }
+ break;
+ case HTTP_READING:
+ if (gprs.httpIsRead()) {
+ SerialRF.print("#HTTP:");
+ SerialRF.println(gprs.response);
+ // ready for next connection
+ gprs.httpState = HTTP_READY;
+ }
+ break;
+ case HTTP_ERROR:
+ SerialRF.println("#HTTP ERROR");
+ // re-initialize HTTP
+ /*
+ gprs.httpUninit();
+ if (gprs.httpInit())
+ gprs.httpState = HTTP_READY;
+ */
+ gprs.httpState = HTTP_READY;
+ break;
+ }
+#if USE_MPU6050 || USE_MPU9150
+ if (state & STATE_MEMS_READY) {
+ processMEMS();
+ }
+#endif
+ }
+#if USE_MPU6050 || USE_MPU9150
+ void processMEMS()
+ {
+ if (dataTime - lastMemsDataTime < ACC_DATA_INTERVAL) {
+ return;
+ }
+
+ #if USE_MPU9150
+ accelgyro.getMotion9(&ax, &ay, &az, &gx, &gy, &gz, &mx, &my, &mz);
+ #else
+ accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
+ #endif
+
+ dataTime = millis();
+
+ temp = accelgyro.getTemperature();
+
+ ax /= ACC_DATA_RATIO;
+ ay /= ACC_DATA_RATIO;
+ az /= ACC_DATA_RATIO;
+ gx /= GYRO_DATA_RATIO;
+ gy /= GYRO_DATA_RATIO;
+ gz /= GYRO_DATA_RATIO;
+ #if USE_MPU9150
+ mx /= COMPASS_DATA_RATIO;
+ my /= COMPASS_DATA_RATIO;
+ mz /= COMPASS_DATA_RATIO;
+ #endif
+
+ // log x/y/z of accelerometer
+ logData(PID_ACC, ax, ay, az);
+ // log x/y/z of gyro meter
+ logData(PID_GYRO, gx, gy, gz);
+ #if USE_MPU9150
+ // log x/y/z of compass
+ logData(PID_COMPASS, mx, my, mz);
+ #endif
+ logData(PID_MEMS_TEMP, temp);
+
+ lastMemsDataTime = dataTime;
+ }
+#endif
+ int logOBDData(byte pid)
+ {
+ int value = 0;
+ // send a query to OBD adapter for specified OBD-II pid
+ if (read(pid, value)) {
+ dataTime = millis();
+ // log data to SD card
+ logData(0x100 | pid, value);
+ }
+ return value;
+ }
+ bool logGPSData()
+ {
+#if 0
+ char *p;
+ write("ATGPS\r");
+ if (receive(gpsline) == 0 || !(p = strstr(gpsline, "$GPS")))
+ return false;
+ //SerialRF.println(buf);
+ return true;
+#endif
+ if (getGPSData(&gd) && gd.lat && gd.lon && gd.time != lastGPSTime) {
+ logData(PID_GPS_TIME, gd.time);
+ logData(PID_GPS_ALTITUDE, gd.lat);
+ logData(PID_GPS_LONGITUDE, gd.lon);
+ logData(PID_GPS_ALTITUDE, gd.alt);
+ lastGPSTime = gd.time;
+ return true;
+ }
+ return false;
+ }
+ void reconnect()
+ {
+#if ENABLE_DATA_LOG
+ closeFile();
+#endif
+ SerialRF.println("#Sleeping");
+ startTime = millis();
+ state &= ~STATE_OBD_READY;
+ state |= STATE_SLEEPING;
+ //digitalWrite(SD_CS_PIN, LOW);
+ for (uint16_t i = 0; ; i++) {
+ if (init()) {
+ int value;
+ if (read(PID_RPM, value) && value > 0)
+ break;
+ }
+ }
+ SerialRF.println("#Resuming");
+ state &= ~STATE_SLEEPING;
+ fileIndex++;
+ recover();
+ setup();
+ }
+ byte state;
+ CGPRS gprs;
+};
+
+static COBDLogger logger;
+
+
+void setup()
+{
+ logger.begin();
+ logger.initSender();
+
+ logger.setup();
+}
+
+void loop()
+{
+ logger.loop();
+}
diff --git a/telelogger/telelogger.layout b/telelogger/telelogger.layout
new file mode 100644
index 0000000..c592465
--- /dev/null
+++ b/telelogger/telelogger.layout
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<CodeBlocks_layout_file>
+ <ActiveTarget name="Arduino Uno" />
+ <File name="config.h" open="1" top="0" tabpos="2" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
+ <Cursor>
+ <Cursor1 position="522" topLine="0" />
+ </Cursor>
+ </File>
+ <File name="datalogger.h" open="1" top="0" tabpos="4" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
+ <Cursor>
+ <Cursor1 position="5326" topLine="176" />
+ </Cursor>
+ </File>
+ <File name="images.h" open="1" top="0" tabpos="3" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
+ <Cursor>
+ <Cursor1 position="0" topLine="0" />
+ </Cursor>
+ </File>
+ <File name="telelogger.ino" open="1" top="1" tabpos="1" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
+ <Cursor>
+ <Cursor1 position="11941" topLine="440" />
+ </Cursor>
+ </File>
+</CodeBlocks_layout_file>