Neopixel ring Compass Road Safety Dog Jacket

Abandoned the waterproof sleeves idea and decided to do more sewing, not pvc/plastic welding. (from previous blog post)

My dog Baxter is an excellent model and his old coat is never used because he hates wearing it, so I gathered my Arduino Liliypad, compass sensor and other materials listed here:

Bill of Materials: waterproof.fzz

/Users/jtreg/gold/physical/waterproof.fzz

Friday, February 9 2018, 09:35:35
Assembly List
Label Part Type Properties
Part1 Lilypad Arduino Board type Lilypad Arduino
Part2 Seven-Segment LED Backpack 1.2 Inch Digits variant Red; part # 1270
Part3 16 NeoPixel Ring variant variant 1; part # 1463
Part4 FTDI Basic Programmer type Basic; voltage 5V
Real Time Clock ZS-042 RTC Module chip DS3231; variant variant 4
U1 HMC5883L package 16lpcc; axis 3; variant smd
U2 LIPO-2000mAh package lipo-2000; variant 2000mAh
Shopping List
Amount Part Type Properties
1 Lilypad Arduino Board type Lilypad Arduino
1 Seven-Segment LED Backpack 1.2 Inch Digits variant Red; part # 1270
1 16 NeoPixel Ring variant variant 1; part # 1463
1 FTDI Basic Programmer type Basic; voltage 5V
1 ZS-042 RTC Module chip DS3231; variant variant 4
1 HMC5883L package 16lpcc; axis 3; variant smd
1 LIPO-2000mAh package lipo-2000; variant 2000mAh
(I used a 2 x aaa battery as well)

Exported with Fritzing 0.9.3- http://fritzing.org

I tried out the compass code (other components plugged into breadboard, please ignore them!)

I have not yet integrated the real time clock into the project as I ran out of time. I will be usig the pixel ring to add hour, minute and second pixel to flash up.

Originally I planned to use a 7 segment display for the time and temperature
but I think the part I had was faulty. Additional functions could incorporate the temp display off the real time clock…

A little Evo-Stick on the end of electric thread stops it unravelling and helps thread through tiny component holes…

External USB power socket

LilyPad sewn in!

Power test

Added compass chip

Real time clock, battery. I ran out of electric thread so I used light wiring sewn down instead.

ready for walkies

My patient model. Extra waterproof to protect components in rain. Best results after dark!

 

Listing for Lilypad (work in progress)

/*

James Tregaskis

NeoPixel ring for dog jacket
—————————-
9th Feb 2018
This is code I used from two sources and merged them
I have not yet integrated the real time clock into the
project as I ran out of time. I will be usig the pixel
ring to add hour, minute and second pixel to flash up.
Originally I planned to use a 7 segment display for the time and temp
but I think the part I had was faulty.
Additional functions could incorporate the temp display off the real
time clock

Bill of Materials: waterproof.fzz

/Users/jtreg/gold/physical/waterproof.fzz

Friday, February 9 2018, 09:35:35
Assembly List
Label Part Type Properties
Part1 Lilypad Arduino Board type Lilypad Arduino
Part2 Seven-Segment LED Backpack 1.2 Inch Digits variant Red; part # 1270
Part3 16 NeoPixel Ring variant variant 1; part # 1463
Part4 FTDI Basic Programmer type Basic; voltage 5V
Real Time Clock ZS-042 RTC Module chip DS3231; variant variant 4
U1 HMC5883L package 16lpcc; axis 3; variant smd
U2 LIPO-2000mAh package lipo-2000; variant 2000mAh

Shopping List
Amount Part Type Properties
1 Lilypad Arduino Board type Lilypad Arduino
1 Seven-Segment LED Backpack 1.2 Inch Digits variant Red; part # 1270
1 16 NeoPixel Ring variant variant 1; part # 1463
1 FTDI Basic Programmer type Basic; voltage 5V
1 ZS-042 RTC Module chip DS3231; variant variant 4
1 HMC5883L package 16lpcc; axis 3; variant smd
1 LIPO-2000mAh package lipo-2000; variant 2000mAh
(I used a 2 x aaa battery as well)

Exported with Fritzing 0.9.3- http://fritzing.org

*/
/***************************************************************************
This is a library example for the HMC5883 magnentometer/compass

Designed specifically to work with the Adafruit HMC5883 Breakout
http://www.adafruit.com/products/1746

*** You will also need to install the Adafruit_Sensor library! ***

These displays use I2C to communicate, 2 pins are required to interface.

Adafruit invests time and resources providing this open source code,
please support Adafruit andopen-source hardware by purchasing products
from Adafruit!

Written by Kevin Townsend for Adafruit Industries with some heading example from
Love Electronics (loveelectronics.co.uk)

This program is free software: you can redistribute it and/or modify
it under the terms of the version 3 GNU General Public License as
published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

***************************************************************************/

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_HMC5883_U.h>
#include “ds3231.h”
#define BUFF_MAX 128
uint8_t time[8];
char recv[BUFF_MAX];
long previousMillis = 0;
//long interval = 1000;
unsigned int recv_size = 0;
unsigned long prev, interval = 5000;
boolean doFunkyThings = false;
/* Assign a unique ID to this sensor at the same time */
Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345);
#include <Adafruit_NeoPixel.h>

