BlueIno
// Sample application that communicates with Heximus Android app
//
Author. C. Hannukainen 2019,2020,2021,2022
// this sketch uses a
BMP180, a RTC and an analog to digital converter.
// We are measuring
voltage,temperature, and humidity.
// The RTC returns the number of
seconds since 1970 which is written to each record.
// define the
schema as such, use the exact spelling
// FLOAT = 4 bytes
//
DOUBLE = 8 bytes // only 8 bytes on DUO
// INTEGER = 2 bytes
// LONG =
4 bytes
// CHANGE these two lines to create your database schema that
matchs your device and sensors.
#define SCHEMASIZE 8
String
schema[SCHEMASIZE] = {"SECS1970","LONG",
"Voltage","FLOAT","Temperature","FLOAT","Pressure","FLOAT"};
// this
structure holds what you are interested in storing and must match the schema
line above in the EXACT SAME order
struct Pack // User modify this Pack
Structure to suit your data
{
unsigned long secs1970; // 4
float
voltage; // 4
float temperature; // 4
float pressure; // 4
};
unsigned long interval = 1000ul * 10ul; // polling interval
(milliseconds) USE ul after the number !!! Otherwise it will be 16 bit and
overflow
// initially set low to 10 seconds for initial testing
long
utcoffset = 0;
// circular buffer code
//#define CBSIZE 128 //
number of records before buffer wraps // uses 112%, 2296 bytes, using this
size will crash the nano, but would work on a MEGA since it has more RAM.
#define CBSIZE 72 // number of records before buffer wraps // uses 65%, 1336
bytes NO warnings // Multiply CBSIZE by Pack Structure Size to get idea of
memory footprint
// using CBSIZE 72 with an interval of 1 hour , we can
store 3 days worth of data.
// Circular buffer object DO NOT
MODIFY this structure until you fully understand what it is doing and how it
works
typedef struct
{
int size; /* maximum number of elements */
int start; /* index of oldest element */
int end; /* index at which to
write new element */
struct Pack pack[CBSIZE+1];
} CircularBuffer;
// incoming packets will look like this <<<<<<<< -----------------
INCOMING PACKET
// magic 2 bytes
// len 2
// numrecords 2
//
data n bytes
// crc 2 crc calculate from magic till end of data, not
including crc
// The trick to using a circular buffer and not
losing data is to extract the data before the circular buffer wraps.
//
This is a function of interval time and the size of the buffer.
//
Example. if your buffer size CBSIZE is 100, and your interval is 1 hour,
interval = 1000 * 3600, you can go 100 hours without losing data.
// So
if you come by with your cell phone before 100 hours has passed since your
last visit, you will not lose records.
// Unfortunately you cannot set
CBSIZE to a very large value since there is extremely limited memory on the
Arduino. See above CBSIZE comments
// The size of your Pack structure and
CBSIZE dictates how much memory is required
// Unfortunately, there is no
method to get the upper limit nor get a usefull error message. The Arduino
just goes off into the weeds and locks up.
// So start off with small
values to get it working, and then with experimetation you can increase the
buffer size.
// When you compile in Arduino IDE, you will get messages if
you are getting low on memory. Try to stay out of that zone were you are
getting warning messages.
// And then back off a quarter turn. (:
#include <Wire.h>
// include the liquid library code:
#include
<LiquidCrystal_I2C.h>
#include "DHT.h"
#include <EEPROM.h>
#include
"RTClib.h"
#include <SFE_BMP180.h> // needed for bmp180 pressure
#include <Adafruit_ADS1015.h> // // using the analog to digital converter
unsigned long previousMillis = 0;
unsigned long ivalue32 = 0;
// The lcd is optional but is highly usefull for debugging your sketch.
I use the 4 line version that uses SDL/SDA
// using SDL/SDA devices
simplifies device hook up, that is, less wires.
// the liquid crystal can
be a little tricky setting up. We are using the 4 line version in this
sketch.
//LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
// Set the LCD I2C address, if it's not working try 0x27
//LiquidCrystal_I2C lcd(0x3f, 16,4); // try 0x27
LiquidCrystal_I2C
lcd(0x3f,16,4); // try 0x27
//LiquidCrystal_I2C lcd(0x27, 16,2); // try
0x27
SFE_BMP180 pressure; // using a BMP180 pressure and
temperature sensor
float H1 = 0;
float TH = 0;
float P1 = 0;
float T1 = -273.0;
int P1old = 0;
// This is also a optional
device. So far it has been very reliable
// we are using an analog to
digital converter in this sketch
//ADS1115 adc0(ADS1115_DEFAULT_ADDRESS);
// 48, tie address pin to gnd
Adafruit_ADS1115 ads; /* Use this for the
16-bit version */
char data = 0;
byte jack = 0;
int i = 0;
int j = 0;
int s = 0;
int sentbytes = 0;
char abc[64];
int
adsize = 0;
int presize = 0;
int savecbstart = 0;
unsigned char
btout[64];
int magic = 0;
int len = 0;
int numrecs = 0;
int
func,pin,ivalue16;
#define SLAVE_ADDRESS 0x04
byte ACK = 0x06;
byte
NAK = 0x15;
int serialmode = 0; // when set to 1, sending raw bytes, no
packets, no interpretation , usefull for testing and seting up bluetooth
devices
unsigned char serialbyte;
unsigned long secs1970 = 0;
int
icc = 0;
int hasrtc; // 0 no rtc, 1 has rtc
int year = 2021;
int month = 10;
int day = 29;
int hour = 0;
int minute = 0;
int
second = 0;
union jack
{
byte b[4];
unsigned long l;
};
typedef struct
{
int magic; /* magic Hex1 2 bytes watch
out, int is 4 bytes on Android */
int len; /* length of data 2 bytes
watch out int is 4 bytes on Android */
int numrecords; /* number of
records 2 bytes Total is 8 bytes for Preamble structure */
} Preamble;
// followed by len bytes of data
// followed by crc
Preamble
pream;
union jack jacker;
DateTime now;
RTC_DS1307 rtc;
// Checking and using CRC "Cyclic Redundancy Check" may seem
overkill, but it can happen, interference, out of range, this all happens.
// We are using 16 bit CRC, 16 bit is still efficent and will catch most
errors.
// The Heximus application uses this same CRC code. Changing
anything on this side pertaining to CRC will break the protocol with
//
Heximus and this application will not communicate properly.
// We
calcualate CRC as a running total to save memory. That is why CRC is always
the last field in the structure
uint16_t crc16 = 0xFFFF;
uint16_t
crcpack = 0;
//uint16_t crc16 (const uint8_t * buffer, uint32_t size)
void CRC16 (byte *buffer, int size)
{
// uint16_t crc16 = 0xFFFF; //
we init outside since we want a running total
if (buffer && size)
while (size--)
{
crc16 = (crc16 >> 8) | (crc16 << 8);
crc16 ^=
*buffer++;
crc16 ^= ((unsigned char) crc16) >> 4;
crc16 ^= crc16 <<
12;
crc16 ^= (crc16 & 0xFF) << 5;
}
// return crc16;
}
// Start circular buffer code
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
CircularBuffer cb;
struct Pack adelem;
void cbInit(int size)
{
cb.size = size +
1; // include empty elem
cb.start = 0;
cb.end = 0;
}
int
cbIsFull()
{
return (cb.end + 1) % cb.size == cb.start;
}
int cbSize()
{
int size = (cb.size - cb.start + cb.end) % cb.size;
return size;
}
int cbIsEmpty()
{
return cb.end == cb.start;
}
// Write an element, overwriting oldest element if buffer is full.
App can choose to avoid the overwrite by checking cbIsFull().
void
cbWrite(struct Pack *pack)
{
cb.pack[cb.end] = *pack;
cb.end =
(cb.end + 1) % cb.size;
if (cb.end == cb.start)
cb.start = (cb.start +
1) % cb.size; // full, overwrite
}
// Read oldest element. App
must ensure !cbIsEmpty() first.
void cbRead(struct Pack *pack)
{
*pack = cb.pack[cb.start];
cb.start = (cb.start + 1) % cb.size;
}
// End circular buffer code
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// this
routine simply prints the time to the lcd display, can be removed.
void
showtime()
{
if (hasrtc == 0)
return;
now = rtc.now(); //
Grab the time from the RTC
lcd.setCursor(0, 2);
lcd.print(now.year(), DEC);
lcd.print('/');
lcd.print(now.month(),
DEC);
lcd.print('/');
lcd.print(now.day(), DEC);
lcd.setCursor(0, 3);
lcd.print(now.hour(), DEC);
lcd.print(':');
lcd.print(now.minute(), DEC);
lcd.print(':');
lcd.print(now.second(),
DEC);
lcd.print('__');
}
// A0 - A15 can be different on
different boards
int pin2analog(int pin)
{
int v = -1;
switch
(pin)
{
case 0: v = A0; break;
case 1: v = A1; break;
case 2: v
= A2; break;
case 3: v = A3; break;
case 4: v = A4; break;
case 5:
v = A5; break;
case 6: v = A6; break;
case 7: v = A7; break;
case
8: v = A8; break;
case 9: v = A9; break;
case 10: v = A10; break;
case 11: v = A11; break;
case 12: v = A12; break;
case 13: v = A13;
break;
case 14: v = A14; break;
case 15: v = A15; break;
}
return v;
}
// bmp180 does not have humidity, need another
sensor for that
void getpressureprim()
{
char status;
double
T,P,p0,a;
float tm;
P1old = P1 + 0.5;
// If you want to
measure altitude, and not pressure, you will instead need
// to provide a
known baseline pressure.
// Start a temperature measurement:
//
If request is successful, the number of ms to wait is returned.
// If
request is unsuccessful, 0 is returned.
status =
pressure.startTemperature();
if (status != 0)
{
// Wait for the
measurement to complete:
delay(status);
// Retrieve the completed
temperature measurement:
// Note that the measurement is stored in the
variable T.
// Function returns 1 if successful, 0 if failure.
status = pressure.getTemperature(T);
if (status != 0)
{
// Print
out the measurement:
// Start a pressure measurement:
// The parameter
is the oversampling setting, from 0 to 3 (highest res, longest wait).
//
If request is successful, the number of ms to wait is returned.
// If
request is unsuccessful, 0 is returned.
status =
pressure.startPressure(3);
if (status != 0)
{
// Wait for the
measurement to complete:
delay(status);
// Retrieve the completed
pressure measurement:
// Note that the measurement is stored in the
variable P.
// Note also that the function requires the previous
temperature measurement (T).
// (If temperature is stable, you can do one
temperature measurement for a number of pressure measurements.)
//
Function returns 1 if successful, 0 if failure.
status =
pressure.getPressure(P,T);
if (status != 0)
{
// Print out the
measurement:
P1 = P;
T1 = T;
// The pressure sensor returns
abolute pressure, which varies with altitude.
// To remove the effects of
altitude, use the sealevel function and your current altitude.
// This
number is commonly used in weather reports.
// Parameters: P = absolute
pressure in mb, ALTITUDE = current altitude in m.
// Result: p0 =
sea-level compensated pressure in mb
}
else Serial.println("error
retrieving pressure measurement\n");
}
else Serial.println("error
starting pressure measurement\n");
}
else Serial.println("error
retrieving temperature measurement\n");
}
else Serial.println("error
starting temperature measurement\n");
}
void setup()
{
// put your setup code here, to run once:
Serial.begin(9600);
//Serial.begin(38400);
Wire.begin(SLAVE_ADDRESS); // NEED TO DO THIS STEP FIRST !!
rtc.begin();
// start up the RTC
ads.setGain(GAIN_TWOTHIRDS); // 2/3x gain +/-
6.144V 1 bit = 3mV 0.1875mV (default) 6.144 volt
ads.begin();
lcd.init();
lcd.backlight();
lcd.begin(16,4);
lcd.setCursor(0,1);
if (! rtc.isrunning())
{
lcd.print("NO
RTC");
hasrtc = 0;
}
else
{
hasrtc = 1;
}
// read
the interval from eeprom
jacker.b[0] = EEPROM.read(0);
jacker.b[1] =
EEPROM.read(1);
jacker.b[2] = EEPROM.read(2);
jacker.b[3] =
EEPROM.read(3);
interval = jacker.l;
if (interval <= 0 || interval >
86400000) // sanity check
interval = 10000; // 10 seconds is the default
// the rtc chip does not support timezones
// so we read the
offset from eprom
// that is set by user with bt.setutcoffset
jacker.b[0] = EEPROM.read(4);
jacker.b[1] = EEPROM.read(5);
jacker.b[2] = EEPROM.read(6);
jacker.b[3] = EEPROM.read(7);
utcoffset
= jacker.l;
if (utcoffset <= 86400 || utcoffset >= 86400) // sanity check
utcoffset = 0; // 0 seconds is the default
lcd.setCursor(0,
2);
if (pressure.begin())
{
Serial.println("BMP180 init
success");
}
else
{
Serial.println("BMP180 init fail\n\n");
}
cbInit(CBSIZE); // init the circular buffer
adsize =
sizeof(adelem);
presize = sizeof(pream);
sprintf(abc, "%d presize %d",
adsize, presize);
lcd.setCursor(0,0);
lcd.print(abc);
}
// Software reset function for arudino , basically reboot at memory
address 0
void (* resetFunc) (void) = 0;
void sendpacket(int
magicin,int sendsize,unsigned char *bout)
{
sentbytes = 0;
unsigned
char pre[6];
pream.magic = magicin;
pream.len = sendsize +
sizeof(crc16);
pream.numrecords = 0; // not used
memcpy(pre,&pream,presize); // presize , the size of the preamble 6 bytes
for this design
crc16 = 0xFFFF;
CRC16(pre, 6);
Serial.write(pre,presize); //send first 6 bytes
sentbytes += presize;
CRC16(btout, sendsize);
Serial.write(bout,sendsize); // sending the
data
sentbytes += sendsize;
Serial.write((byte *) &crc16,2); //
send computed CRC at end of data
sentbytes += 2;
lcd.setCursor(0,3);
sprintf(abc, "snt %d ", sentbytes);
lcd.print(abc);
}
void ReadSensors()
{
if
(hasrtc == 0)
{
secs1970 = millis(); // since no RTC present, use
millis()
}
else
{
now = rtc.now(); // Grab the time from the
RTC
secs1970 = now.unixtime(); // convert it to seconds since 1970
secs1970 = secs1970 - utcoffset;
}
int16_t v =
ads.readADC_SingleEnded(0);
float voltage = (float) v * 0.1875 / 1000.0;
getpressureprim();
// Fill the structure with your values and
write to circular buffer
adelem.secs1970 = secs1970;
adelem.voltage =
voltage;
adelem.pressure = P1;
adelem.temperature = T1;
}
void loop()
{
jack = Serial.available();
if (serialmode ==
1)
{
if (jack >= 1)
{
serialbyte = Serial.read(); //Read the
incoming data and store it into variable data
lcd.clear();
lcd.setCursor(0,0);
sprintf(abc, "in %d %x",serialbyte,serialbyte);
lcd.print(abc);
if (serialbyte == 1)
{
Serial.write((byte *)
&ACK,1); // Send ACK ascii 6
}
if (serialbyte == 2)
{
serialmode
= 0;
}
if (serialbyte == 3)
{
Serial.write((byte *) abc,8);
}
}
return;
}
// this serial code needs to be on
main loop since packets can come in at any time and must be read to capture
without loss
if (jack > 0) // Send data only when you receive data:
{
btout[icc] = Serial.read(); //Read the incoming data and store it into
variable data
icc = icc + 1;
if (icc >= 63)
{
icc = 0;
len = 0;
// problem here, maybe old data, get and trash
while
(Serial.available() > 0)
serialbyte = Serial.read();
}
if (icc
== 6) // at 6 bytes we have the preamble
{
magic = (btout[0] << 8) &
0xff00 | (btout[1] << 0) & 0x00ff;
len = (btout[2] << 8) & 0xff00 |
(btout[3] << 0) & 0x00ff;
numrecs = (btout[4] << 8) & 0xff00 | (btout[5]
<< 0) & 0x00ff;
lcd.clear();
lcd.setCursor(0,0);
sprintf(abc,
"ma %x %d %d", magic, len, numrecs);
lcd.print(abc);
if (len < 0
|| len >= 63)
{
// problem here, maybe old data, get and trash
while (Serial.available() > 0)
serialbyte = Serial.read();
icc = 0;
len = 0;
}
}
if (icc == (6 + len) ) // now that we have
entire packet,check the CRC
{
crcpack = (btout[4 + len] << 8) &
0xff00 | (btout[5 + len] << 0) & 0x00ff;
crc16 = 0xFFFF;
CRC16(btout,
6 + len - 2);
sprintf(abc, "crc %d %d", crcpack, crc16);
lcd.setCursor(0,2);
lcd.print(abc);
if (crcpack != crc16) // crc
mismatch
{
lcd.setCursor(0,3);
lcd.print("Bad CRC");
icc = 0;
len = 0;
magic = 0;
// Serial.write((byte *) &NAK,1); // crc error
send NAK hex 15 dec 21
}
if (magic == 0x4242)
resetFunc();
switch (magic)
{
case 0x4856: // create a single
packet by reading sensors
icc = 0;
len = 0;
ReadSensors(); // get
latest data readings into adelem structure
memcpy(btout,&adelem,adsize);
// copy our sensor data to btout array by memcopy the structure
sendpacket(0x4857,adsize,btout); // add one to magic send back via bluetooth
to the cell phone the data
break;
case 0x4880: // called by
bt.interval = integer value in milliseconds
s = 6;
icc = 0;
len =
0;
// endian mismatch fix
jacker.b[0] = btout[s+3];
jacker.b[1]
= btout[s+2];
jacker.b[2] = btout[s+1];
jacker.b[3] = btout[s+0];
interval = jacker.l;
ivalue32 = jacker.l;
btout[0] =
jacker.b[0];
btout[1] = jacker.b[1];
btout[2] = jacker.b[2];
btout[3] = jacker.b[3];
EEPROM.update(0,btout[0]); // using the
update method,
EEPROM.update(1,btout[1]); // only writes to eeprom if
data is different
EEPROM.update(2,btout[2]); // eeprom only lasts 100,000
write cycles
EEPROM.update(3,btout[3]);
jacker.b[0] =
EEPROM.read(0);
jacker.b[1] = EEPROM.read(1);
jacker.b[2] =
EEPROM.read(2);
jacker.b[3] = EEPROM.read(3);
interval = jacker.l;
sprintf(abc, "eeprom %ld",interval);
lcd.setCursor(0,2);
lcd.print(abc);
lcd.setCursor(0,3);
lcd.print(abc);
memcpy(btout,&ivalue32,4); // copy interval to output buffer
sendpacket(magic+1,4,btout); // send back the old value
break;
case 0x4882: // called by bt.utcoffset = integer value in
seconds
s = 6;
icc = 0;
len = 0;
// endian mismatch fix
jacker.b[0] = btout[s+3];
jacker.b[1] = btout[s+2];
jacker.b[2] =
btout[s+1];
jacker.b[3] = btout[s+0];
utcoffset = jacker.l;
ivalue32 = jacker.l;
btout[0] = jacker.b[0];
btout[1] =
jacker.b[1];
btout[2] = jacker.b[2];
btout[3] = jacker.b[3];
EEPROM.update(4,btout[0]); // using the update method,
EEPROM.update(5,btout[1]); // only writes to eeprom if data is different
EEPROM.update(6,btout[2]); // eeprom only lasts 100,000 write cycles
EEPROM.update(7,btout[3]);
jacker.b[0] = EEPROM.read(4);
jacker.b[1] = EEPROM.read(5);
jacker.b[2] = EEPROM.read(6);
jacker.b[3] = EEPROM.read(7);
utcoffset = jacker.l;
sprintf(abc,
"Eoff %ld",utcoffset);
lcd.setCursor(0,2);
lcd.print(abc);
lcd.setCursor(0,3);
lcd.print(abc);
memcpy(btout,&ivalue32,4); //
copy interval to output buffer
sendpacket(magic+1,4,btout); // send back
the old value
break;
case
0x4850: // get interval
case 0x4852: // get record count
case 0x4854:
// get max record count
case 0x4870: // rtc secs 1970
case 0x4872: //
millis
case 0x4874: // utc offset
icc = 0;
len = 0;
if (magic == 0x4850) // get interval
{
ivalue32 = interval;
}
if (magic == 0x4874) // get utc offset
{
ivalue32 = utcoffset;
}
if (magic == 0x4852)
{
ivalue32 = cbSize(); // get number of
records currently in circular buffer
}
if (magic == 0x4854) // get
the maximum number of records the circular buffer can hold
{
ivalue32
= CBSIZE; // circular buffer size
}
if (magic == 0x4870) // get
secs1970 unixtime
{
now = rtc.now(); // Grab the time from the RTC
ivalue32 = now.unixtime(); // convert it to seconds since 1970
ivalue32 =
ivalue32 - utcoffset;
}
if (magic == 0x4872) // get milli
{
ivalue32 = millis(); // get millis()
}
sprintf(abc, "mag %x v
%ld",magic,ivalue32);
lcd.clear();
lcd.setCursor(0,1);
lcd.print(abc);
lcd.setCursor(0,2);
lcd.print(abc);
// copy
long value, 4 bytes
memcpy(btout,&ivalue32,4); // copy interval to output
buffer
sendpacket(magic+1,4,btout); // add one to magic
break;
case 0x4840: // get the time from the RTC we connected to
the arduino
icc = 0;
len = 0;
func = (btout[6] << 8) & 0xff00 |
(btout[7] << 0) & 0x00ff;
if (hasrtc == 1)
{
now = rtc.now(); //
Grab the time from the RTC
year = now.year();
month = now.month();
day = now.day();
hour = now.hour();
minute = now.minute();
second =
now.second();
}
memcpy(&btout[0],(byte *)&year,2);
memcpy(&btout[2],(byte *)&month,2);
memcpy(&btout[4],(byte *)&day,2);
memcpy(&btout[6],(byte *)&hour,2);
memcpy(&btout[8],(byte *)&minute,2);
memcpy(&btout[10], (byte *)&second,2);
sendpacket(0x4841,12,btout);
// 12 = 6 * 2 from above
break;
case 0x4844: // setting
time
// have all the time data now
icc = 0;
len = 0;
year = (btout[6] << 8) & 0xff00 | (btout[7] << 0) & 0x00ff;
month =
(btout[8] << 8) & 0xff00 | (btout[9] << 0) & 0x00ff;
day = (btout[10] <<
8) & 0xff00 | (btout[11] << 0) & 0x00ff;
hour = (btout[12] << 8) & 0xff00
| (btout[13] << 0) & 0x00ff;
minute = (btout[14] << 8) & 0xff00 |
(btout[15] << 0) & 0x00ff;
second = (btout[16] << 8) & 0xff00 |
(btout[17] << 0) & 0x00ff;
sprintf(abc, "year %d", year);
lcd.setCursor(0,2);
lcd.print(abc);
rtc.adjust(DateTime(year,month,day,hour,minute,second));
ivalue16 =
6;
memcpy(&btout[0],(byte *)&ivalue16,2);
sendpacket(0x4845,2,btout);
break;
default:
sprintf(abc, "??? %d", magic);
lcd.setCursor(0,3);
lcd.print(abc);
break;
}
if (magic == 0x4860) // setting arduino pin
{
icc = 0;
len = 0;
func = (btout[6] << 8) & 0xff00 | (btout[7]
<< 0) & 0x00ff;
pin = (btout[8] << 8) & 0xff00 | (btout[9] << 0) &
0x00ff;
ivalue16 = (btout[10] << 8) & 0xff00 | (btout[11] << 0) & 0x00ff;
if (func == 1)
{
if (ivalue16 == 0)
pinMode(pin,INPUT);
if
(ivalue16 == 1)
pinMode(pin,OUTPUT);
if (ivalue16 == 2)
pinMode(pin,INPUT_PULLUP);
}
if (func == 2)
{
if (ivalue16
== 0)
digitalWrite(pin,LOW);
if (ivalue16 == 1)
digitalWrite(pin,HIGH);
}
if (func == 3)
{
analogWrite(pin,ivalue16);
}
if (func == 4)
{
ivalue16 =
digitalRead(pin);
}
if (func == 5)
{
ivalue16 =
analogRead(pin);
}
if (func == 6)
{
ivalue16 =
pin2analog(pin);
}
sprintf(abc, "f,p,v %d %d
%d",func,pin,ivalue16);
lcd.clear();
lcd.setCursor(0,2);
lcd.print(abc);
memcpy(btout,&ivalue16,2); // copy interval to output
buffer
sendpacket(magic+1,2,btout); // add one to magic
}
if (magic == 0x4836) // Send the schema to Android so it can create
our database for us
{
// get the length of our schema
icc = 0;
len = 0;
int s = 0;
crc16 = 0xFFFF; // we are doing a runing total
on crc
// Arduino has limited memory and can't just allocate buffers ad
hoc
for (i = 0; i < SCHEMASIZE; ++ i)
{
s += schema[i].length()
+ 1; // 1 for trailing null
}
pream.magic = 0x4837;
pream.len
= s + sizeof(crc16); // size plus crc
pream.numrecords = SCHEMASIZE;
memset(btout,0,sizeof(btout));
memcpy(btout,&pream,presize); //
presize , the size of the preamble
CRC16(btout, presize);
Serial.write(btout,presize); // send preamble
for (i = 0; i <
SCHEMASIZE; ++ i)
{
memset(btout,0,sizeof(btout));
s =
schema[i].length();
schema[i].getBytes(btout,sizeof(btout));
Serial.write(btout,s+1); // writing an extra byte a trailing null
CRC16(btout, s+1); // computing crc into crc16 as we gather each record
}
Serial.write((byte *) &crc16,2); // send CRC at end of data
//
we did the crc check on the run without allocating any temporary buffers
}
if (magic == 0x4842) // This code writes out the data of the
circular buffer
{
icc = 0;
len = 0;
sentbytes = 0;
short
removeflag = (btout[6] << 8) & 0xff00 | (btout[7] << 0) & 0x00ff;
int s = cbSize(); // current number of records in circular buffer
int
size = s * adsize; // num recs * structe size
pream.magic = 0x4843;
pream.len = size + sizeof(crc16);
pream.numrecords = cbSize();
memcpy(btout,&pream,presize); // presize , the size of the preamble
Serial.write(btout,presize);
crc16 = 0xFFFF;
CRC16(btout,
presize);
sentbytes += presize;
savecbstart = cb.start;
while (!cbIsEmpty())
{
cbRead(&adelem);
memcpy(btout,&adelem,adsize);
CRC16(btout, adsize);
Serial.write(btout,adsize);
sentbytes += adsize;
}
Serial.write((byte *) &crc16,2); // send CRC at end of data
sentbytes +=
2;
if (removeflag == 0)
cb.start = savecbstart; // this has
effect of reading items but not removing them
// this is needed in
situations when we have not recieved acknowledment that the data was
recieved by the master ("android device itself")
lcd.setCursor(0,1);
sprintf(abc, "sent %d", sentbytes);
lcd.print(abc);
}
icc = 0;
len = 0;
}
} // end of
jack > 1 loop
// Arduino millis() function wraps around in ....
//
2 ^ 32 / 1000 / 3600 / 24 = 49.71 Days
unsigned long currentMillis =
millis();
// sprintf(abc, "%ld %ld %ld", currentMillis,
previousMillis,interval);
// lcd.clear();
// lcd.setCursor(0,1);
//
lcd.print(abc);
if (currentMillis - previousMillis > interval)
{
previousMillis = currentMillis;
ReadSensors();
lcd.setCursor(0,0);
// sprintf(abc, "v=%s ", abc); // no floating point
in this land
dtostrf(adelem.voltage, 6, 2, abc); // so use this instead
lcd.print("v = ");
lcd.print(abc);
dtostrf(adelem.pressure, 6,
1, abc);
lcd.print(" p = ");
lcd.print(abc);
lcd.print(" ");
cbWrite(&adelem); // write to circular buffer
lcd.setCursor(0,1);
sprintf(abc, "%ld", millis());
lcd.print(abc);
lcd.print(" ");
sprintf(abc, "%ld", secs1970);
lcd.print(abc);
showtime();
}
}