Hardware components | ||||||
![]() |
| × | 1 | |||
| × | 1 | ||||
![]() |
| × | 1 | |||
Software apps and online services | ||||||
![]() |
| |||||
![]() |
| |||||
![]() |
| |||||
| ||||||
Hand tools and fabrication machines | ||||||
![]() |
| |||||
![]() |
|
Two-wheelers have claimed 69, 240 lives, contributing to 44.5% of road accident deaths. This report is with respect to only one country, India. The world's count would be much more. Unfortunately, about 30% of deaths are caused due to delayed ambulance service. With a paradigm shift in electronics baked into every, can we resolve this issue with a smart solution?
We propose a proof of concept smart helmet that detects fatal crashes and generates SOS to concerned authorities autonomously. The Unique Selling Proposition of this project is the helmet works autonomously without being connected to any smartphone. This is possible because of emerging low-power cellular networks such as LTE-M, suitable for urban area wireless connectivity with lower data rates. Pioneering this technology is blues wireless, who is offering full stack support ranging from hardware to software for incorporating cellular networks into everyday devices and perhaps making them smart as well.
The data is collected wirelessly using Blues Wireless Notecard to train our model over the cellular network on the Edge Impulse Cloud Platform using Data Ingestion Services via Notehub routes. This makes collecting real-time data far more straightforward for a field device like the helmet.
System DesignThe system includes an ADXL355 accelerometer to collect motion data from the helmet. The swan microcontroller from the blues team is used as the choice of MCU for being small and low power. The accelerometer is interfaced with MCU using SPI. The I2C bus is used for communicating with the anchor of the project blues wireless notecard. Initially, we wanted to collect data onto the SD card and upload it later for training. However, that would defy the purpose as frequent model updates might be required even after deployment as the road condition might vary depending on region to region. So we set up a pipeline to collect data from the sensor and send it over the air to the edge impulse server for training. More details on this are covered in a separate section below. The model is trained to detect three classes idle (helmet stationary), riding (when the driver is riding), and crash (occurrence of crash). Once the data acquisition is over, the model is trained and deployed back on the system. In case of a crash, SOS is generated through Twilio to the concerned authority.
Hardware Build:The anchor of the project is the Blues Notecard which is used for a device-to-cloud data pump that reduces the complexity of building connected solutions with a secure and reliable connection. The building block of the project are mentioned below:
AXL355: This is a low-power 3-Axis MEMS Accelerometer with inbuilt activity threshold detection. This would help us capture the different vibration patterns of helmets under different conditions. The size and precision of the accelerometer also make it a good candidate for such a project.
Blues Wireless Starter KitIt has LTE Cat-1 Notecard – EMEA is designed to be used in the EMEA (Europe, Middle East, Africa) regions and uses Quectel EG91-EX modem, which connects to LTE Cat-1 / WCDMA / GSM networks.
To facilitate the cellular network in India, we used an external Airtel Sim, which seemed to work well. The Kit comes with a feather-compatible carrier board, the Notecarrier F, designed for quick prototyping. It comes with Blues Swan MCU. A low-cost, embeddable STM32L4-based microcontroller designed to accelerate the development and deployment of battery-powered IoT solutions.
Interfacing Sensor and NotecardThe project is developed using Arduino Library. note-Arduino is the official Arduino library for communicating with the Notecard over serial or I2C. The library can be installed from the Arduino Library Manager.
For the prototype, Swan MCU collects accelerometer data from ADXL355z over SPI and sends it to Notecard with JSON APIs over I2C Bus. A slim 1000 mAh Lion Battery supplies power to the whole system. Below refer to the image below for more details on the jumper connection.
Routing The Data from Notehub to Edge Impulse Cloud:One powerful feature of Notehub is Routes, which allows you to forward your data from Notehub to a public cloud like AWS, Azure, or Google Cloud, a messaging platform like MQTT, a data cloud like Snowflake, or a custom HTTP endpoint. A Route is an external API or server location where Notes can be forwarded upon receipt. For more on creating routes, refer to the link: Route Tutorial - ThingSpeak - Blues Wireless Developers.
For the smart helmet project, I've created a route named EIBluesTest.
Route Configuration:Edge Impulse Ingestion service is used to send recently acquired data to Edge Impulse. It's available on HTTP/HTTPS endpoints and requires an API key to authenticate. Here is the link to the ingestion API: https://ingestion.edgeimpulse.com
And three endpoints are available for training, testing, and anomaly dataset, respectively.
- POST /api/training/files
- POST /api/testing/files
- POST /api/anomaly/files
However, we faced an internal server error while making these POST requests. You can learn more about the issues on this thread of the Edge Impulse forum: Error:500 {"success":false, "error":"No data found (in data form field)"} - Help - Edge Impulse
Basically, for CBOR/JSON file types, the endpoint should be POST /api/training/data because POST /api/training/files expect multipart form data (with a files[]
object).
URL: https://ingestion.edgeimpulse.com/API/TRAINING/DATA
Additional Headers:They include an API key to uniquely identify your Edge Impulse Project, a header to enable the addition of date and time to the file log, and the file name.
Data Aquisition Format:Before routing, it is crucial to convert the raw JSON into a specific data acquisition format compatible with the Edge Impulse platform. This is a small specification that describes the type of data, the sensor data itself, and information about the device that generated the data. More about Data Acquisition Format: Data acquisition format - Edge Impulse API
Notehub Route allows us to modify the JSON before routing it to HTTP Endpoints. It is done with the help of JSONata Script.
The “values” field has the raw data in the form of a 2D Array of various accelerometer readings in respective x, y, and z-axis. It is enclosed in structure - body.
The JSON file was verified using Webhook.site, which allows us to preview the data transferred to the endpoint. You can use it by changing the POST request URL to https://webhook.site/00971415-2c0a-4747-8bd4-9d7f69b47abb in Route Configurations.
Please take a look at the demonstration below of collecting training datasets wirelessly over the cellular network to Edge Impulse Cloud Platform with Blues Notecard.
All the collected is routed wirelessly over the cellular network to my Notehub project - smart_helmet. The logs can be seen in the Events tab, as shown below.
To train the dataset, we created our first smart helmet prototype. We made space by removing the cushion and thermocouple at the helmet's base to fit our prototype PCB.
Then, the PCB inside the Helmet was fixated with the help of a glue gun.
Idle data - I left it stable on the table, hanging it to the two-wheeler and cupboard-like places to record Idle data. This is to ensure that when the helmet is not in use, we can put the device in a deep sleep and save some battery. This class was also necessary because while testing, sometimes weird placements of the Helmet (like upside-down) were also classified as crashes.
Riding Data - It's time for a road test! I wore the helmet and ride my two-wheeler around Bangalore to collect actual riding data with turns, bumps, humps, potholes, traffic, frequent braking, etc.
Crash Data - This was a bit difficult, I can't put my life at stake by becoming a stuntman! So, I recreated a crash by acting like getting hit and thrashing my bed. I ensured it felt like sudden braking, the vehicle skidding, or the rider rolling.
Edge Impulse ModelOur whole concept is now dependent upon the performance of the model. Edge Impulse makes all this a hassle-free process. After collecting data, we create our model pipeline in the impulse design tab. We wanted to keep the design simple without being compute-intensive, so we selected flattening the data set as the preprocessing stage. Another reason is that flattening the data works quite well with slow-moving averages, and we only had three classes for inference.
The model generated gives us good accuracy and seems to be a bit overfitting, however, the model does perform well in new data, which is demonstrated in the below sections.
You can access the whole project through this public clone:https://studio.edgeimpulse.com/public/171803/latest
Below is a snapshot of the Twilio configuration for generating an SOS from the note card hub.
We can also send GPS coordinates to specify the accident location. But the device has to be outdoors (which is obvious for a real scenario).
Future Scope1. Power profiling for low-power optimization.
2. Autonomouly deploy the trained model back to the device using https://blues.io/blog/notecard-outboard-firmware-update/
3. Make a better chassis with explosive contents like battery outside
4. Solar panel to charge the device
Setting Up communication between Notecard and Swan MCU
C/C++#include "Wire.h"
#include <Notecard.h> /*Install the Blues Wireless Arduino Library*/
#define productUID "com.gmail.sobhitpanda25:helmet"
Notecard notecard;
void setup()
{
notecard.begin();
Serial.begin(115200);
notecard.setDebugOutputStream(Serial);
J *req = notecard.newRequest("hub.set");
JAddStringToObject(req, "product", productUID);
JAddStringToObject(req, "mode", "continuous");
notecard.sendRequest(req);
}
void loop()
{
J *req = notecard.newRequest("note.add");
if (req != NULL)
{
JAddStringToObject(req, "file", "sensors.qo");
JAddBoolToObject(req, "sync", true);
J *body = JAddObjectToObject(req, "body");
if(body)
{
JAddNumberToObject(body, "x", 9.14);
JAddNumberToObject(body, "y", 9.14);
JAddNumberToObject(body, "z", 9.14);
}
notecard.sendRequest(req);
Serial.println("not null");
}
delay(10000);
}
ADXL355Z Interfacing SPI
C/C++#include <Arduino.h>
#include <SPI.h>
namespace Adxl355 {
const char SPI_W = 0;
const char SPI_R = 1;
enum Reg {
DEVID_AD = 0x00,
DEVID_MST = 0x01,
DEVID = 0x02,
REVID = 0x03,
STATUS = 0x04,
FIFO_ENTRIES = 0x05,
TEMP2 = 0x06,
TEMP1 = 0x07,
XDATA3 = 0x08,
XDATA2 = 0x09,
XDATA1 = 0x0A,
YDATA3 = 0x0B,
YDATA2 = 0x0C,
YDATA1 = 0x0D,
ZDATA3 = 0x0E,
ZDATA2 = 0x0F,
ZDATA1 = 0x10,
FIFO_DATA = 0x11,
OFFSET_X_H = 0x1E,
OFFSET_X_L = 0x1F,
OFFSET_Y_H = 0x20,
OFFSET_Y_L = 0x21,
OFFSET_Z_H = 0x22,
OFFSET_Z_L = 0x23,
ACT_EN = 0x24,
ACT_THRESH_H = 0x25,
ACT_THRESH_L = 0x26,
ACT_COUNT = 0x27,
FILTER = 0x28,
FIFO_SAMPLES = 0x29,
INT_MAP = 0x2A,
SYNC = 0x2B,
RANGE = 0x2C,
POWER_CTL = 0x2D,
SELF_TEST = 0x2E,
RESET = 0x2F
};
namespace Range {
struct type {
uint8_t code;
double coef;
};
const type _2G = { 0b01, 256000.0 };
const type _4G = { 0b10, 128000.0 };
const type _8G = { 0b11, 64000.0 };
}
namespace ODR {
struct type {
uint8_t code;
};
const type ODR_4000_Hz = { 0 }; // odr = 4000 Hz and lpf = 1000 Hz
const type ODR_2000_Hz = { 1 }; // odr = 2000 Hz and lpf = 500 Hz
const type ODR_1000_Hz = { 2 }; // odr = 1000 Hz and lpf = 250 Hz
const type ODR_500_Hz = { 3 }; // odr = 500 Hz and lpf = 125 Hz
const type ODR_250_Hz = { 4 }; // odr = 250 Hz and lpf = 62.5 Hz
const type ODR_125_Hz = { 5 }; // odr = 125 Hz and lpf = 31.25 Hz
const type ODR_62_5_Hz = { 6 }; // odr = 62.5 Hz and lpf = 15.625 Hz
const type ODR_31_25_Hz = { 7 }; // odr = 31.25 Hz and lpf = 7.813 Hz
const type ODR_15_625_Hz = { 8 }; // odr = 15.625 Hz and lpf = 3.906
const type ODR_7_813_Hz = { 9 }; // odr = 7.813 Hz and lpf = 1.953 Hz
const type ODR_3_906_Hz = { 10 }; // odr = 3.906 Hz and lpf = 0.977 Hz
}
namespace HPF {
struct type {
uint8_t code;
};
const type NO_HPF = { 0 }; // no high pass filter
const type _247_ODR = { 1 }; // corner freq = 247 10^3 ODR
const type _62_084_ODR = { 2 }; // corner freq = 62.048 10^3 ODR
const type _15_545_ODR = { 3 }; // corner freq = 15.454 10^3 ODR
const type _3_862_ODR = { 4 }; // corner freq = 3.862 10^3 ODR
const type _0_954_ODR = { 5 }; // corner freq = 0.954 10^3 ODR
const type _0_238_ODR = { 6 }; // corner freq = 0.238 10^3 ODR
}
class Adxl355 {
public:
Adxl355(SPIClass *spi, uint32_t cs, uint32_t spiFreq = 10000000)
:spi(spi), cs(cs), spiFreq(spiFreq) {}
void begin();
void resetSpi(); // reset spi settings to this objects config
void wakeup();
void sleep();
void setRange(Range::type range);
void setODR(ODR::type odr);
void setHPF(HPF::type hpf);
uint8_t readByte(uint32_t addr);
void readBytes(uint32_t addr, uint8_t *bytes, size_t len);
void writeByte(uint32_t addr, uint8_t byte);
void writeBytes(uint32_t addr, uint8_t *bytes, size_t len);
void updateT();
void updateXyz();
void updateTxyz();
float x;
float y;
float z;
float temp;
private:
SPIClass *spi;
uint32_t cs;
uint32_t spiFreq;
Range::type range = Range::_2G;
ODR::type odr = ODR::ODR_4000_Hz;
HPF::type hpf = HPF::NO_HPF;
};
void Adxl355::begin() {
pinMode(cs, OUTPUT);
digitalWrite(cs, HIGH);
spi->beginTransaction(SPISettings(spiFreq, MSBFIRST, SPI_MODE0));
}
void Adxl355::resetSpi() {
spi->beginTransaction(SPISettings(spiFreq, MSBFIRST, SPI_MODE0));
}
void Adxl355::wakeup() {
writeByte(Reg::POWER_CTL, 0);
}
void Adxl355::sleep() {
writeByte(Reg::POWER_CTL, 1);
}
void Adxl355::setRange(Range::type range) {
this->range = range;
writeByte(Reg::RANGE, range.code);
}
void Adxl355::setODR(ODR::type odr) {
this->odr = odr;
uint8_t byte = (this->hpf.code & 0x07) << 4 | (this->odr.code & 0x0F);
writeByte(Reg::FILTER, byte);
}
void Adxl355::setHPF(HPF::type hpf) {
this->hpf = hpf;
uint8_t byte = (this->hpf.code & 0x07) << 4 | (this->odr.code & 0x0F);
writeByte(Reg::FILTER, byte);
}
uint8_t Adxl355::readByte(uint32_t addr) {
uint8_t bytes[] = {(addr << 1) | SPI_R, 0};
digitalWrite(cs, LOW);
spi->transfer(bytes, 2);
digitalWrite(cs, HIGH);
return bytes[1];
}
void Adxl355::readBytes(uint32_t addr, uint8_t *bytes, size_t len) {
bytes[0] = (addr << 1) | SPI_R;
digitalWrite(cs, LOW);
spi->transfer(bytes, 12);
digitalWrite(cs, HIGH);
}
void Adxl355::writeByte(uint32_t addr, uint8_t byte) {
uint8_t bytes[] = {(addr << 1) | SPI_W, byte};
digitalWrite(cs, LOW);
spi->transfer(bytes, 2);
digitalWrite(cs, HIGH);
}
void Adxl355::writeBytes(uint32_t addr, uint8_t *bytes, size_t len) {
return;
}
void Adxl355::updateTxyz() {
uint8_t bytes[12];
readBytes(Reg::TEMP2, bytes, 12);
int16_t temp = int16_t(bytes[1]) << 8 | bytes[2];
int32_t x = ((int32_t(bytes[3]) << 24) | (int32_t(bytes[4]) << 16) | int32_t(bytes[5] & 0xF0) << 8) >> 12;
int32_t y = ((int32_t(bytes[6]) << 24) | (int32_t(bytes[7]) << 16) | int32_t(bytes[8] & 0xF0) << 8) >> 12;
int32_t z = ((int32_t(bytes[9]) << 24) | (int32_t(bytes[10]) << 16) | int32_t(bytes[11] & 0xF0) << 8) >> 12;
this->temp = ((temp - 1852) / (-9.05)) + 25; // below is simplified version of this line
// this->temp = temp / -9.05 + 229.640883978;
this->x = float(x) / range.coef;
this->y = float(y) / range.coef;
this->z = float(z) / range.coef;
}
void Adxl355::updateXyz() {
uint8_t bytes[10];
readBytes(Reg::XDATA3, bytes, 10);
int32_t x = ((int32_t(bytes[1]) << 24) | (int32_t(bytes[2]) << 16) | int32_t(bytes[3] & 0xF0) << 8) >> 12;
int32_t y = ((int32_t(bytes[4]) << 24) | (int32_t(bytes[5]) << 16) | int32_t(bytes[6] & 0xF0) << 8) >> 12;
int32_t z = ((int32_t(bytes[7]) << 24) | (int32_t(bytes[8]) << 16) | int32_t(bytes[9] & 0xF0) << 8) >> 12;
this->x = float(x) / range.coef;
this->y = float(y) / range.coef;
this->z = float(z) / range.coef;
}
void Adxl355::updateT() {
uint8_t bytes[2];
readBytes(Reg::TEMP2, bytes, 2);
int16_t temp = int16_t(bytes[1]) << 8 | bytes[2];
this->temp = ((temp - 1852) / (-9.05)) + 25; // below is simplified version of this line
// this->temp = temp / -9.05 + 229.640883978;
}
} // namespace Adxl355
using namespace Adxl355;
#define CS PC5 //Chip Select Pin
Adxl355::Adxl355 adxl (&SPI, CS);
void setup() {
Serial.begin(115200);
adxl.begin();
adxl.setRange(Range::_8G);
adxl.setHPF(HPF::NO_HPF);
adxl.setODR(ODR::ODR_2000_Hz);
adxl.wakeup();
}
void loop() {
adxl.updateTxyz();
int id = adxl.readByte(Reg::DEVID);
Serial.print(id, HEX); Serial.print(" ");
Serial.print(adxl.x); Serial.print(" ");
Serial.print(adxl.y); Serial.print(" ");
Serial.print(adxl.z); Serial.println("");
}
Continuous Riding Data Collection
C/C++#include <Arduino.h>
#include <SPI.h>
#include <Notecard.h>
#include "Wire.h"
#define productUID "com.gmail.sobhitpanda25:helmet"
Notecard notecard;
unsigned long prevtime, currtime;
namespace Adxl355 {
const char SPI_W = 0;
const char SPI_R = 1;
enum Reg {
DEVID_AD = 0x00,
DEVID_MST = 0x01,
DEVID = 0x02,
REVID = 0x03,
STATUS = 0x04,
FIFO_ENTRIES = 0x05,
TEMP2 = 0x06,
TEMP1 = 0x07,
XDATA3 = 0x08,
XDATA2 = 0x09,
XDATA1 = 0x0A,
YDATA3 = 0x0B,
YDATA2 = 0x0C,
YDATA1 = 0x0D,
ZDATA3 = 0x0E,
ZDATA2 = 0x0F,
ZDATA1 = 0x10,
FIFO_DATA = 0x11,
OFFSET_X_H = 0x1E,
OFFSET_X_L = 0x1F,
OFFSET_Y_H = 0x20,
OFFSET_Y_L = 0x21,
OFFSET_Z_H = 0x22,
OFFSET_Z_L = 0x23,
ACT_EN = 0x24,
ACT_THRESH_H = 0x25,
ACT_THRESH_L = 0x26,
ACT_COUNT = 0x27,
FILTER = 0x28,
FIFO_SAMPLES = 0x29,
INT_MAP = 0x2A,
SYNC = 0x2B,
RANGE = 0x2C,
POWER_CTL = 0x2D,
SELF_TEST = 0x2E,
RESET = 0x2F
};
namespace Range {
struct type {
uint8_t code;
double coef;
};
const type _2G = { 0b01, 256000.0 };
const type _4G = { 0b10, 128000.0 };
const type _8G = { 0b11, 64000.0 };
}
namespace ODR {
struct type {
uint8_t code;
};
const type ODR_4000_Hz = { 0 }; // odr = 4000 Hz and lpf = 1000 Hz
const type ODR_2000_Hz = { 1 }; // odr = 2000 Hz and lpf = 500 Hz
const type ODR_1000_Hz = { 2 }; // odr = 1000 Hz and lpf = 250 Hz
const type ODR_500_Hz = { 3 }; // odr = 500 Hz and lpf = 125 Hz
const type ODR_250_Hz = { 4 }; // odr = 250 Hz and lpf = 62.5 Hz
const type ODR_125_Hz = { 5 }; // odr = 125 Hz and lpf = 31.25 Hz
const type ODR_62_5_Hz = { 6 }; // odr = 62.5 Hz and lpf = 15.625 Hz
const type ODR_31_25_Hz = { 7 }; // odr = 31.25 Hz and lpf = 7.813 Hz
const type ODR_15_625_Hz = { 8 }; // odr = 15.625 Hz and lpf = 3.906
const type ODR_7_813_Hz = { 9 }; // odr = 7.813 Hz and lpf = 1.953 Hz
const type ODR_3_906_Hz = { 10 }; // odr = 3.906 Hz and lpf = 0.977 Hz
}
namespace HPF {
struct type {
uint8_t code;
};
const type NO_HPF = { 0 }; // no high pass filter
const type _247_ODR = { 1 }; // corner freq = 247 10^3 ODR
const type _62_084_ODR = { 2 }; // corner freq = 62.048 10^3 ODR
const type _15_545_ODR = { 3 }; // corner freq = 15.454 10^3 ODR
const type _3_862_ODR = { 4 }; // corner freq = 3.862 10^3 ODR
const type _0_954_ODR = { 5 }; // corner freq = 0.954 10^3 ODR
const type _0_238_ODR = { 6 }; // corner freq = 0.238 10^3 ODR
}
class Adxl355 {
public:
Adxl355(SPIClass *spi, uint32_t cs, uint32_t spiFreq = 10000000)
:spi(spi), cs(cs), spiFreq(spiFreq) {}
void begin();
void resetSpi(); // reset spi settings to this objects config
void wakeup();
void sleep();
void setRange(Range::type range);
void setODR(ODR::type odr);
void setHPF(HPF::type hpf);
uint8_t readByte(uint32_t addr);
void readBytes(uint32_t addr, uint8_t *bytes, size_t len);
void writeByte(uint32_t addr, uint8_t byte);
void writeBytes(uint32_t addr, uint8_t *bytes, size_t len);
void updateT();
void updateXyz();
void updateTxyz();
float x;
float y;
float z;
float temp;
private:
SPIClass *spi;
uint32_t cs;
uint32_t spiFreq;
Range::type range = Range::_2G;
ODR::type odr = ODR::ODR_4000_Hz;
HPF::type hpf = HPF::NO_HPF;
};
void Adxl355::begin() {
pinMode(cs, OUTPUT);
digitalWrite(cs, HIGH);
spi->beginTransaction(SPISettings(spiFreq, MSBFIRST, SPI_MODE0));
}
void Adxl355::resetSpi() {
spi->beginTransaction(SPISettings(spiFreq, MSBFIRST, SPI_MODE0));
}
void Adxl355::wakeup() {
writeByte(Reg::POWER_CTL, 0);
}
void Adxl355::sleep() {
writeByte(Reg::POWER_CTL, 1);
}
void Adxl355::setRange(Range::type range) {
this->range = range;
writeByte(Reg::RANGE, range.code);
}
void Adxl355::setODR(ODR::type odr) {
this->odr = odr;
uint8_t byte = (this->hpf.code & 0x07) << 4 | (this->odr.code & 0x0F);
writeByte(Reg::FILTER, byte);
}
void Adxl355::setHPF(HPF::type hpf) {
this->hpf = hpf;
uint8_t byte = (this->hpf.code & 0x07) << 4 | (this->odr.code & 0x0F);
writeByte(Reg::FILTER, byte);
}
uint8_t Adxl355::readByte(uint32_t addr) {
uint8_t bytes[] = {(addr << 1) | SPI_R, 0};
digitalWrite(cs, LOW);
spi->transfer(bytes, 2);
digitalWrite(cs, HIGH);
return bytes[1];
}
void Adxl355::readBytes(uint32_t addr, uint8_t *bytes, size_t len) {
bytes[0] = (addr << 1) | SPI_R;
digitalWrite(cs, LOW);
spi->transfer(bytes, 12);
digitalWrite(cs, HIGH);
}
void Adxl355::writeByte(uint32_t addr, uint8_t byte) {
uint8_t bytes[] = {(addr << 1) | SPI_W, byte};
digitalWrite(cs, LOW);
spi->transfer(bytes, 2);
digitalWrite(cs, HIGH);
}
void Adxl355::writeBytes(uint32_t addr, uint8_t *bytes, size_t len) {
return;
}
void Adxl355::updateTxyz() {
uint8_t bytes[12];
readBytes(Reg::TEMP2, bytes, 12);
int16_t temp = int16_t(bytes[1]) << 8 | bytes[2];
int32_t x = ((int32_t(bytes[3]) << 24) | (int32_t(bytes[4]) << 16) | int32_t(bytes[5] & 0xF0) << 8) >> 12;
int32_t y = ((int32_t(bytes[6]) << 24) | (int32_t(bytes[7]) << 16) | int32_t(bytes[8] & 0xF0) << 8) >> 12;
int32_t z = ((int32_t(bytes[9]) << 24) | (int32_t(bytes[10]) << 16) | int32_t(bytes[11] & 0xF0) << 8) >> 12;
this->temp = ((temp - 1852) / (-9.05)) + 25; // below is simplified version of this line
// this->temp = temp / -9.05 + 229.640883978;
this->x = float(x) / range.coef;
this->y = float(y) / range.coef;
this->z = float(z) / range.coef;
}
void Adxl355::updateXyz() {
uint8_t bytes[10];
readBytes(Reg::XDATA3, bytes, 10);
int32_t x = ((int32_t(bytes[1]) << 24) | (int32_t(bytes[2]) << 16) | int32_t(bytes[3] & 0xF0) << 8) >> 12;
int32_t y = ((int32_t(bytes[4]) << 24) | (int32_t(bytes[5]) << 16) | int32_t(bytes[6] & 0xF0) << 8) >> 12;
int32_t z = ((int32_t(bytes[7]) << 24) | (int32_t(bytes[8]) << 16) | int32_t(bytes[9] & 0xF0) << 8) >> 12;
this->x = float(x) / range.coef;
this->y = float(y) / range.coef;
this->z = float(z) / range.coef;
}
void Adxl355::updateT() {
uint8_t bytes[2];
readBytes(Reg::TEMP2, bytes, 2);
int16_t temp = int16_t(bytes[1]) << 8 | bytes[2];
this->temp = ((temp - 1852) / (-9.05)) + 25; // below is simplified version of this line
// this->temp = temp / -9.05 + 229.640883978;
}
} // namespace Adxl355
using namespace Adxl355;
#define CS PC5
Adxl355::Adxl355 adxl (&SPI, CS);
void setup() {
pinMode(USER_BTN, INPUT);
notecard.begin();
Serial.begin(115200);
notecard.setDebugOutputStream(Serial);
Serial.println("Notecard running");
adxl.begin();
adxl.setRange(Range::_2G);
adxl.setHPF(HPF::NO_HPF);
adxl.setODR(ODR::ODR_2000_Hz);
adxl.wakeup();
J *req = notecard.newRequest("hub.set");
JAddStringToObject(req, "product", productUID);
JAddStringToObject(req, "mode", "continuous");
notecard.sendRequest(req);
delay(10000);
}
void loop() {
int id, xdata,ydata,zdata;
if(digitalRead(USER_BTN) == 0){
delay(120000);
while(1){
J *req = notecard.newRequest("note.add");
if (req != NULL)
{
JAddStringToObject(req, "file", "sensors.qo");
JAddBoolToObject(req, "sync", true);
J *body = JAddObjectToObject(req, "body");
if(body)
{
J *values = JAddArrayToObject(body, "values");
currtime = micros();
for(int i=0;i<500;i++){
adxl.updateTxyz();
prevtime = currtime;
id = adxl.readByte(Reg::DEVID);
xdata = adxl.x * 100;
ydata = adxl.y * 100;
zdata = adxl.z * 100;
double num[] = {xdata,ydata,zdata};
JAddItemToArray(values, JCreateNumberArray(num, 3));
currtime = micros();
while((currtime-prevtime) <= 10000){
currtime = micros();
}
}
}
notecard.sendRequest(req);
Serial.println("not null");
}
}
}
}
Inferencing at the Edge
C/C++/* Includes ---------------------------------------------------------------- */
#include <Blues_test_inferencing.h>
/* Private variables ------------------------------------------------------- */
static bool debug_nn = false; // Set this to true to see e.g. features generated from the raw signal
#include <Arduino.h>
#include <SPI.h>
#include <Notecard.h>
#include "Wire.h"
#define productUID "com.gmail.sobhitpanda25:helmet"
Notecard notecard;
unsigned long prevtime, currtime;
namespace Adxl355 {
const char SPI_W = 0;
const char SPI_R = 1;
enum Reg {
DEVID_AD = 0x00,
DEVID_MST = 0x01,
DEVID = 0x02,
REVID = 0x03,
STATUS = 0x04,
FIFO_ENTRIES = 0x05,
TEMP2 = 0x06,
TEMP1 = 0x07,
XDATA3 = 0x08,
XDATA2 = 0x09,
XDATA1 = 0x0A,
YDATA3 = 0x0B,
YDATA2 = 0x0C,
YDATA1 = 0x0D,
ZDATA3 = 0x0E,
ZDATA2 = 0x0F,
ZDATA1 = 0x10,
FIFO_DATA = 0x11,
OFFSET_X_H = 0x1E,
OFFSET_X_L = 0x1F,
OFFSET_Y_H = 0x20,
OFFSET_Y_L = 0x21,
OFFSET_Z_H = 0x22,
OFFSET_Z_L = 0x23,
ACT_EN = 0x24,
ACT_THRESH_H = 0x25,
ACT_THRESH_L = 0x26,
ACT_COUNT = 0x27,
FILTER = 0x28,
FIFO_SAMPLES = 0x29,
INT_MAP = 0x2A,
SYNC = 0x2B,
RANGE = 0x2C,
POWER_CTL = 0x2D,
SELF_TEST = 0x2E,
RESET = 0x2F
};
namespace Range {
struct type {
uint8_t code;
double coef;
};
const type _2G = { 0b01, 256000.0 };
const type _4G = { 0b10, 128000.0 };
const type _8G = { 0b11, 64000.0 };
}
namespace ODR {
struct type {
uint8_t code;
};
const type ODR_4000_Hz = { 0 }; // odr = 4000 Hz and lpf = 1000 Hz
const type ODR_2000_Hz = { 1 }; // odr = 2000 Hz and lpf = 500 Hz
const type ODR_1000_Hz = { 2 }; // odr = 1000 Hz and lpf = 250 Hz
const type ODR_500_Hz = { 3 }; // odr = 500 Hz and lpf = 125 Hz
const type ODR_250_Hz = { 4 }; // odr = 250 Hz and lpf = 62.5 Hz
const type ODR_125_Hz = { 5 }; // odr = 125 Hz and lpf = 31.25 Hz
const type ODR_62_5_Hz = { 6 }; // odr = 62.5 Hz and lpf = 15.625 Hz
const type ODR_31_25_Hz = { 7 }; // odr = 31.25 Hz and lpf = 7.813 Hz
const type ODR_15_625_Hz = { 8 }; // odr = 15.625 Hz and lpf = 3.906
const type ODR_7_813_Hz = { 9 }; // odr = 7.813 Hz and lpf = 1.953 Hz
const type ODR_3_906_Hz = { 10 }; // odr = 3.906 Hz and lpf = 0.977 Hz
}
namespace HPF {
struct type {
uint8_t code;
};
const type NO_HPF = { 0 }; // no high pass filter
const type _247_ODR = { 1 }; // corner freq = 247 10^3 ODR
const type _62_084_ODR = { 2 }; // corner freq = 62.048 10^3 ODR
const type _15_545_ODR = { 3 }; // corner freq = 15.454 10^3 ODR
const type _3_862_ODR = { 4 }; // corner freq = 3.862 10^3 ODR
const type _0_954_ODR = { 5 }; // corner freq = 0.954 10^3 ODR
const type _0_238_ODR = { 6 }; // corner freq = 0.238 10^3 ODR
}
class Adxl355 {
public:
Adxl355(SPIClass *spi, uint32_t cs, uint32_t spiFreq = 10000000)
:spi(spi), cs(cs), spiFreq(spiFreq) {}
void begin();
void resetSpi(); // reset spi settings to this objects config
void wakeup();
void sleep();
void setRange(Range::type range);
void setODR(ODR::type odr);
void setHPF(HPF::type hpf);
uint8_t readByte(uint32_t addr);
void readBytes(uint32_t addr, uint8_t *bytes, size_t len);
void writeByte(uint32_t addr, uint8_t byte);
void writeBytes(uint32_t addr, uint8_t *bytes, size_t len);
void updateT();
void updateXyz();
void updateTxyz();
float x;
float y;
float z;
float temp;
private:
SPIClass *spi;
uint32_t cs;
uint32_t spiFreq;
Range::type range = Range::_2G;
ODR::type odr = ODR::ODR_4000_Hz;
HPF::type hpf = HPF::NO_HPF;
};
void Adxl355::begin() {
pinMode(cs, OUTPUT);
digitalWrite(cs, HIGH);
spi->beginTransaction(SPISettings(spiFreq, MSBFIRST, SPI_MODE0));
}
void Adxl355::resetSpi() {
spi->beginTransaction(SPISettings(spiFreq, MSBFIRST, SPI_MODE0));
}
void Adxl355::wakeup() {
writeByte(Reg::POWER_CTL, 0);
}
void Adxl355::sleep() {
writeByte(Reg::POWER_CTL, 1);
}
void Adxl355::setRange(Range::type range) {
this->range = range;
writeByte(Reg::RANGE, range.code);
}
void Adxl355::setODR(ODR::type odr) {
this->odr = odr;
uint8_t byte = (this->hpf.code & 0x07) << 4 | (this->odr.code & 0x0F);
writeByte(Reg::FILTER, byte);
}
void Adxl355::setHPF(HPF::type hpf) {
this->hpf = hpf;
uint8_t byte = (this->hpf.code & 0x07) << 4 | (this->odr.code & 0x0F);
writeByte(Reg::FILTER, byte);
}
uint8_t Adxl355::readByte(uint32_t addr) {
uint8_t bytes[] = {(addr << 1) | SPI_R, 0};
digitalWrite(cs, LOW);
spi->transfer(bytes, 2);
digitalWrite(cs, HIGH);
return bytes[1];
}
void Adxl355::readBytes(uint32_t addr, uint8_t *bytes, size_t len) {
bytes[0] = (addr << 1) | SPI_R;
digitalWrite(cs, LOW);
spi->transfer(bytes, 12);
digitalWrite(cs, HIGH);
}
void Adxl355::writeByte(uint32_t addr, uint8_t byte) {
uint8_t bytes[] = {(addr << 1) | SPI_W, byte};
digitalWrite(cs, LOW);
spi->transfer(bytes, 2);
digitalWrite(cs, HIGH);
}
void Adxl355::writeBytes(uint32_t addr, uint8_t *bytes, size_t len) {
return;
}
void Adxl355::updateTxyz() {
uint8_t bytes[12];
readBytes(Reg::TEMP2, bytes, 12);
int16_t temp = int16_t(bytes[1]) << 8 | bytes[2];
int32_t x = ((int32_t(bytes[3]) << 24) | (int32_t(bytes[4]) << 16) | int32_t(bytes[5] & 0xF0) << 8) >> 12;
int32_t y = ((int32_t(bytes[6]) << 24) | (int32_t(bytes[7]) << 16) | int32_t(bytes[8] & 0xF0) << 8) >> 12;
int32_t z = ((int32_t(bytes[9]) << 24) | (int32_t(bytes[10]) << 16) | int32_t(bytes[11] & 0xF0) << 8) >> 12;
this->temp = ((temp - 1852) / (-9.05)) + 25; // below is simplified version of this line
// this->temp = temp / -9.05 + 229.640883978;
this->x = float(x) / range.coef;
this->y = float(y) / range.coef;
this->z = float(z) / range.coef;
}
void Adxl355::updateXyz() {
uint8_t bytes[10];
readBytes(Reg::XDATA3, bytes, 10);
int32_t x = ((int32_t(bytes[1]) << 24) | (int32_t(bytes[2]) << 16) | int32_t(bytes[3] & 0xF0) << 8) >> 12;
int32_t y = ((int32_t(bytes[4]) << 24) | (int32_t(bytes[5]) << 16) | int32_t(bytes[6] & 0xF0) << 8) >> 12;
int32_t z = ((int32_t(bytes[7]) << 24) | (int32_t(bytes[8]) << 16) | int32_t(bytes[9] & 0xF0) << 8) >> 12;
this->x = float(x) / range.coef;
this->y = float(y) / range.coef;
this->z = float(z) / range.coef;
}
void Adxl355::updateT() {
uint8_t bytes[2];
readBytes(Reg::TEMP2, bytes, 2);
int16_t temp = int16_t(bytes[1]) << 8 | bytes[2];
this->temp = ((temp - 1852) / (-9.05)) + 25; // below is simplified version of this line
// this->temp = temp / -9.05 + 229.640883978;
}
} // namespace Adxl355
using namespace Adxl355;
#define CS PC5
Adxl355::Adxl355 adxl (&SPI, CS);
/**
* @brief Arduino setup function
*/
void setup()
{
pinMode(USER_BTN , INPUT);
notecard.begin();
Serial.begin(115200);
notecard.setDebugOutputStream(Serial);
Serial.println("Notecard running");
adxl.begin();
adxl.setRange(Range::_2G);
adxl.setHPF(HPF::NO_HPF);
adxl.setODR(ODR::ODR_2000_Hz);
adxl.wakeup();
Serial.println("IMU running");
J *req = notecard.newRequest("hub.set");
JAddStringToObject(req, "product", productUID);
JAddStringToObject(req, "mode", "continuous");
notecard.sendRequest(req);
if (EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME != 3) {
ei_printf("ERR: EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME should be equal to 3 (the 3 sensor axes)\n");
return;
}
}
/**
* @brief Return the sign of the number
*
* @param number
* @return int 1 if positive (or 0) -1 if negative
*/
float ei_get_sign(float number) {
return (number >= 0.0) ? 1.0 : -1.0;
}
/**
* @brief Get data and run inferencing
*
* @param[in] debug Get debug info if true
*/
void loop()
{
ei_printf("\nStarting inferencing in 2 seconds...\n");
delay(2000);
ei_printf("Sampling...\n");
// Allocate a buffer here for the values we'll read from the IMU
float buffer[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE] = { 0 };
for (size_t ix = 0; ix < EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE; ix += 3) {
// Determine the next tick (and then sleep later)
uint64_t next_tick = micros() + (EI_CLASSIFIER_INTERVAL_MS * 1000);
adxl.updateTxyz();
buffer[ix] = adxl.x * 100;
buffer[ix + 1] = adxl.y * 100;
buffer[ix + 2] = adxl.z * 100;
delayMicroseconds(next_tick - micros());
}
// Turn the raw buffer in a signal which we can the classify
signal_t signal;
int err = numpy::signal_from_buffer(buffer, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, &signal);
if (err != 0) {
ei_printf("Failed to create signal from buffer (%d)", err);
Serial.println("");
return;
}
// Run the classifier
ei_impulse_result_t result = { 0 };
err = run_classifier(&signal, &result, debug_nn);
if (err != EI_IMPULSE_OK) {
ei_printf("ERR: Failed to run classifier (%d)", err);
Serial.println("");
return;
}
// print the predictions
ei_printf("Predictions ");
ei_printf("(DSP: %d ms., Classification: %d ms., Anomaly: %d ms.)",
result.timing.dsp, result.timing.classification, result.timing.anomaly);
ei_printf(": \n");
Serial.println("");
for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
// ei_printf_float(" %s: %.5f\n", result.classification[ix].label, result.classification[ix].value);
ei_printf("%s:",result.classification[ix].label);
ei_printf_float(result.classification[ix].value);
Serial.println("");
}
#if EI_CLASSIFIER_HAS_ANOMALY == 1
ei_printf_float(" anomaly score: %.3f\n", result.anomaly);
Serial.println("");
#endif
}
#if !defined(EI_CLASSIFIER_SENSOR) || EI_CLASSIFIER_SENSOR != EI_CLASSIFIER_SENSOR_ACCELEROMETER
#error "Invalid model for current sensor"
#endif
/* Includes ---------------------------------------------------------------- */
#include <Blues_test_inferencing.h>
#include <Arduino.h>
#include <SPI.h>
#include <Notecard.h>
#include "Wire.h"
#include "string.h"
/* Private variables ------------------------------------------------------- */
static bool debug_nn = false; // Set this to true to see e.g. features generated from the raw signal
#define CS PC5
size_t gps_time_s;
using namespace std;
#define productUID "com.gmail.sobhitpanda25:helmet"
Notecard notecard;
unsigned long prevtime, currtime;
namespace Adxl355 {
const char SPI_W = 0;
const char SPI_R = 1;
enum Reg {
DEVID_AD = 0x00,
DEVID_MST = 0x01,
DEVID = 0x02,
REVID = 0x03,
STATUS = 0x04,
FIFO_ENTRIES = 0x05,
TEMP2 = 0x06,
TEMP1 = 0x07,
XDATA3 = 0x08,
XDATA2 = 0x09,
XDATA1 = 0x0A,
YDATA3 = 0x0B,
YDATA2 = 0x0C,
YDATA1 = 0x0D,
ZDATA3 = 0x0E,
ZDATA2 = 0x0F,
ZDATA1 = 0x10,
FIFO_DATA = 0x11,
OFFSET_X_H = 0x1E,
OFFSET_X_L = 0x1F,
OFFSET_Y_H = 0x20,
OFFSET_Y_L = 0x21,
OFFSET_Z_H = 0x22,
OFFSET_Z_L = 0x23,
ACT_EN = 0x24,
ACT_THRESH_H = 0x25,
ACT_THRESH_L = 0x26,
ACT_COUNT = 0x27,
FILTER = 0x28,
FIFO_SAMPLES = 0x29,
INT_MAP = 0x2A,
SYNC = 0x2B,
RANGE = 0x2C,
POWER_CTL = 0x2D,
SELF_TEST = 0x2E,
RESET = 0x2F
};
namespace Range {
struct type {
uint8_t code;
double coef;
};
const type _2G = { 0b01, 256000.0 };
const type _4G = { 0b10, 128000.0 };
const type _8G = { 0b11, 64000.0 };
}
namespace ODR {
struct type {
uint8_t code;
};
const type ODR_4000_Hz = { 0 }; // odr = 4000 Hz and lpf = 1000 Hz
const type ODR_2000_Hz = { 1 }; // odr = 2000 Hz and lpf = 500 Hz
const type ODR_1000_Hz = { 2 }; // odr = 1000 Hz and lpf = 250 Hz
const type ODR_500_Hz = { 3 }; // odr = 500 Hz and lpf = 125 Hz
const type ODR_250_Hz = { 4 }; // odr = 250 Hz and lpf = 62.5 Hz
const type ODR_125_Hz = { 5 }; // odr = 125 Hz and lpf = 31.25 Hz
const type ODR_62_5_Hz = { 6 }; // odr = 62.5 Hz and lpf = 15.625 Hz
const type ODR_31_25_Hz = { 7 }; // odr = 31.25 Hz and lpf = 7.813 Hz
const type ODR_15_625_Hz = { 8 }; // odr = 15.625 Hz and lpf = 3.906
const type ODR_7_813_Hz = { 9 }; // odr = 7.813 Hz and lpf = 1.953 Hz
const type ODR_3_906_Hz = { 10 }; // odr = 3.906 Hz and lpf = 0.977 Hz
}
namespace HPF {
struct type {
uint8_t code;
};
const type NO_HPF = { 0 }; // no high pass filter
const type _247_ODR = { 1 }; // corner freq = 247 10^3 ODR
const type _62_084_ODR = { 2 }; // corner freq = 62.048 10^3 ODR
const type _15_545_ODR = { 3 }; // corner freq = 15.454 10^3 ODR
const type _3_862_ODR = { 4 }; // corner freq = 3.862 10^3 ODR
const type _0_954_ODR = { 5 }; // corner freq = 0.954 10^3 ODR
const type _0_238_ODR = { 6 }; // corner freq = 0.238 10^3 ODR
}
class Adxl355 {
public:
Adxl355(SPIClass *spi, uint32_t cs, uint32_t spiFreq = 10000000)
:spi(spi), cs(cs), spiFreq(spiFreq) {}
void begin();
void resetSpi(); // reset spi settings to this objects config
void wakeup();
void sleep();
void setRange(Range::type range);
void setODR(ODR::type odr);
void setHPF(HPF::type hpf);
uint8_t readByte(uint32_t addr);
void readBytes(uint32_t addr, uint8_t *bytes, size_t len);
void writeByte(uint32_t addr, uint8_t byte);
void writeBytes(uint32_t addr, uint8_t *bytes, size_t len);
void updateT();
void updateXyz();
void updateTxyz();
float x;
float y;
float z;
float temp;
private:
SPIClass *spi;
uint32_t cs;
uint32_t spiFreq;
Range::type range = Range::_2G;
ODR::type odr = ODR::ODR_4000_Hz;
HPF::type hpf = HPF::NO_HPF;
};
void Adxl355::begin() {
pinMode(cs, OUTPUT);
digitalWrite(cs, HIGH);
spi->beginTransaction(SPISettings(spiFreq, MSBFIRST, SPI_MODE0));
}
void Adxl355::resetSpi() {
spi->beginTransaction(SPISettings(spiFreq, MSBFIRST, SPI_MODE0));
}
void Adxl355::wakeup() {
writeByte(Reg::POWER_CTL, 0);
}
void Adxl355::sleep() {
writeByte(Reg::POWER_CTL, 1);
}
void Adxl355::setRange(Range::type range) {
this->range = range;
writeByte(Reg::RANGE, range.code);
}
void Adxl355::setODR(ODR::type odr) {
this->odr = odr;
uint8_t byte = (this->hpf.code & 0x07) << 4 | (this->odr.code & 0x0F);
writeByte(Reg::FILTER, byte);
}
void Adxl355::setHPF(HPF::type hpf) {
this->hpf = hpf;
uint8_t byte = (this->hpf.code & 0x07) << 4 | (this->odr.code & 0x0F);
writeByte(Reg::FILTER, byte);
}
uint8_t Adxl355::readByte(uint32_t addr) {
uint8_t bytes[] = {(addr << 1) | SPI_R, 0};
digitalWrite(cs, LOW);
spi->transfer(bytes, 2);
digitalWrite(cs, HIGH);
return bytes[1];
}
void Adxl355::readBytes(uint32_t addr, uint8_t *bytes, size_t len) {
bytes[0] = (addr << 1) | SPI_R;
digitalWrite(cs, LOW);
spi->transfer(bytes, 12);
digitalWrite(cs, HIGH);
}
void Adxl355::writeByte(uint32_t addr, uint8_t byte) {
uint8_t bytes[] = {(addr << 1) | SPI_W, byte};
digitalWrite(cs, LOW);
spi->transfer(bytes, 2);
digitalWrite(cs, HIGH);
}
void Adxl355::writeBytes(uint32_t addr, uint8_t *bytes, size_t len) {
return;
}
void Adxl355::updateTxyz() {
uint8_t bytes[12];
readBytes(Reg::TEMP2, bytes, 12);
int16_t temp = int16_t(bytes[1]) << 8 | bytes[2];
int32_t x = ((int32_t(bytes[3]) << 24) | (int32_t(bytes[4]) << 16) | int32_t(bytes[5] & 0xF0) << 8) >> 12;
int32_t y = ((int32_t(bytes[6]) << 24) | (int32_t(bytes[7]) << 16) | int32_t(bytes[8] & 0xF0) << 8) >> 12;
int32_t z = ((int32_t(bytes[9]) << 24) | (int32_t(bytes[10]) << 16) | int32_t(bytes[11] & 0xF0) << 8) >> 12;
this->temp = ((temp - 1852) / (-9.05)) + 25; // below is simplified version of this line
// this->temp = temp / -9.05 + 229.640883978;
this->x = float(x) / range.coef;
this->y = float(y) / range.coef;
this->z = float(z) / range.coef;
}
void Adxl355::updateXyz() {
uint8_t bytes[10];
readBytes(Reg::XDATA3, bytes, 10);
int32_t x = ((int32_t(bytes[1]) << 24) | (int32_t(bytes[2]) << 16) | int32_t(bytes[3] & 0xF0) << 8) >> 12;
int32_t y = ((int32_t(bytes[4]) << 24) | (int32_t(bytes[5]) << 16) | int32_t(bytes[6] & 0xF0) << 8) >> 12;
int32_t z = ((int32_t(bytes[7]) << 24) | (int32_t(bytes[8]) << 16) | int32_t(bytes[9] & 0xF0) << 8) >> 12;
this->x = float(x) / range.coef;
this->y = float(y) / range.coef;
this->z = float(z) / range.coef;
}
void Adxl355::updateT() {
uint8_t bytes[2];
readBytes(Reg::TEMP2, bytes, 2);
int16_t temp = int16_t(bytes[1]) << 8 | bytes[2];
this->temp = ((temp - 1852) / (-9.05)) + 25; // below is simplified version of this line
// this->temp = temp / -9.05 + 229.640883978;
}
} // namespace Adxl355
using namespace Adxl355;
Adxl355::Adxl355 adxl (&SPI, CS);
/**
* @brief Arduino setup function
*/
void setup()
{
pinMode(USER_BTN , INPUT);
notecard.begin();
Serial.begin(115200);
notecard.setDebugOutputStream(Serial);
Serial.println("Notecard running");
adxl.begin();
adxl.setRange(Range::_2G);
adxl.setHPF(HPF::NO_HPF);
adxl.setODR(ODR::ODR_2000_Hz);
adxl.wakeup();
Serial.println("IMU running");
J *req = notecard.newRequest("hub.set");
JAddStringToObject(req, "product", productUID);
JAddStringToObject(req, "mode", "periodic");
notecard.sendRequest(req);
req = notecard.newRequest("card.location.mode");
JAddStringToObject(req, "mode", "periodic");
JAddNumberToObject(req, "seconds", 3600);
notecard.sendRequest(req);
req = notecard.newRequest("card.location.track");
JAddBoolToObject(req, "sync", true);
JAddBoolToObject(req, "heartbeat", true);
JAddNumberToObject(req, "hours", 12);
notecard.sendRequest(req);
J *rsp = notecard.requestAndResponse(notecard.newRequest("card.location"));
gps_time_s = JGetInt(rsp, "time");
NoteDeleteResponse(rsp);
if (EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME != 3) {
ei_printf("ERR: EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME should be equal to 3 (the 3 sensor axes)\n");
return;
}
}
/**
* @brief Return the sign of the number
*
* @param number
* @return int 1 if positive (or 0) -1 if negative
*/
float ei_get_sign(float number) {
return (number >= 0.0) ? 1.0 : -1.0;
}
/**
* @brief Get data and run inferencing
*
* @param[in] debug Get debug info if true
*/
void loop()
{
if(digitalRead(USER_BTN) == 0){
ei_printf("\nStarting inferencing in 120 seconds...\n");
delay(15000);
ei_printf("Sampling...\n");
// Allocate a buffer here for the values we'll read from the IMU
float buffer[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE] = { 0 };
for (size_t ix = 0; ix < EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE; ix += 3) {
// Determine the next tick (and then sleep later)
uint64_t next_tick = micros() + (EI_CLASSIFIER_INTERVAL_MS * 1000);
adxl.updateTxyz();
buffer[ix] = adxl.x * 100;
buffer[ix + 1] = adxl.y * 100;
buffer[ix + 2] = adxl.z * 100;
delayMicroseconds(next_tick - micros());
}
// Turn the raw buffer in a signal which we can the classify
signal_t signal;
int err = numpy::signal_from_buffer(buffer, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, &signal);
if (err != 0) {
ei_printf("Failed to create signal from buffer (%d)", err);
Serial.println("");
return;
}
// Run the classifier
ei_impulse_result_t result = { 0 };
err = run_classifier(&signal, &result, debug_nn);
if (err != EI_IMPULSE_OK) {
ei_printf("ERR: Failed to run classifier (%d)", err);
Serial.println("");
return;
}
// print the predictions
ei_printf("Predictions ");
ei_printf("(DSP: %d ms., Classification: %d ms., Anomaly: %d ms.)",
result.timing.dsp, result.timing.classification, result.timing.anomaly);
ei_printf(": \n");
Serial.println("");
for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
// ei_printf_float(" %s: %.5f\n", result.classification[ix].label, result.classification[ix].value);
ei_printf("%s:",result.classification[ix].label);
ei_printf_float(result.classification[ix].value);
Serial.println("");
}
double lat,lon;
char arr[200];
//Send the data to twilio if it is a crash
if(result.classification[1].value > 0.8){
J *req = notecard.newRequest("card.location.mode");
JAddStringToObject(req, "mode", "continuous");
notecard.sendRequest(req);
// Block while waiting for a GPS/GNSS location
for (;;)
{
// Get the latest location
J *rsp = notecard.requestAndResponse(notecard.newRequest("card.location"));
// See if the location has changed from the previous reading
if (JGetInt(rsp, "time") != gps_time_s)
{
// If you get in here, you have a new reading.
lat = JGetNumber(rsp, "lat");
lon = JGetNumber(rsp, "lon");
Serial.println(lat);
Serial.println(lon);
String t = "Its a crash! Send help to: http://maps.google.com/?q=" + String(lat, 6) + "," + String(lon, 6);
strcpy(arr,t.c_str());
Serial.println(t);
Serial.println(arr);
// This is where youd place your application-specific code to use the
// new coordinates.
NoteDeleteResponse(rsp);
break;
}
NoteDeleteResponse(rsp);
// Wait 2 seconds before trying again
delay(2000);
}
}
J *req = notecard.newRequest("note.add");
if (req != NULL)
{
JAddStringToObject(req, "file", "twilio.qo");
JAddBoolToObject(req, "sync", true);
J *body = JAddObjectToObject(req, "body");
if(body)
{
JAddStringToObject(body, "twilioBody", arr);
}
notecard.sendRequest(req);
}
#if EI_CLASSIFIER_HAS_ANOMALY == 1
ei_printf_float(" anomaly score: %.3f\n", result.anomaly);
Serial.println("");
#endif
}
}
#if !defined(EI_CLASSIFIER_SENSOR) || EI_CLASSIFIER_SENSOR != EI_CLASSIFIER_SENSOR_ACCELEROMETER
#error "Invalid model for current sensor"
#endif
Data Collection Over the Air
C/C++#include <Arduino.h>
#include <SPI.h>
#include <Notecard.h>
#include "Wire.h"
#define productUID "com.gmail.sobhitpanda25:helmet"
Notecard notecard;
unsigned long prevtime, currtime;
namespace Adxl355 {
const char SPI_W = 0;
const char SPI_R = 1;
enum Reg {
DEVID_AD = 0x00,
DEVID_MST = 0x01,
DEVID = 0x02,
REVID = 0x03,
STATUS = 0x04,
FIFO_ENTRIES = 0x05,
TEMP2 = 0x06,
TEMP1 = 0x07,
XDATA3 = 0x08,
XDATA2 = 0x09,
XDATA1 = 0x0A,
YDATA3 = 0x0B,
YDATA2 = 0x0C,
YDATA1 = 0x0D,
ZDATA3 = 0x0E,
ZDATA2 = 0x0F,
ZDATA1 = 0x10,
FIFO_DATA = 0x11,
OFFSET_X_H = 0x1E,
OFFSET_X_L = 0x1F,
OFFSET_Y_H = 0x20,
OFFSET_Y_L = 0x21,
OFFSET_Z_H = 0x22,
OFFSET_Z_L = 0x23,
ACT_EN = 0x24,
ACT_THRESH_H = 0x25,
ACT_THRESH_L = 0x26,
ACT_COUNT = 0x27,
FILTER = 0x28,
FIFO_SAMPLES = 0x29,
INT_MAP = 0x2A,
SYNC = 0x2B,
RANGE = 0x2C,
POWER_CTL = 0x2D,
SELF_TEST = 0x2E,
RESET = 0x2F
};
namespace Range {
struct type {
uint8_t code;
double coef;
};
const type _2G = { 0b01, 256000.0 };
const type _4G = { 0b10, 128000.0 };
const type _8G = { 0b11, 64000.0 };
}
namespace ODR {
struct type {
uint8_t code;
};
const type ODR_4000_Hz = { 0 }; // odr = 4000 Hz and lpf = 1000 Hz
const type ODR_2000_Hz = { 1 }; // odr = 2000 Hz and lpf = 500 Hz
const type ODR_1000_Hz = { 2 }; // odr = 1000 Hz and lpf = 250 Hz
const type ODR_500_Hz = { 3 }; // odr = 500 Hz and lpf = 125 Hz
const type ODR_250_Hz = { 4 }; // odr = 250 Hz and lpf = 62.5 Hz
const type ODR_125_Hz = { 5 }; // odr = 125 Hz and lpf = 31.25 Hz
const type ODR_62_5_Hz = { 6 }; // odr = 62.5 Hz and lpf = 15.625 Hz
const type ODR_31_25_Hz = { 7 }; // odr = 31.25 Hz and lpf = 7.813 Hz
const type ODR_15_625_Hz = { 8 }; // odr = 15.625 Hz and lpf = 3.906
const type ODR_7_813_Hz = { 9 }; // odr = 7.813 Hz and lpf = 1.953 Hz
const type ODR_3_906_Hz = { 10 }; // odr = 3.906 Hz and lpf = 0.977 Hz
}
namespace HPF {
struct type {
uint8_t code;
};
const type NO_HPF = { 0 }; // no high pass filter
const type _247_ODR = { 1 }; // corner freq = 247 10^3 ODR
const type _62_084_ODR = { 2 }; // corner freq = 62.048 10^3 ODR
const type _15_545_ODR = { 3 }; // corner freq = 15.454 10^3 ODR
const type _3_862_ODR = { 4 }; // corner freq = 3.862 10^3 ODR
const type _0_954_ODR = { 5 }; // corner freq = 0.954 10^3 ODR
const type _0_238_ODR = { 6 }; // corner freq = 0.238 10^3 ODR
}
class Adxl355 {
public:
Adxl355(SPIClass *spi, uint32_t cs, uint32_t spiFreq = 10000000)
:spi(spi), cs(cs), spiFreq(spiFreq) {}
void begin();
void resetSpi(); // reset spi settings to this objects config
void wakeup();
void sleep();
void setRange(Range::type range);
void setODR(ODR::type odr);
void setHPF(HPF::type hpf);
uint8_t readByte(uint32_t addr);
void readBytes(uint32_t addr, uint8_t *bytes, size_t len);
void writeByte(uint32_t addr, uint8_t byte);
void writeBytes(uint32_t addr, uint8_t *bytes, size_t len);
void updateT();
void updateXyz();
void updateTxyz();
float x;
float y;
float z;
float temp;
private:
SPIClass *spi;
uint32_t cs;
uint32_t spiFreq;
Range::type range = Range::_2G;
ODR::type odr = ODR::ODR_4000_Hz;
HPF::type hpf = HPF::NO_HPF;
};
void Adxl355::begin() {
pinMode(cs, OUTPUT);
digitalWrite(cs, HIGH);
spi->beginTransaction(SPISettings(spiFreq, MSBFIRST, SPI_MODE0));
}
void Adxl355::resetSpi() {
spi->beginTransaction(SPISettings(spiFreq, MSBFIRST, SPI_MODE0));
}
void Adxl355::wakeup() {
writeByte(Reg::POWER_CTL, 0);
}
void Adxl355::sleep() {
writeByte(Reg::POWER_CTL, 1);
}
void Adxl355::setRange(Range::type range) {
this->range = range;
writeByte(Reg::RANGE, range.code);
}
void Adxl355::setODR(ODR::type odr) {
this->odr = odr;
uint8_t byte = (this->hpf.code & 0x07) << 4 | (this->odr.code & 0x0F);
writeByte(Reg::FILTER, byte);
}
void Adxl355::setHPF(HPF::type hpf) {
this->hpf = hpf;
uint8_t byte = (this->hpf.code & 0x07) << 4 | (this->odr.code & 0x0F);
writeByte(Reg::FILTER, byte);
}
uint8_t Adxl355::readByte(uint32_t addr) {
uint8_t bytes[] = {(addr << 1) | SPI_R, 0};
digitalWrite(cs, LOW);
spi->transfer(bytes, 2);
digitalWrite(cs, HIGH);
return bytes[1];
}
void Adxl355::readBytes(uint32_t addr, uint8_t *bytes, size_t len) {
bytes[0] = (addr << 1) | SPI_R;
digitalWrite(cs, LOW);
spi->transfer(bytes, 12);
digitalWrite(cs, HIGH);
}
void Adxl355::writeByte(uint32_t addr, uint8_t byte) {
uint8_t bytes[] = {(addr << 1) | SPI_W, byte};
digitalWrite(cs, LOW);
spi->transfer(bytes, 2);
digitalWrite(cs, HIGH);
}
void Adxl355::writeBytes(uint32_t addr, uint8_t *bytes, size_t len) {
return;
}
void Adxl355::updateTxyz() {
uint8_t bytes[12];
readBytes(Reg::TEMP2, bytes, 12);
int16_t temp = int16_t(bytes[1]) << 8 | bytes[2];
int32_t x = ((int32_t(bytes[3]) << 24) | (int32_t(bytes[4]) << 16) | int32_t(bytes[5] & 0xF0) << 8) >> 12;
int32_t y = ((int32_t(bytes[6]) << 24) | (int32_t(bytes[7]) << 16) | int32_t(bytes[8] & 0xF0) << 8) >> 12;
int32_t z = ((int32_t(bytes[9]) << 24) | (int32_t(bytes[10]) << 16) | int32_t(bytes[11] & 0xF0) << 8) >> 12;
this->temp = ((temp - 1852) / (-9.05)) + 25; // below is simplified version of this line
// this->temp = temp / -9.05 + 229.640883978;
this->x = float(x) / range.coef;
this->y = float(y) / range.coef;
this->z = float(z) / range.coef;
}
void Adxl355::updateXyz() {
uint8_t bytes[10];
readBytes(Reg::XDATA3, bytes, 10);
int32_t x = ((int32_t(bytes[1]) << 24) | (int32_t(bytes[2]) << 16) | int32_t(bytes[3] & 0xF0) << 8) >> 12;
int32_t y = ((int32_t(bytes[4]) << 24) | (int32_t(bytes[5]) << 16) | int32_t(bytes[6] & 0xF0) << 8) >> 12;
int32_t z = ((int32_t(bytes[7]) << 24) | (int32_t(bytes[8]) << 16) | int32_t(bytes[9] & 0xF0) << 8) >> 12;
this->x = float(x) / range.coef;
this->y = float(y) / range.coef;
this->z = float(z) / range.coef;
}
void Adxl355::updateT() {
uint8_t bytes[2];
readBytes(Reg::TEMP2, bytes, 2);
int16_t temp = int16_t(bytes[1]) << 8 | bytes[2];
this->temp = ((temp - 1852) / (-9.05)) + 25; // below is simplified version of this line
// this->temp = temp / -9.05 + 229.640883978;
}
} // namespace Adxl355
using namespace Adxl355;
#define CS PC5
Adxl355::Adxl355 adxl (&SPI, CS);
void setup() {
pinMode(USER_BTN , INPUT);
notecard.begin();
Serial.begin(115200);
notecard.setDebugOutputStream(Serial);
Serial.println("Notecard running");
adxl.begin();
adxl.setRange(Range::_2G);
adxl.setHPF(HPF::NO_HPF);
adxl.setODR(ODR::ODR_2000_Hz);
adxl.wakeup();
J *req = notecard.newRequest("card.location.mode");
JAddStringToObject(req, "mode", "periodic");
JAddNumberToObject(req, "seconds", 3600);
notecard.sendRequest(req);
req = notecard.newRequest("hub.set");
JAddStringToObject(req, "product", productUID);
JAddStringToObject(req, "mode", "continuous");
notecard.sendRequest(req);
}
void loop() {
int id, xdata,ydata,zdata;
if(digitalRead(USER_BTN) == LOW){
delay(10000);
J *req = notecard.newRequest("note.add");
if (req != NULL)
{
JAddStringToObject(req, "file", "sensors.qo");
JAddBoolToObject(req, "sync", true);
J *body = JAddObjectToObject(req, "body");
if(body)
{
J *values = JAddArrayToObject(body, "values");
currtime = micros();
for(int i=0;i<500;i++){
adxl.updateTxyz();
prevtime = currtime;
id = adxl.readByte(Reg::DEVID);
xdata = adxl.x * 100;
ydata = adxl.y * 100;
zdata = adxl.z * 100;
double num[] = {xdata,ydata,zdata};
JAddItemToArray(values, JCreateNumberArray(num, 3));
currtime = micros();
while((currtime-prevtime) <= 10000){
currtime = micros();
}
}
}
notecard.sendRequest(req);
Serial.println("not null");
}
}
}
/* Includes ---------------------------------------------------------------- */
#include <Blues_test_inferencing.h>
/* Private variables ------------------------------------------------------- */
static bool debug_nn = false; // Set this to true to see e.g. features generated from the raw signal
#include <Arduino.h>
#include <SPI.h>
#include <Notecard.h>
#include "Wire.h"
#include "string.h"
using namespace std;
#define productUID "com.gmail.sobhitpanda25:helmet"
Notecard notecard;
unsigned long prevtime, currtime;
namespace Adxl355 {
const char SPI_W = 0;
const char SPI_R = 1;
enum Reg {
DEVID_AD = 0x00,
DEVID_MST = 0x01,
DEVID = 0x02,
REVID = 0x03,
STATUS = 0x04,
FIFO_ENTRIES = 0x05,
TEMP2 = 0x06,
TEMP1 = 0x07,
XDATA3 = 0x08,
XDATA2 = 0x09,
XDATA1 = 0x0A,
YDATA3 = 0x0B,
YDATA2 = 0x0C,
YDATA1 = 0x0D,
ZDATA3 = 0x0E,
ZDATA2 = 0x0F,
ZDATA1 = 0x10,
FIFO_DATA = 0x11,
OFFSET_X_H = 0x1E,
OFFSET_X_L = 0x1F,
OFFSET_Y_H = 0x20,
OFFSET_Y_L = 0x21,
OFFSET_Z_H = 0x22,
OFFSET_Z_L = 0x23,
ACT_EN = 0x24,
ACT_THRESH_H = 0x25,
ACT_THRESH_L = 0x26,
ACT_COUNT = 0x27,
FILTER = 0x28,
FIFO_SAMPLES = 0x29,
INT_MAP = 0x2A,
SYNC = 0x2B,
RANGE = 0x2C,
POWER_CTL = 0x2D,
SELF_TEST = 0x2E,
RESET = 0x2F
};
namespace Range {
struct type {
uint8_t code;
double coef;
};
const type _2G = { 0b01, 256000.0 };
const type _4G = { 0b10, 128000.0 };
const type _8G = { 0b11, 64000.0 };
}
namespace ODR {
struct type {
uint8_t code;
};
const type ODR_4000_Hz = { 0 }; // odr = 4000 Hz and lpf = 1000 Hz
const type ODR_2000_Hz = { 1 }; // odr = 2000 Hz and lpf = 500 Hz
const type ODR_1000_Hz = { 2 }; // odr = 1000 Hz and lpf = 250 Hz
const type ODR_500_Hz = { 3 }; // odr = 500 Hz and lpf = 125 Hz
const type ODR_250_Hz = { 4 }; // odr = 250 Hz and lpf = 62.5 Hz
const type ODR_125_Hz = { 5 }; // odr = 125 Hz and lpf = 31.25 Hz
const type ODR_62_5_Hz = { 6 }; // odr = 62.5 Hz and lpf = 15.625 Hz
const type ODR_31_25_Hz = { 7 }; // odr = 31.25 Hz and lpf = 7.813 Hz
const type ODR_15_625_Hz = { 8 }; // odr = 15.625 Hz and lpf = 3.906
const type ODR_7_813_Hz = { 9 }; // odr = 7.813 Hz and lpf = 1.953 Hz
const type ODR_3_906_Hz = { 10 }; // odr = 3.906 Hz and lpf = 0.977 Hz
}
namespace HPF {
struct type {
uint8_t code;
};
const type NO_HPF = { 0 }; // no high pass filter
const type _247_ODR = { 1 }; // corner freq = 247 10^3 ODR
const type _62_084_ODR = { 2 }; // corner freq = 62.048 10^3 ODR
const type _15_545_ODR = { 3 }; // corner freq = 15.454 10^3 ODR
const type _3_862_ODR = { 4 }; // corner freq = 3.862 10^3 ODR
const type _0_954_ODR = { 5 }; // corner freq = 0.954 10^3 ODR
const type _0_238_ODR = { 6 }; // corner freq = 0.238 10^3 ODR
}
class Adxl355 {
public:
Adxl355(SPIClass *spi, uint32_t cs, uint32_t spiFreq = 10000000)
:spi(spi), cs(cs), spiFreq(spiFreq) {}
void begin();
void resetSpi(); // reset spi settings to this objects config
void wakeup();
void sleep();
void setRange(Range::type range);
void setODR(ODR::type odr);
void setHPF(HPF::type hpf);
uint8_t readByte(uint32_t addr);
void readBytes(uint32_t addr, uint8_t *bytes, size_t len);
void writeByte(uint32_t addr, uint8_t byte);
void writeBytes(uint32_t addr, uint8_t *bytes, size_t len);
void updateT();
void updateXyz();
void updateTxyz();
float x;
float y;
float z;
float temp;
private:
SPIClass *spi;
uint32_t cs;
uint32_t spiFreq;
Range::type range = Range::_2G;
ODR::type odr = ODR::ODR_4000_Hz;
HPF::type hpf = HPF::NO_HPF;
};
void Adxl355::begin() {
pinMode(cs, OUTPUT);
digitalWrite(cs, HIGH);
spi->beginTransaction(SPISettings(spiFreq, MSBFIRST, SPI_MODE0));
}
void Adxl355::resetSpi() {
spi->beginTransaction(SPISettings(spiFreq, MSBFIRST, SPI_MODE0));
}
void Adxl355::wakeup() {
writeByte(Reg::POWER_CTL, 0);
}
void Adxl355::sleep() {
writeByte(Reg::POWER_CTL, 1);
}
void Adxl355::setRange(Range::type range) {
this->range = range;
writeByte(Reg::RANGE, range.code);
}
void Adxl355::setODR(ODR::type odr) {
this->odr = odr;
uint8_t byte = (this->hpf.code & 0x07) << 4 | (this->odr.code & 0x0F);
writeByte(Reg::FILTER, byte);
}
void Adxl355::setHPF(HPF::type hpf) {
this->hpf = hpf;
uint8_t byte = (this->hpf.code & 0x07) << 4 | (this->odr.code & 0x0F);
writeByte(Reg::FILTER, byte);
}
uint8_t Adxl355::readByte(uint32_t addr) {
uint8_t bytes[] = {(addr << 1) | SPI_R, 0};
digitalWrite(cs, LOW);
spi->transfer(bytes, 2);
digitalWrite(cs, HIGH);
return bytes[1];
}
void Adxl355::readBytes(uint32_t addr, uint8_t *bytes, size_t len) {
bytes[0] = (addr << 1) | SPI_R;
digitalWrite(cs, LOW);
spi->transfer(bytes, 12);
digitalWrite(cs, HIGH);
}
void Adxl355::writeByte(uint32_t addr, uint8_t byte) {
uint8_t bytes[] = {(addr << 1) | SPI_W, byte};
digitalWrite(cs, LOW);
spi->transfer(bytes, 2);
digitalWrite(cs, HIGH);
}
void Adxl355::writeBytes(uint32_t addr, uint8_t *bytes, size_t len) {
return;
}
void Adxl355::updateTxyz() {
uint8_t bytes[12];
readBytes(Reg::TEMP2, bytes, 12);
int16_t temp = int16_t(bytes[1]) << 8 | bytes[2];
int32_t x = ((int32_t(bytes[3]) << 24) | (int32_t(bytes[4]) << 16) | int32_t(bytes[5] & 0xF0) << 8) >> 12;
int32_t y = ((int32_t(bytes[6]) << 24) | (int32_t(bytes[7]) << 16) | int32_t(bytes[8] & 0xF0) << 8) >> 12;
int32_t z = ((int32_t(bytes[9]) << 24) | (int32_t(bytes[10]) << 16) | int32_t(bytes[11] & 0xF0) << 8) >> 12;
this->temp = ((temp - 1852) / (-9.05)) + 25; // below is simplified version of this line
// this->temp = temp / -9.05 + 229.640883978;
this->x = float(x) / range.coef;
this->y = float(y) / range.coef;
this->z = float(z) / range.coef;
}
void Adxl355::updateXyz() {
uint8_t bytes[10];
readBytes(Reg::XDATA3, bytes, 10);
int32_t x = ((int32_t(bytes[1]) << 24) | (int32_t(bytes[2]) << 16) | int32_t(bytes[3] & 0xF0) << 8) >> 12;
int32_t y = ((int32_t(bytes[4]) << 24) | (int32_t(bytes[5]) << 16) | int32_t(bytes[6] & 0xF0) << 8) >> 12;
int32_t z = ((int32_t(bytes[7]) << 24) | (int32_t(bytes[8]) << 16) | int32_t(bytes[9] & 0xF0) << 8) >> 12;
this->x = float(x) / range.coef;
this->y = float(y) / range.coef;
this->z = float(z) / range.coef;
}
void Adxl355::updateT() {
uint8_t bytes[2];
readBytes(Reg::TEMP2, bytes, 2);
int16_t temp = int16_t(bytes[1]) << 8 | bytes[2];
this->temp = ((temp - 1852) / (-9.05)) + 25; // below is simplified version of this line
// this->temp = temp / -9.05 + 229.640883978;
}
} // namespace Adxl355
using namespace Adxl355;
#define CS PC5
Adxl355::Adxl355 adxl (&SPI, CS);
/**
* @brief Arduino setup function
*/
void setup()
{
pinMode(USER_BTN , INPUT);
notecard.begin();
Serial.begin(115200);
notecard.setDebugOutputStream(Serial);
Serial.println("Notecard running");
adxl.begin();
adxl.setRange(Range::_2G);
adxl.setHPF(HPF::NO_HPF);
adxl.setODR(ODR::ODR_2000_Hz);
adxl.wakeup();
Serial.println("IMU running");
J *req = notecard.newRequest("hub.set");
JAddStringToObject(req, "product", productUID);
JAddStringToObject(req, "mode", "continuous");
notecard.sendRequest(req);
req = notecard.newRequest("card.location.track");
JAddBoolToObject(req, "sync", true);
JAddBoolToObject(req, "heartbeat", true);
JAddNumberToObject(req, "hours", 12);
notecard.sendRequest(req);
if (EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME != 3) {
ei_printf("ERR: EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME should be equal to 3 (the 3 sensor axes)\n");
return;
}
}
/**
* @brief Return the sign of the number
*
* @param number
* @return int 1 if positive (or 0) -1 if negative
*/
float ei_get_sign(float number) {
return (number >= 0.0) ? 1.0 : -1.0;
}
/**
* @brief Get data and run inferencing
*
* @param[in] debug Get debug info if true
*/
void loop()
{
if(digitalRead(USER_BTN) == 0){
ei_printf("\nStarting inferencing in 120 seconds...\n");
delay(15000);
ei_printf("Sampling...\n");
// Allocate a buffer here for the values we'll read from the IMU
float buffer[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE] = { 0 };
for (size_t ix = 0; ix < EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE; ix += 3) {
// Determine the next tick (and then sleep later)
uint64_t next_tick = micros() + (EI_CLASSIFIER_INTERVAL_MS * 1000);
adxl.updateTxyz();
buffer[ix] = adxl.x * 100;
buffer[ix + 1] = adxl.y * 100;
buffer[ix + 2] = adxl.z * 100;
delayMicroseconds(next_tick - micros());
}
// Turn the raw buffer in a signal which we can the classify
signal_t signal;
int err = numpy::signal_from_buffer(buffer, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, &signal);
if (err != 0) {
ei_printf("Failed to create signal from buffer (%d)", err);
Serial.println("");
return;
}
// Run the classifier
ei_impulse_result_t result = { 0 };
err = run_classifier(&signal, &result, debug_nn);
if (err != EI_IMPULSE_OK) {
ei_printf("ERR: Failed to run classifier (%d)", err);
Serial.println("");
return;
}
// print the predictions
ei_printf("Predictions ");
ei_printf("(DSP: %d ms., Classification: %d ms., Anomaly: %d ms.)",
result.timing.dsp, result.timing.classification, result.timing.anomaly);
ei_printf(": \n");
Serial.println("");
for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
// ei_printf_float(" %s: %.5f\n", result.classification[ix].label, result.classification[ix].value);
ei_printf("%s:",result.classification[ix].label);
ei_printf_float(result.classification[ix].value);
Serial.println("");
}
//Send the data to twilio if it is a crash
if(result.classification[1].value > 0.8){
J *req = notecard.newRequest("note.add");
if (req != NULL)
{
JAddStringToObject(req, "file", "twilio.qo");
JAddBoolToObject(req, "sync", true);
J *body = JAddObjectToObject(req, "body");
if(body)
{
JAddStringToObject(body, "twilioBody", "Its a crash!");
}
}
notecard.sendRequest(req);
}
#if EI_CLASSIFIER_HAS_ANOMALY == 1
ei_printf_float(" anomaly score: %.3f\n", result.anomaly);
Serial.println("");
#endif
}
}
#if !defined(EI_CLASSIFIER_SENSOR) || EI_CLASSIFIER_SENSOR != EI_CLASSIFIER_SENSOR_ACCELEROMETER
#error "Invalid model for current sensor"
#endif
Comments