#define PIN 3

// Parameter 1 = number of pixels in strip
// Parameter 2 = pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic ‘v1’ (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(16, PIN, NEO_RGB + NEO_KHZ400);

int fixedHeadingDegrees; // Used to store Heading value
float headingDegrees = 0;//heading * 180 / M_PI;
void displaySensorDetails(void)
{
sensor_t sensor;
mag.getSensor(&sensor);
Serial.println(“————————————“);
Serial.print (“Sensor: “); Serial.println(sensor.name);
Serial.print (“Driver Ver: “); Serial.println(sensor.version);
Serial.print (“Unique ID: “); Serial.println(sensor.sensor_id);
Serial.print (“Max Value: “); Serial.print(sensor.max_value); Serial.println(” uT”);
Serial.print (“Min Value: “); Serial.print(sensor.min_value); Serial.println(” uT”);
Serial.print (“Resolution: “); Serial.print(sensor.resolution); Serial.println(” uT”);
Serial.println(“————————————“);
Serial.println(“”);
delay(500);
}

void setup(void)
{
Serial.begin(9600);
Serial.println(“HMC5883 Magnetometer Test”); Serial.println(“”);

/* Initialise the sensor */
if (!mag.begin())
{
/* There was a problem detecting the HMC5883 … check your connections */
Serial.println(“Ooops, no HMC5883 detected … Check your wiring!”);
while (1);
strip.begin();
strip.setBrightness(30); //adjust brightness here
strip.show(); // Initialize all pixels to ‘off’
/* Display some basic information on this sensor */

}
// clock stuff
DS3231_init(DS3231_INTCN);
memset(recv, 0, BUFF_MAX);
Serial.println(“GET time”);
//
strip.begin();
strip.setBrightness(30); //adjust brightness here
strip.show(); // Initialize all pixels to ‘off’
displaySensorDetails();
colorWipe(strip.Color(255, 0, 0), 0);
}

void loop(void)
{
unsigned long currentMillis = millis();
/* Get a new sensor event */
sensors_event_t event;
mag.getEvent(&event);

/* Display the results (magnetic vector values are in micro-Tesla (uT)) */
// Serial.print(“X: “); Serial.print(event.magnetic.x); Serial.print(” “);
// Serial.print(“Y: “); Serial.print(event.magnetic.y); Serial.print(” “);
// Serial.print(“Z: “); Serial.print(event.magnetic.z); Serial.print(” “); Serial.println(“uT”);

// Hold the module so that Z is pointing ‘up’ and you can measure the heading with x&y
// Calculate heading when the magnetometer is level, then correct for signs of axis.
float heading = atan2(event.magnetic.y, event.magnetic.x);

// Once you have your heading, you must then add your ‘Declination Angle’, which is the ‘Error’ of the magnetic field in your location.
// Find yours here: http://www.magnetic-declination.com/
// Mine is: -13* 2′ W, which is ~13 Degrees, or (which we need) 0.22 radians
// If you cannot find your Declination, comment out these two lines, your compass will be slightly off.
float declinationAngle = 0.22;
heading += declinationAngle;

// Correct for when signs are reversed.
if (heading < 0)
heading += 2 * PI;

// Check for wrap due to addition of declination.
if (heading > 2 * PI)
heading -= 2 * PI;

// Convert to degrees
float headingDegrees = heading * 180 / M_PI;

// To Fix rotation speed of HMC5883L Compass module
if (headingDegrees >= 1 && headingDegrees < 240)
{
fixedHeadingDegrees = map (headingDegrees * 100, 0, 239 * 100, 0, 179 * 100) / 100.00;
}
else {
if (headingDegrees >= 240)
{
fixedHeadingDegrees = map (headingDegrees * 100, 240 * 100, 360 * 100, 180 * 100, 360 * 100) / 100.00;
}
}
int headvalue = fixedHeadingDegrees / 18;
int ledtoheading = map(headvalue, 0, 15, 15, 0);

// Serial.print(“Heading (degrees): “); Serial.print(“ledtoheading : “); Serial.print(ledtoheading); Serial.println(headingDegrees);
if (currentMillis – previousMillis > interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
doFunkyThings = !doFunkyThings;
}
doClockStuffi.nLoop();
if (!doFunkyThings) {
funky();
}
else {
colorWipe(strip.Color(0, 0, 255), 0);

if (ledtoheading == 0) {
strip.setPixelColor(15, 255, 0, 50); //Red
strip.setPixelColor(0, 0, 255, 0); //Green
strip.setPixelColor(14, 0, 255, 0); //Green

}
else {
if (ledtoheading == 15) {
strip.setPixelColor(0, 255, 0, 50); //Red
strip.setPixelColor(15, 0, 255, 0); //Green
strip.setPixelColor(1, 0, 255, 0); //Green
}
else {
strip.setPixelColor(ledtoheading, 255, 0, 50); //Red
strip.setPixelColor(ledtoheading + 1, 0, 255, 0); //Green
strip.setPixelColor(ledtoheading – 1, 0, 255, 0); //Green

}
}
}

strip.setBrightness(50);
strip.show();
delay(100);
}
void colorWipe(uint32_t c, uint8_t wait) {
for (uint16_t i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, c);
strip.show();
delay(wait);
}
}
void doClockStuffinLoop() {
char in;
char buff[BUFF_MAX];
unsigned long now = millis();
struct ts t;

// show time once in a while
if ((now – prev > interval) && (Serial.available() <= 0)) {
DS3231_get(&t);

// there is a compile time option in the library to include unixtime support
#ifdef CONFIG_UNIXTIME
snprintf(buff, BUFF_MAX, “%d.%02d.%02d %02d:%02d:%02d %ld”, t.year,
t.mon, t.mday, t.hour, t.min, t.sec, t.unixtime);
#else
//Serial.println(“here it is..”);
snprintf(buff, BUFF_MAX, “%d.%02d.%02d %02d:%02d:%02d”, t.year,
t.mon, t.mday, t.hour, t.min, t.sec);
#endif

Serial.println(buff);
prev = now;
}

if (Serial.available() > 0) {
in = Serial.read();

if ((in == 10 || in == 13) && (recv_size > 0)) {
parse_cmd(recv, recv_size);
recv_size = 0;
recv[0] = 0;
} else if (in < 48 || in > 122) {
; // ignore ~[0-9A-Za-z] } else if (recv_size > BUFF_MAX – 2) { // drop lines that are too long
// drop
recv_size = 0;
recv[0] = 0;
} else if (recv_size < BUFF_MAX – 2) {
recv[recv_size] = in;
recv[recv_size + 1] = 0;
recv_size += 1;
}

}
}
void funky() {
// Some example procedures showing how to display to the pixels:
// colorWipe(strip.Color(255, 0, 0), 50); // Red
// colorWipe(strip.Color(0, 255, 0), 50); // Green
// colorWipe(strip.Color(0, 0, 255), 50); // Blue
rainbow(1);
rainbowCycle(1);
}

void rainbow(uint8_t wait) {
uint16_t i, j;

for (j = 0; j < 256; j++) {
for (i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel((i + j) & 255));
}
strip.show();
delay(wait);
}
}
// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
uint16_t i, j;

for (j = 0; j < 256 * 5; j++) { // 5 cycles of all colors on wheel
for (i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
}
strip.show();
delay(wait);
}
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r – g – b – back to r.
uint32_t Wheel(byte WheelPos) {
if (WheelPos < 85) {
return strip.Color(WheelPos * 3, 255 – WheelPos * 3, 0);
} else if (WheelPos < 170) {
WheelPos -= 85;
return strip.Color(255 – WheelPos * 3, 0, WheelPos * 3);
} else {
WheelPos -= 170;
return strip.Color(0, WheelPos * 3, 255 – WheelPos * 3);
}
}
void parse_cmd(char *cmd, int cmdsize)
{
uint8_t i;
uint8_t reg_val;
char buff[BUFF_MAX];
struct ts t;

//snprintf(buff, BUFF_MAX, “cmd was ‘%s’ %d\n”, cmd, cmdsize);
//Serial.print(buff);

// TssmmhhWDDMMYYYY aka set time
if (cmd[0] == 84 && cmdsize == 16) {
//T355720619112011
t.sec = inp2toi(cmd, 1);
t.min = inp2toi(cmd, 3);
t.hour = inp2toi(cmd, 5);
t.wday = cmd[7] – 48;
t.mday = inp2toi(cmd, 8);
t.mon = inp2toi(cmd, 10);
t.year = inp2toi(cmd, 12) * 100 + inp2toi(cmd, 14);
DS3231_set(t);
Serial.println(“OK”);
} else if (cmd[0] == 49 && cmdsize == 1) { // “1” get alarm 1
DS3231_get_a1(&buff[0], 59);
Serial.println(buff);
} else if (cmd[0] == 50 && cmdsize == 1) { // “2” get alarm 1
DS3231_get_a2(&buff[0], 59);
Serial.println(buff);
} else if (cmd[0] == 51 && cmdsize == 1) { // “3” get aging register
Serial.print(“aging reg is “);
Serial.println(DS3231_get_aging(), DEC);
} else if (cmd[0] == 65 && cmdsize == 9) { // “A” set alarm 1
DS3231_set_creg(DS3231_INTCN | DS3231_A1IE);
//ASSMMHHDD
for (i = 0; i < 4; i++) {
time[i] = (cmd[2 * i + 1] – 48) * 10 + cmd[2 * i + 2] – 48; // ss, mm, hh, dd
}
uint8_t flags[5] = { 0, 0, 0, 0, 0 };
DS3231_set_a1(time[0], time[1], time[2], time[3], flags);
DS3231_get_a1(&buff[0], 59);
Serial.println(buff);
} else if (cmd[0] == 66 && cmdsize == 7) { // “B” Set Alarm 2
DS3231_set_creg(DS3231_INTCN | DS3231_A2IE);
//BMMHHDD
for (i = 0; i < 4; i++) {
time[i] = (cmd[2 * i + 1] – 48) * 10 + cmd[2 * i + 2] – 48; // mm, hh, dd
}
uint8_t flags[5] = { 0, 0, 0, 0 };
DS3231_set_a2(time[0], time[1], time[2], flags);
DS3231_get_a2(&buff[0], 59);
Serial.println(buff);
} else if (cmd[0] == 67 && cmdsize == 1) { // “C” – get temperature register
Serial.print(“temperature reg is “);
Serial.println(DS3231_get_treg(), DEC);
} else if (cmd[0] == 68 && cmdsize == 1) { // “D” – reset status register alarm flags
reg_val = DS3231_get_sreg();
reg_val &= B11111100;
DS3231_set_sreg(reg_val);
} else if (cmd[0] == 70 && cmdsize == 1) { // “F” – custom fct
reg_val = DS3231_get_addr(0x5);
Serial.print(“orig “);
Serial.print(reg_val, DEC);
Serial.print(“month is “);
Serial.println(bcdtodec(reg_val & 0x1F), DEC);
} else if (cmd[0] == 71 && cmdsize == 1) { // “G” – set aging status register
DS3231_set_aging(0);
} else if (cmd[0] == 83 && cmdsize == 1) { // “S” – get status register
Serial.print(“status reg is “);
Serial.println(DS3231_get_sreg(), DEC);
} else {
Serial.print(“unknown command prefix “);
Serial.println(cmd[0]);
Serial.println(cmd[0], DEC);
}
}

 

Leave a Reply

Your email address will not be published. Required fields are marked *