├── Adafruit_GFX.cpp ├── Adafruit_GFX.h ├── ColorMemLCD.cpp ├── ColorMemLCD.h ├── DateStrings.cpp ├── PAH8002HRmon ├── examples │ └── PAH8002compileTest.ino ├── keywords.txt ├── library.properties └── src │ ├── PAH8002HRmon.cpp │ ├── PAH8002HRmon.h │ ├── cortex-m4 │ └── libheart.a │ ├── pah8series_api_c.h │ └── pah8series_data_c.h ├── PAH8002driver ├── PAH8002.h └── PAH8002.ino ├── README.md ├── SMAQ2_FW3.ino ├── SMAQ2_FW3.zip ├── Time.cpp ├── Time.h ├── TimeLib.h ├── gfxfont.h ├── glcdfont.c ├── hardware.zip ├── heartRate.ino ├── i2csoft.cpp ├── i2csoft.h ├── menue.ino ├── nrfutil.exe ├── remote.ino ├── tremor.ino └── utils.ino /Adafruit_GFX.h: -------------------------------------------------------------------------------- 1 | #ifndef _ADAFRUIT_GFX_H 2 | #define _ADAFRUIT_GFX_H 3 | 4 | #if ARDUINO >= 100 5 | #include "Arduino.h" 6 | #include "Print.h" 7 | #else 8 | #include "WProgram.h" 9 | #endif 10 | #include "gfxfont.h" 11 | 12 | /// A generic graphics superclass that can handle all sorts of drawing. At a minimum you can subclass and provide drawPixel(). At a maximum you can do a ton of overriding to optimize. Used for any/all Adafruit displays! 13 | class Adafruit_GFX : public Print { 14 | 15 | public: 16 | 17 | Adafruit_GFX(int16_t w, int16_t h); // Constructor 18 | 19 | // This MUST be defined by the subclass: 20 | virtual void drawPixel(int16_t x, int16_t y, uint16_t color) = 0; ///< Virtual drawPixel() function to draw to the screen/framebuffer/etc, must be overridden in subclass. @param x X coordinate. @param y Y coordinate. @param color 16-bit pixel color. 21 | 22 | // TRANSACTION API / CORE DRAW API 23 | // These MAY be overridden by the subclass to provide device-specific 24 | // optimized code. Otherwise 'generic' versions are used. 25 | virtual void startWrite(void); 26 | virtual void writePixel(int16_t x, int16_t y, uint16_t color); 27 | virtual void writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); 28 | virtual void writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); 29 | virtual void writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); 30 | virtual void writeLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color); 31 | virtual void endWrite(void); 32 | 33 | // CONTROL API 34 | // These MAY be overridden by the subclass to provide device-specific 35 | // optimized code. Otherwise 'generic' versions are used. 36 | virtual void setRotation(uint8_t r); 37 | virtual void invertDisplay(boolean i); 38 | 39 | // BASIC DRAW API 40 | // These MAY be overridden by the subclass to provide device-specific 41 | // optimized code. Otherwise 'generic' versions are used. 42 | virtual void 43 | // It's good to implement those, even if using transaction API 44 | drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color), 45 | drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color), 46 | fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color), 47 | fillScreen(uint16_t color), 48 | // Optional and probably not necessary to change 49 | drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color), 50 | drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); 51 | 52 | // These exist only with Adafruit_GFX (no subclass overrides) 53 | void 54 | drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color), 55 | drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, 56 | uint16_t color), 57 | fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color), 58 | fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, 59 | int16_t delta, uint16_t color), 60 | drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, 61 | int16_t x2, int16_t y2, uint16_t color), 62 | fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, 63 | int16_t x2, int16_t y2, uint16_t color), 64 | drawRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, 65 | int16_t radius, uint16_t color), 66 | fillRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, 67 | int16_t radius, uint16_t color), 68 | drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], 69 | int16_t w, int16_t h, uint16_t color), 70 | drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[], 71 | int16_t w, int16_t h, uint16_t color, uint16_t bg), 72 | drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, 73 | int16_t w, int16_t h, uint16_t color), 74 | drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, 75 | int16_t w, int16_t h, uint16_t color, uint16_t bg), 76 | drawXBitmap(int16_t x, int16_t y, const uint8_t bitmap[], 77 | int16_t w, int16_t h, uint16_t color), 78 | drawGrayscaleBitmap(int16_t x, int16_t y, const uint8_t bitmap[], 79 | int16_t w, int16_t h), 80 | drawGrayscaleBitmap(int16_t x, int16_t y, uint8_t *bitmap, 81 | int16_t w, int16_t h), 82 | drawGrayscaleBitmap(int16_t x, int16_t y, 83 | const uint8_t bitmap[], const uint8_t mask[], 84 | int16_t w, int16_t h), 85 | drawGrayscaleBitmap(int16_t x, int16_t y, 86 | uint8_t *bitmap, uint8_t *mask, int16_t w, int16_t h), 87 | drawRGBBitmap(int16_t x, int16_t y, const uint16_t bitmap[], 88 | int16_t w, int16_t h), 89 | drawRGBBitmap(int16_t x, int16_t y, uint16_t *bitmap, 90 | int16_t w, int16_t h), 91 | drawRGBBitmap(int16_t x, int16_t y, 92 | const uint16_t bitmap[], const uint8_t mask[], 93 | int16_t w, int16_t h), 94 | drawRGBBitmap(int16_t x, int16_t y, 95 | uint16_t *bitmap, uint8_t *mask, int16_t w, int16_t h), 96 | drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, 97 | uint16_t bg, uint8_t size), 98 | setCursor(int16_t x, int16_t y), 99 | setTextColor(uint16_t c), 100 | setTextColor(uint16_t c, uint16_t bg), 101 | setTextSize(uint8_t s), 102 | setTextWrap(boolean w), 103 | cp437(boolean x=true), 104 | setFont(const GFXfont *f = NULL), 105 | getTextBounds(const char *string, int16_t x, int16_t y, 106 | int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h), 107 | getTextBounds(const __FlashStringHelper *s, int16_t x, int16_t y, 108 | int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h), 109 | getTextBounds(const String &str, int16_t x, int16_t y, 110 | int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h); 111 | 112 | 113 | #if ARDUINO >= 100 114 | virtual size_t write(uint8_t); 115 | #else 116 | virtual void write(uint8_t); 117 | #endif 118 | 119 | int16_t height(void) const; 120 | int16_t width(void) const; 121 | 122 | uint8_t getRotation(void) const; 123 | 124 | // get current cursor position (get rotation safe maximum values, using: width() for x, height() for y) 125 | int16_t getCursorX(void) const; 126 | int16_t getCursorY(void) const; 127 | 128 | protected: 129 | void 130 | charBounds(char c, int16_t *x, int16_t *y, 131 | int16_t *minx, int16_t *miny, int16_t *maxx, int16_t *maxy); 132 | const int16_t 133 | WIDTH, ///< This is the 'raw' display width - never changes 134 | HEIGHT; ///< This is the 'raw' display height - never changes 135 | int16_t 136 | _width, ///< Display width as modified by current rotation 137 | _height, ///< Display height as modified by current rotation 138 | cursor_x, ///< x location to start print()ing text 139 | cursor_y; ///< y location to start print()ing text 140 | uint16_t 141 | textcolor, ///< 16-bit background color for print() 142 | textbgcolor; ///< 16-bit text color for print() 143 | uint8_t 144 | textsize, ///< Desired magnification of text to print() 145 | rotation; ///< Display rotation (0 thru 3) 146 | boolean 147 | wrap, ///< If set, 'wrap' text at right edge of display 148 | _cp437; ///< If set, use correct CP437 charset (default is off) 149 | GFXfont 150 | *gfxFont; ///< Pointer to special font 151 | }; 152 | 153 | 154 | /// A simple drawn button UI element 155 | class Adafruit_GFX_Button { 156 | 157 | public: 158 | Adafruit_GFX_Button(void); 159 | // "Classic" initButton() uses center & size 160 | void initButton(Adafruit_GFX *gfx, int16_t x, int16_t y, 161 | uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, 162 | uint16_t textcolor, char *label, uint8_t textsize); 163 | // New/alt initButton() uses upper-left corner & size 164 | void initButtonUL(Adafruit_GFX *gfx, int16_t x1, int16_t y1, 165 | uint16_t w, uint16_t h, uint16_t outline, uint16_t fill, 166 | uint16_t textcolor, char *label, uint8_t textsize); 167 | void drawButton(boolean inverted = false); 168 | boolean contains(int16_t x, int16_t y); 169 | 170 | void press(boolean p); 171 | boolean isPressed(); 172 | boolean justPressed(); 173 | boolean justReleased(); 174 | 175 | private: 176 | Adafruit_GFX *_gfx; 177 | int16_t _x1, _y1; // Coordinates of top-left corner 178 | uint16_t _w, _h; 179 | uint8_t _textsize; 180 | uint16_t _outlinecolor, _fillcolor, _textcolor; 181 | char _label[10]; 182 | 183 | boolean currstate, laststate; 184 | }; 185 | 186 | 187 | /// A GFX 1-bit canvas context for graphics 188 | class GFXcanvas1 : public Adafruit_GFX { 189 | public: 190 | GFXcanvas1(uint16_t w, uint16_t h); 191 | ~GFXcanvas1(void); 192 | void drawPixel(int16_t x, int16_t y, uint16_t color), 193 | fillScreen(uint16_t color); 194 | uint8_t *getBuffer(void); 195 | private: 196 | uint8_t *buffer; 197 | }; 198 | 199 | 200 | /// A GFX 8-bit canvas context for graphics 201 | class GFXcanvas8 : public Adafruit_GFX { 202 | public: 203 | GFXcanvas8(uint16_t w, uint16_t h); 204 | ~GFXcanvas8(void); 205 | void drawPixel(int16_t x, int16_t y, uint16_t color), 206 | fillScreen(uint16_t color), 207 | writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); 208 | 209 | uint8_t *getBuffer(void); 210 | private: 211 | uint8_t *buffer; 212 | }; 213 | 214 | 215 | /// A GFX 16-bit canvas context for graphics 216 | class GFXcanvas16 : public Adafruit_GFX { 217 | public: 218 | GFXcanvas16(uint16_t w, uint16_t h); 219 | ~GFXcanvas16(void); 220 | void drawPixel(int16_t x, int16_t y, uint16_t color), 221 | fillScreen(uint16_t color); 222 | uint16_t *getBuffer(void); 223 | private: 224 | uint16_t *buffer; 225 | }; 226 | 227 | #endif // _ADAFRUIT_GFX_H 228 | -------------------------------------------------------------------------------- /ColorMemLCD.cpp: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | Library for the LPM013M126A 176×176 Japan Display Color memory LCD 3 | inside the SMA-Q2 / SMA-TIME smartwatch. 4 | 5 | The ColorMemLCD library is based on the mbed library for the JDI color memory LCD LPM013M126A 6 | by Tadayuki Okamoto, originally released under the MIT License, and the Adafruit SHARP Memory display library. 7 | 8 | Uses hardware SPI on the nrf52832 and an external signal for the EXTCOMIN (P0.06). 9 | By default the EXTMODE pin of the display is set HIGH via a 10k resistor, 10 | which means that the display expects toggling this pin from time to time (at least 11 | once per minute, see datasheet). If EXTMODE is set LOW (use a soldering iron to move 12 | the resistor, there is an unpopulated place for that), the "toggle" has to be done in 13 | software, like in the Adaruit library. 14 | A. Jordan 2018. 15 | *********************************************************************/ 16 | 17 | #include "ColorMemLCD.h" 18 | 19 | 20 | /************************************************************************** 21 | Sharp Memory Display Connector 22 | ----------------------------------------------------------------------- 23 | Pin Function Notes 24 | === ============== =============================== 25 | 1 VIN 3.3-5.0V (into LDO supply) 26 | 2 3V3 3.3V out 27 | 3 GND 28 | 4 SCLK Serial Clock 29 | 5 MOSI Serial Data Input 30 | 6 CS Serial Chip Select 31 | 9 EXTMODE COM Inversion Select (Low = SW clock/serial) 32 | 7 EXTCOMIN External COM Inversion Signal 33 | 8 DISP Display On(High)/Off(Low) 34 | 35 | **************************************************************************/ 36 | 37 | //new stuff 38 | /** @def 39 | * LCD_Color SPI commands 40 | */ 41 | #define LCD_COLOR_CMD_UPDATE (0x90) //!< Update Mode (4bit Data Mode) 42 | #define LCD_COLOR_CMD_ALL_CLEAR (0x20) //!< All Clear Mode 43 | #define LCD_COLOR_CMD_NO_UPDATE (0x00) //!< No Update Mode 44 | #define LCD_COLOR_CMD_BLINKING_WHITE (0x18) //!< Display Blinking Color Mode (White) 45 | #define LCD_COLOR_CMD_BLINKING_BLACK (0x10) //!< Display Blinking Color Mode (Black) 46 | #define LCD_COLOR_CMD_INVERSION (0x14) //!< Display Inversion Mode 47 | 48 | 49 | /* ************* */ 50 | /* CONSTRUCTORS */ 51 | /* ************* */ 52 | ColorMemLCD::ColorMemLCD(uint8_t clk, uint8_t mosi, uint8_t ss, uint8_t extcomin) : 53 | Adafruit_GFX(LCD_DEVICE_WIDTH, LCD_DEVICE_HEIGHT) { 54 | _clk = clk; 55 | _mosi = mosi; 56 | _ss = ss; 57 | _extcomin= extcomin; 58 | // Set pin state before direction to make sure they start this way (no glitching) 59 | digitalWrite(_ss, HIGH); 60 | pinMode(_ss, OUTPUT); 61 | pinMode(_extcomin, OUTPUT); 62 | 63 | /* initialize variables */ 64 | char_x = 0; 65 | char_y = 0; 66 | trans_mode = LCD_TRANSMODE_OPAQUE; 67 | window_x = 0; 68 | window_y = 0; 69 | window_w = LCD_DISP_WIDTH; 70 | window_h = LCD_DEVICE_HEIGHT; //LCD_DISP_HEIGHT_MAX_BUF; 71 | 72 | polarity = 0; 73 | blink_cmd = 0x00; 74 | extcomin_stat = 0; 75 | digitalWrite(_extcomin, extcomin_stat); 76 | /* set default color */ 77 | //foreground( LCD_COLOR_WHITE ); 78 | //background( LCD_COLOR_BLACK ); 79 | 80 | /* initialize temporary buffer */ 81 | memset( &cmd_buf[0], 0, sizeof(cmd_buf) ); 82 | memset( &disp_buf[0], (char)( (_background & 0x0F ) | ( (_background & 0x0F ) << 4 ) ), sizeof(disp_buf) ); 83 | memset( &file_buf[0], 0, sizeof(file_buf) ); 84 | 85 | /* display turn ON */ 86 | //command_AllClear(); 87 | //_disp = 1; 88 | clearDisplay(); 89 | 90 | #ifdef __AVR__ 91 | digitalWrite(_clk, LOW); 92 | digitalWrite(_mosi, HIGH); 93 | 94 | pinMode(_clk, OUTPUT); 95 | pinMode(_mosi, OUTPUT); 96 | 97 | clkport = portOutputRegister(digitalPinToPort(_clk)); 98 | clkpinmask = digitalPinToBitMask(_clk); 99 | dataport = portOutputRegister(digitalPinToPort(_mosi)); 100 | datapinmask = digitalPinToBitMask(_mosi); 101 | #endif 102 | 103 | 104 | } 105 | 106 | /** Set a pixel on the window memory */ 107 | void ColorMemLCD::drawPixel( int16_t x, int16_t y, uint16_t color ) 108 | { 109 | if( ( window_x > x )|| 110 | ( ( window_x + window_w ) <= x )|| 111 | ( window_y > y )|| 112 | ( ( window_y + window_h ) <= y ) ) { 113 | /* out of display buffer */ 114 | return; 115 | } 116 | 117 | if( ( x % 2 ) == 0 ) { 118 | disp_buf[ ( (window_w / 2) * ( y - window_y ) ) + ( ( x - window_x ) / 2 ) ] &= 0x0F; 119 | disp_buf[ ( (window_w / 2) * ( y - window_y ) ) + ( ( x - window_x ) / 2 ) ] |= ( ( color & 0x0F ) << 4 ); 120 | } 121 | else { 122 | disp_buf[ ( (window_w / 2) * ( y - window_y ) ) + ( ( x - window_x ) / 2 ) ] &= 0xF0; 123 | disp_buf[ ( (window_w / 2) * ( y - window_y ) ) + ( ( x - window_x ) / 2 ) ] |= ( ( color & 0x0F ) ); 124 | } 125 | } 126 | 127 | 128 | /** Set a pixel - for transrucent mode */ 129 | 130 | // void ColorMemLCD::pixel_alpha( int x, int y, int color ) 131 | // { 132 | // if( ( window_x > x )|| 133 | // ( ( window_x + window_w ) <= x )|| 134 | // ( window_y > y )|| 135 | // ( ( window_y + window_h ) <= y ) ) { 136 | // /* out of display buffer */ 137 | // return; 138 | // } 139 | 140 | // if( ( x % 2 ) == 0 ) { 141 | // disp_buf[ ( (window_w / 2) * ( y - window_y ) ) + ( ( x - window_x ) / 2 ) ] &= ( ( ( color & 0x0F ) << 4 ) | 0x0F ); 142 | // } 143 | // else { 144 | // disp_buf[ ( (window_w / 2) * ( y - window_y ) ) + ( ( x - window_x ) / 2 ) ] &= ( ( ( color & 0x0F ) ) | 0xF0 ); 145 | // } 146 | // } 147 | 148 | 149 | /** Fill the window memory with background color */ 150 | void ColorMemLCD::cls( void ) 151 | { 152 | memset( &disp_buf[0], (char)( (_background & 0x0F ) | ( (_background & 0x0F ) << 4 ) ), sizeof(disp_buf) ); 153 | } 154 | 155 | void ColorMemLCD::begin() { 156 | //setRotation(2); 157 | 158 | #ifndef __AVR__ 159 | SPI.begin(); // 160 | //SPI.setClockDivider(SPI_CLOCK_DIV8); //set SPI to 8Mhz 161 | #endif 162 | } 163 | 164 | /** set transpalent effect */ 165 | void ColorMemLCD::setTransMode( char mode ) 166 | { 167 | trans_mode = mode; 168 | } 169 | 170 | /** Transfer to the LCD from diaply buffer */ 171 | void ColorMemLCD::refresh() 172 | { 173 | int32_t i; 174 | int copy_width; 175 | //TOGGLE_VCOM; 176 | if( window_x + window_w < LCD_DISP_WIDTH ) { 177 | copy_width = (window_w / 2); 178 | } 179 | else { 180 | copy_width = ( ( LCD_DISP_WIDTH - window_x ) / 2 ); 181 | } 182 | 183 | for( i = 0 ; i < window_h ; i++ ) { 184 | 185 | if( window_y + i > LCD_DISP_HEIGHT ){ 186 | /* out of window system */ 187 | break; 188 | } 189 | 190 | /* initialize command buffer */ 191 | memset( &cmd_buf[0], (char)( (_background & 0x0F ) | ( (_background & 0x0F ) << 4 ) ), sizeof(cmd_buf) ); 192 | 193 | /* copy to command bufffer */ 194 | memcpy( &cmd_buf[(window_x/2)], &disp_buf[ (window_w / 2) * i ], copy_width ); 195 | 196 | /* send cmaoond request */ 197 | sendLineCommand( &cmd_buf[0], window_y + i ); 198 | } 199 | extcomin_stat=!extcomin_stat; 200 | digitalWrite(_extcomin, extcomin_stat); //toggle extcomin 201 | } 202 | 203 | 204 | /** send data packet */ 205 | void ColorMemLCD::sendLineCommand( char* line_cmd, int line ) 206 | { 207 | int32_t j; 208 | 209 | if( ( line < 0 )|| 210 | ( line >= LCD_DEVICE_HEIGHT ) ) { 211 | /* out of device size */ 212 | return; 213 | } 214 | 215 | delayMicroseconds(6); 216 | digitalWrite(_ss, HIGH); 217 | delayMicroseconds(6); 218 | sendbyte( LCD_COLOR_CMD_UPDATE | ( polarity << 6 ) ); // Command 219 | sendbyte( line + 1 ); // line 220 | 221 | for( j = 0 ; j < (LCD_DISP_WIDTH/2) ; j++ ) { 222 | if( j >= (LCD_DEVICE_WIDTH/2) ) { 223 | /* out of device size */ 224 | break; 225 | } 226 | sendbyte(line_cmd[j]); // data 227 | } 228 | for( ; j < (LCD_DEVICE_WIDTH/2) ; j++ ) { 229 | /* padding to device size */ 230 | sendbyteLSB( 0x00 ); 231 | } 232 | 233 | sendbyteLSB( 0x00 ); 234 | sendbyteLSB( 0x00 ); 235 | delayMicroseconds(6); 236 | digitalWrite(_ss, LOW); 237 | } 238 | 239 | 240 | /** Command to blink */ 241 | void ColorMemLCD::setBlinkMode( char mode ) 242 | { 243 | switch( mode ) { 244 | case LCD_BLINKMODE_NONE: 245 | /* Blinking None */ 246 | blink_cmd = LCD_COLOR_CMD_NO_UPDATE; 247 | break; 248 | case LCD_BLINKMODE_WHITE: 249 | /* Blinking White */ 250 | blink_cmd = LCD_COLOR_CMD_BLINKING_WHITE; 251 | break; 252 | case LCD_BLINKMODE_BLACK: 253 | /* Blinking Black */ 254 | blink_cmd = LCD_COLOR_CMD_BLINKING_BLACK; 255 | break; 256 | case LCD_BLINKMODE_INVERSE: 257 | /* Inversion Mode */ 258 | blink_cmd = LCD_COLOR_CMD_INVERSION; 259 | break; 260 | default: 261 | /* No Update */ 262 | blink_cmd = LCD_COLOR_CMD_NO_UPDATE; 263 | break; 264 | } 265 | 266 | digitalWrite(_ss, HIGH); 267 | delayMicroseconds(6); 268 | sendbyte( blink_cmd | ( polarity << 6 )); 269 | sendbyteLSB( 0x00 ); 270 | delayMicroseconds(6); 271 | digitalWrite(_ss, LOW); 272 | } 273 | /* *************** */ 274 | /* PRIVATE METHODS */ 275 | /* *************** */ 276 | 277 | 278 | /**************************************************************************/ 279 | /*! 280 | @brief Sends a single byte in (pseudo)-SPI. 281 | */ 282 | /**************************************************************************/ 283 | void ColorMemLCD::sendbyte(uint8_t data) 284 | { 285 | #ifdef __AVR__ 286 | uint8_t i = 0; 287 | 288 | // LCD expects LSB first 289 | for (i=0; i<8; i++) 290 | { 291 | // Make sure clock starts low 292 | //digitalWrite(_clk, LOW); 293 | *clkport &= ~clkpinmask; 294 | if (data & 0x80) 295 | //digitalWrite(_mosi, HIGH); 296 | *dataport |= datapinmask; 297 | else 298 | //digitalWrite(_mosi, LOW); 299 | *dataport &= ~datapinmask; 300 | 301 | // Clock is active high 302 | //digitalWrite(_clk, HIGH); 303 | *clkport |= clkpinmask; 304 | data <<= 1; 305 | } 306 | // Make sure clock ends low 307 | //digitalWrite(_clk, LOW); 308 | *clkport &= ~clkpinmask; 309 | #else 310 | SPI.setBitOrder(MSBFIRST); 311 | SPI.transfer(data); 312 | #endif 313 | } 314 | 315 | void ColorMemLCD::sendbyteLSB(uint8_t data) 316 | { 317 | #ifdef __AVR__ 318 | uint8_t i = 0; 319 | 320 | // LCD expects LSB first 321 | for (i=0; i<8; i++) 322 | { 323 | // Make sure clock starts low 324 | //digitalWrite(_clk, LOW); 325 | *clkport &= ~clkpinmask; 326 | if (data & 0x01) 327 | //digitalWrite(_mosi, HIGH); 328 | *dataport |= datapinmask; 329 | else 330 | //digitalWrite(_mosi, LOW); 331 | *dataport &= ~datapinmask; 332 | // Clock is active high 333 | //digitalWrite(_clk, HIGH); 334 | *clkport |= clkpinmask; 335 | data >>= 1; 336 | } 337 | // Make sure clock ends low 338 | //digitalWrite(_clk, LOW); 339 | *clkport &= ~clkpinmask; 340 | #else 341 | SPI.setBitOrder(LSBFIRST); 342 | SPI.transfer(data); 343 | #endif 344 | } 345 | 346 | 347 | // 1<= 100 19 | #include "Arduino.h" 20 | #else 21 | #include "WProgram.h" 22 | #endif 23 | 24 | #include "Adafruit_GFX.h" 25 | #ifdef __AVR 26 | #include 27 | #elif defined(ESP8266) 28 | #include 29 | #endif 30 | 31 | #ifndef __AVR__ 32 | #include 33 | #endif 34 | 35 | 36 | //new stuff 37 | /** @def 38 | * Device define 39 | */ 40 | #define LCD_DEVICE_WIDTH (176) 41 | #define LCD_DEVICE_HEIGHT (176) 42 | 43 | /** @def 44 | * window system define 45 | */ 46 | #define LCD_DISP_WIDTH (176) 47 | #define LCD_DISP_HEIGHT (176) 48 | #define LCD_DISP_HEIGHT_MAX_BUF (44) 49 | 50 | 51 | /** @def 52 | * some RGB color definitions 53 | */ 54 | /* R, G, B */ 55 | #define LCD_COLOR_BLACK (0x00) /* 0 0 0 0 */ 56 | #define LCD_COLOR_BLUE (0x02) /* 0 0 1 0 */ 57 | #define LCD_COLOR_GREEN (0x04) /* 0 1 0 0 */ 58 | #define LCD_COLOR_CYAN (0x06) /* 0 1 1 0 */ 59 | #define LCD_COLOR_RED (0x08) /* 1 0 0 0 */ 60 | #define LCD_COLOR_MAGENTA (0x0a) /* 1 0 1 0 */ 61 | #define LCD_COLOR_YELLOW (0x0c) /* 1 1 0 0 */ 62 | #define LCD_COLOR_WHITE (0x0e) /* 1 1 1 0 */ 63 | 64 | /** @def 65 | * ID for setTransMode 66 | */ 67 | #define LCD_TRANSMODE_OPAQUE (0x00) //!< BackGroud is Opaque 68 | #define LCD_TRANSMODE_TRANSPARENT (0x01) //!< BackGroud is Transparent 69 | #define LCD_TRANSMODE_TRANSLUCENT (0x02) //!< BackGroud is Translucent 70 | 71 | /** @def 72 | *ID for setBlinkMode 73 | */ 74 | #define LCD_BLINKMODE_NONE (0x00) //!< Blinking None 75 | #define LCD_BLINKMODE_WHITE (0x01) //!< Blinking White 76 | #define LCD_BLINKMODE_BLACK (0x02) //!< Blinking Black 77 | #define LCD_BLINKMODE_INVERSE (0x03) //!< Inversion Mode 78 | 79 | /** A class for Color Memory LCD Library 80 | */ 81 | 82 | class ColorMemLCD : public Adafruit_GFX { 83 | public: 84 | ColorMemLCD(uint8_t clk, uint8_t mosi, uint8_t ss, uint8_t extcomin); 85 | void begin(void); 86 | void drawPixel(int16_t x, int16_t y, uint16_t color); 87 | 88 | void clearDisplay(); 89 | void refresh(void); 90 | void setBlinkMode( char mode ); 91 | void setTransMode( char mode ); 92 | void cls( void ); 93 | 94 | private: 95 | uint8_t _ss, _clk, _mosi, _extcomin; 96 | volatile uint8_t *dataport, *clkport; 97 | uint8_t _sharpmem_vcom, datapinmask, clkpinmask; 98 | 99 | /* polarity variable */ 100 | char polarity; 101 | char blink_cmd; 102 | bool extcomin_stat; 103 | 104 | /* trans mode variable */ 105 | char trans_mode; 106 | 107 | /* data for character variable */ 108 | unsigned char* font; 109 | int char_x; 110 | int char_y; 111 | 112 | /* window variable */ 113 | int window_x; 114 | int window_y; 115 | int window_w; 116 | int window_h; 117 | uint16_t _foreground; 118 | uint16_t _background = LCD_COLOR_WHITE; 119 | 120 | /* temporary buffer */ 121 | char cmd_buf[LCD_DISP_WIDTH/2]; /* for sending command */ 122 | //char disp_buf[(LCD_DISP_WIDTH/2)*LCD_DISP_HEIGHT_MAX_BUF]; /* display buffer */ 123 | char disp_buf[(LCD_DISP_WIDTH/2)*LCD_DISP_HEIGHT]; 124 | char file_buf[118]; 125 | 126 | void sendbyte(uint8_t data); 127 | void sendbyteLSB(uint8_t data); 128 | void sendLineCommand( char* line_cmd, int line ); 129 | 130 | }; 131 | -------------------------------------------------------------------------------- /DateStrings.cpp: -------------------------------------------------------------------------------- 1 | /* DateStrings.cpp 2 | * Definitions for date strings for use with the Time library 3 | * 4 | * Updated for Arduino 1.5.7 18 July 2014 5 | * 6 | * No memory is consumed in the sketch if your code does not call any of the string methods 7 | * You can change the text of the strings, make sure the short strings are each exactly 3 characters 8 | * the long strings can be any length up to the constant dt_MAX_STRING_LEN defined in TimeLib.h 9 | * 10 | */ 11 | 12 | #if defined(__AVR__) 13 | #include 14 | #else 15 | // for compatiblity with Arduino Due and Teensy 3.0 and maybe others? 16 | #define PROGMEM 17 | #define PGM_P const char * 18 | #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) 19 | #define pgm_read_word(addr) (*(const unsigned char **)(addr)) 20 | #define strcpy_P(dest, src) strcpy((dest), (src)) 21 | #endif 22 | #include // for strcpy_P or strcpy 23 | #include "TimeLib.h" 24 | 25 | // the short strings for each day or month must be exactly dt_SHORT_STR_LEN 26 | #define dt_SHORT_STR_LEN 3 // the length of short strings 27 | 28 | static char buffer[dt_MAX_STRING_LEN+1]; // must be big enough for longest string and the terminating null 29 | 30 | const char monthStr0[] PROGMEM = ""; 31 | const char monthStr1[] PROGMEM = "January"; 32 | const char monthStr2[] PROGMEM = "February"; 33 | const char monthStr3[] PROGMEM = "March"; 34 | const char monthStr4[] PROGMEM = "April"; 35 | const char monthStr5[] PROGMEM = "May"; 36 | const char monthStr6[] PROGMEM = "June"; 37 | const char monthStr7[] PROGMEM = "July"; 38 | const char monthStr8[] PROGMEM = "August"; 39 | const char monthStr9[] PROGMEM = "September"; 40 | const char monthStr10[] PROGMEM = "October"; 41 | const char monthStr11[] PROGMEM = "November"; 42 | const char monthStr12[] PROGMEM = "December"; 43 | 44 | const PROGMEM char * const PROGMEM monthNames_P[] = 45 | { 46 | monthStr0,monthStr1,monthStr2,monthStr3,monthStr4,monthStr5,monthStr6, 47 | monthStr7,monthStr8,monthStr9,monthStr10,monthStr11,monthStr12 48 | }; 49 | 50 | const char monthShortNames_P[] PROGMEM = "ErrJanFebMarAprMayJunJulAugSepOctNovDec"; 51 | 52 | const char dayStr0[] PROGMEM = "Err"; 53 | const char dayStr1[] PROGMEM = "Sunday"; 54 | const char dayStr2[] PROGMEM = "Monday"; 55 | const char dayStr3[] PROGMEM = "Tuesday"; 56 | const char dayStr4[] PROGMEM = "Wednesday"; 57 | const char dayStr5[] PROGMEM = "Thursday"; 58 | const char dayStr6[] PROGMEM = "Friday"; 59 | const char dayStr7[] PROGMEM = "Saturday"; 60 | 61 | const PROGMEM char * const PROGMEM dayNames_P[] = 62 | { 63 | dayStr0,dayStr1,dayStr2,dayStr3,dayStr4,dayStr5,dayStr6,dayStr7 64 | }; 65 | 66 | const char dayShortNames_P[] PROGMEM = "ErrSunMonTueWedThuFriSat"; 67 | 68 | /* functions to return date strings */ 69 | 70 | char* monthStr(uint8_t month) 71 | { 72 | strcpy_P(buffer, (PGM_P)pgm_read_word(&(monthNames_P[month]))); 73 | return buffer; 74 | } 75 | 76 | char* monthShortStr(uint8_t month) 77 | { 78 | for (int i=0; i < dt_SHORT_STR_LEN; i++) 79 | buffer[i] = pgm_read_byte(&(monthShortNames_P[i+ (month*dt_SHORT_STR_LEN)])); 80 | buffer[dt_SHORT_STR_LEN] = 0; 81 | return buffer; 82 | } 83 | 84 | char* dayStr(uint8_t day) 85 | { 86 | strcpy_P(buffer, (PGM_P)pgm_read_word(&(dayNames_P[day]))); 87 | return buffer; 88 | } 89 | 90 | char* dayShortStr(uint8_t day) 91 | { 92 | uint8_t index = day*dt_SHORT_STR_LEN; 93 | for (int i=0; i < dt_SHORT_STR_LEN; i++) 94 | buffer[i] = pgm_read_byte(&(dayShortNames_P[index + i])); 95 | buffer[dt_SHORT_STR_LEN] = 0; 96 | return buffer; 97 | } 98 | -------------------------------------------------------------------------------- /PAH8002HRmon/examples/PAH8002compileTest.ino: -------------------------------------------------------------------------------- 1 | include 2 | 3 | PAH8002HRmon HRsensor; 4 | 5 | void setup(){ 6 | uint32_t version= HRsensor.getVersion(); 7 | } 8 | 9 | void loop(){ 10 | 11 | } 12 | 13 | -------------------------------------------------------------------------------- /PAH8002HRmon/keywords.txt: -------------------------------------------------------------------------------- 1 | ######################################### 2 | # Syntax Coloring Map For PAH8002HRmon 3 | ######################################### 4 | 5 | ######################################### 6 | # Datatypes and Includes (KEYWORD1) 7 | ######################################### 8 | 9 | 10 | 11 | ########################################### 12 | # Methods and Functions (KEYWORD2) 13 | ########################################### 14 | 15 | 16 | 17 | ########################################### 18 | # Constants (LITERAL1) 19 | ########################################### 20 | 21 | #Common 22 | 23 | 24 | 25 | #Sensor-specific 26 | 27 | -------------------------------------------------------------------------------- /PAH8002HRmon/library.properties: -------------------------------------------------------------------------------- 1 | name=PAH8002HRmon 2 | version=1.0 3 | author=xxx 4 | maintainer=xxx 5 | sentence=Arduino library for the precompiled PixArt HR sensor libs 6 | paragraph=PAH8002 variant for getting the SMA-Q2 smartwatch HR sensor running, in platform.txt add compiler.libraries.ldflags= somewhere and the following in the line with combine.pattern (between {object_files} and -Wl): {compiler.libraries.ldflags} 7 | category=Sensors 8 | url=https://null.null 9 | architectures=nRF5 10 | includes=PAH8002HRmon.h 11 | precompiled=true 12 | ldflags=-lheart 13 | -------------------------------------------------------------------------------- /PAH8002HRmon/src/PAH8002HRmon.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Arduino Library for the PixArt PAH8002 HR sensor 3 | based on precompiled library for nrf52 4 | 5 | */ 6 | 7 | 8 | #include "PAH8002HRmon.h" 9 | 10 | extern "C" {float __hardfp_sqrtf(float f) {return sqrtf(f);} } 11 | 12 | PAH8002HRmon::PAH8002HRmon() 13 | { 14 | 15 | 16 | } 17 | 18 | void PAH8002HRmon::begin(void){ 19 | 20 | 21 | 22 | } 23 | 24 | uint32_t PAH8002HRmon::getVersion(void) 25 | { 26 | return pah8series_version(); 27 | } -------------------------------------------------------------------------------- /PAH8002HRmon/src/PAH8002HRmon.h: -------------------------------------------------------------------------------- 1 | /* 2 | Arduino Library for the PixArt PAH8002 HR sensor 3 | based on precompiled library for nrf52 4 | 5 | */ 6 | #ifndef PAH8002HRmon_h 7 | #define PAH8002HRmon_h 8 | 9 | #include "Arduino.h" 10 | #include "pah8series_api_c.h" 11 | #include "pah8series_data_c.h" 12 | 13 | 14 | class PAH8002HRmon 15 | { 16 | public: 17 | PAH8002HRmon(); 18 | void begin (void); 19 | uint32_t getVersion(void); 20 | 21 | 22 | 23 | }; 24 | 25 | #endif -------------------------------------------------------------------------------- /PAH8002HRmon/src/cortex-m4/libheart.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigCorvus/SMA-Q2-Firmware-3/3ab7506ee2e191e48584a77ff89700214d9b11ac/PAH8002HRmon/src/cortex-m4/libheart.a -------------------------------------------------------------------------------- /PAH8002HRmon/src/pah8series_api_c.h: -------------------------------------------------------------------------------- 1 | #ifndef __PAH8SERIES_API_C_H__ 2 | #define __PAH8SERIES_API_C_H__ 3 | 4 | 5 | #include "pah8series_data_c.h" 6 | 7 | 8 | #if defined(WIN32) && !defined(PXIALG_STATIC_LIB) 9 | # ifdef PXIALG_EXPORTS 10 | # define PXIALG_API __declspec(dllexport) 11 | # else 12 | # define PXIALG_API __declspec(dllimport) 13 | # endif 14 | #else 15 | # define PXIALG_API 16 | #endif // WIN32 17 | 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif // __cplusplus 22 | 23 | 24 | PXIALG_API uint32_t pah8series_version(void); 25 | PXIALG_API uint32_t pah8series_query_open_size(void); 26 | PXIALG_API uint8_t pah8series_open(void *pBuffer); 27 | PXIALG_API uint8_t pah8series_close(void); 28 | PXIALG_API uint8_t pah8series_reset(void); 29 | PXIALG_API uint8_t pah8series_set_param(pah8series_param_idx_t idx, float p1); 30 | PXIALG_API uint8_t pah8series_get_param(pah8series_param_idx_t idx, float *p1); 31 | PXIALG_API uint8_t pah8series_get_hr(float *hr); 32 | PXIALG_API uint8_t pah8series_get_hr_trust_level(int *hr_trust_level); 33 | PXIALG_API uint8_t pah8series_entrance(pah8series_data_t *pah8series_data); 34 | PXIALG_API uint8_t pah8series_set_data(pah8series_data_t *pah8series_data); 35 | PXIALG_API uint8_t pah8series_process(uint8_t *is_finished); 36 | PXIALG_API uint8_t pah8series_get_signal_grade(int16_t *grade); 37 | PXIALG_API uint8_t pah8series_get_display_buffer(int32_t **buffer1, int32_t **buffer2, int32_t *size); 38 | PXIALG_API uint8_t pah8series_get_signal_level(int ch, uint8_t *signal_level); 39 | PXIALG_API uint8_t pah8series_get_rms_gs(int32_t *rms_gs); 40 | PXIALG_API uint8_t pah8series_get_motion_flag(uint8_t *motion_flag); 41 | PXIALG_API uint8_t pah8series_get_wear_index(uint32_t expo_time[3], uint8_t led_dac[3], uint8_t checkRaw, int *wear_index); 42 | PXIALG_API uint8_t pah8series_get_object_flag(uint8_t *object_flag); 43 | 44 | #ifdef __cplusplus 45 | } 46 | #endif // __cplusplus 47 | 48 | 49 | #endif 50 | 51 | -------------------------------------------------------------------------------- /PAH8002HRmon/src/pah8series_data_c.h: -------------------------------------------------------------------------------- 1 | #ifndef __PAH8SERIES_DATA_C_H__ 2 | #define __PAH8SERIES_DATA_C_H__ 3 | 4 | #include 5 | 6 | typedef struct { 7 | uint8_t frame_count; 8 | uint32_t time; 9 | uint8_t touch_flag; 10 | uint32_t nf_ppg_channel; 11 | uint32_t nf_ppg_per_channel; 12 | int32_t *ppg_data; 13 | uint32_t nf_mems; 14 | int16_t *mems_data; 15 | } pah8series_data_t; 16 | 17 | typedef enum { 18 | PAH8SERIES_PARAM_IDX_SAMPLE_RATE = 0, 19 | PAH8SERIES_PARAM_IDX_GSENSOR_MODE, 20 | PAH8SERIES_PARAM_IDX_PPG_CH_NUM, 21 | PAH8SERIES_PARAM_IDX_HAS_IR_CH, 22 | PAH8SERIES_PARAM_IDX_SIGNAL_GRADE, 23 | PAH8SERIES_PARAM_IDX_IIR_SMOOTH_PRED, 24 | PAH8SERIES_PARAM_IDX_IIR_LP, 25 | PAH8SERIES_PARAM_IDX_FIRST_HR_MULTIPLE_OFFSET_MEMS_THR, 26 | PAH8SERIES_PARAM_IDX_FIRST_HR_IN_MOTION_FLAG, 27 | PAH8SERIES_PARAM_IDX_FIRST_HR_MEMS_QUALITY_THR, 28 | PAH8SERIES_PARAM_IDX_FIRST_HR_PPG_QUALITY_THR, 29 | PAH8SERIES_PARAM_IDX_STATIC_MODE, 30 | PAH8SERIES_PARAM_IDX_STATIC_HR_UP_IDX, 31 | PAH8SERIES_PARAM_IDX_STATIC_HR_DN_IDX, 32 | PAH8SERIES_PARAM_IDX_IS_AUTO, 33 | PAH8SERIES_PARAM_IDX_IS_TAG, 34 | PAH8SERIES_PARAM_IDX_IS_8002, 35 | PAH8SERIES_PARAM_IDX_FIRST_HR_6SEC, 36 | PAH8SERIES_PARAM_IDX_HR_MODEL_TIME_CONST, 37 | PAH8SERIES_PARAM_IDX_HR_MODEL_PROGRESS_RATIO, 38 | PAH8SERIES_PARAM_IDX_INIT_WALK_HR, 39 | PAH8SERIES_PARAM_IDX_WALK_HR_UP_IDX, 40 | PAH8SERIES_PARAM_IDX_WALK_HR_DN_IDX, 41 | PAH8SERIES_PARAM_IDX_HR_TOO_LOW_IDX, 42 | PAH8SERIES_PARAM_IDX_EN_LWPS, 43 | PAH8SERIES_PARAM_IDX_EN_JUDGE_WALK, 44 | PAH8SERIES_PARAM_IDX_EN_CHK_HR_GT_MOTION, 45 | PAH8SERIES_PARAM_IDX_EN_HI_QUALITY_HR_JMP, 46 | PAH8SERIES_PARAM_IDX_EN_SPLIT_MOTION_PEAK, 47 | PAH8SERIES_PARAM_IDX_EN_LOCAL_FIRST_PEAK_TRACK, 48 | PAH8SERIES_PARAM_IDX_LOCAL_FIRST_PEAK_FRM_ALIVE_THR, 49 | PAH8SERIES_PARAM_IDX_EN_GLOBAL_FIRST_PEAK_TRACK, 50 | PAH8SERIES_PARAM_IDX_GLOBAL_FIRST_PEAK_FRM_ALIVE_THR, 51 | PAH8SERIES_PARAM_IDX_EN_FORCE_HR_OUT_IN10S, 52 | PAH8SERIES_PARAM_IDX_DEFAULT_HR_OUT_IN10S, 53 | PAH8SERIES_PARAM_IDX_FIRST_HR_HALF_1X_ENERGY_THT, 54 | PAH8SERIES_PARAM_IDX_HR_IN_MOTION_SEARCH_ST, 55 | PAH8SERIES_PARAM_IDX_HR_IN_MOTION_SEARCH_END, 56 | PAH8SERIES_PARAM_IDX_EN_MA_WALK, 57 | PAH8SERIES_PARAM_IDX_EN_HAND_CLAP, 58 | PAH8SERIES_PARAM_IDX_EN_NOR_PRE_DIFF_NOR, 59 | PAH8SERIES_PARAM_IDX_EN_HALF_HR_CHK, 60 | PAH8SERIES_PARAM_IDX_EN_NON_MOTION_FIRST_PEAK_TRACK, 61 | PAH8SERIES_PARAM_IDX_NON_MOTION_FIRST_PEAK_ALIVE_FRM_THR, 62 | PAH8SERIES_PARAM_IDX_PPG_NORMALIZE_LEN, 63 | PAH8SERIES_PARAM_IDX_PPG_DIFF_NORMALIZE_LEN, 64 | PAH8SERIES_PARAM_IDX_EN_CHK_HQ_3X_PPG, 65 | PAH8SERIES_PARAM_IDX_EN_POST_HR_PROCESS, 66 | //v519 67 | PAH8SERIES_PARAM_IDX_SET_EN_JAFMI, 68 | PAH8SERIES_PARAM_IDX_SET_EN_TIME_DOMAIN_CONFIRM, 69 | PAH8SERIES_PARAM_IDX_SET_RESERVE_PEAK_RATIO_THR, 70 | PAH8SERIES_PARAM_IDX_SET_SUPPORT_HR_IDX_OFFSET, 71 | PAH8SERIES_PARAM_IDX_SET_SUPPORT_CHECK_STEP_FREQ, 72 | //v520 73 | PAH8SERIES_PARAM_IDX_SET_SUPPORT_FOUND_HR_IDX, 74 | PAH8SERIES_PARAM_IDX_SET_SUPPORT_SIGNAL_LEVEL_CHECK, 75 | //v521 76 | PAH8SERIES_PARAM_IDX_SET_FORCE_OUT_SAMPLE_COUNT_10S, 77 | //v526 78 | PAH8SERIES_PARAM_IDX_SET_FLAG_LIMIT_HR_UB, 79 | PAH8SERIES_PARAM_IDX_SET_LIMIT_HR_UB, 80 | //v528 81 | PAH8SERIES_PARAM_IDX_SET_SUPPORT_FAST_POST_HR_IDX_FLAG, 82 | //v532 83 | PAH8SERIES_PARAM_IDX_SET_FLAG_LIMIT_HR_LB, 84 | PAH8SERIES_PARAM_IDX_SET_LIMIT_HR_LB, 85 | 86 | PAH8SERIES_NF_PARAM_IDX, 87 | } pah8series_param_idx_t; 88 | 89 | typedef enum { 90 | MSG_SUCCESS = 0, 91 | MSG_ALG_NOT_OPEN, 92 | MSG_ALG_REOPEN, 93 | MSG_MEMS_LEN_TOO_SHORT, 94 | MSG_NO_TOUCH, 95 | MSG_PPG_LEN_TOO_SHORT, 96 | MSG_FRAME_LOSS, 97 | MSG_INVALID_ARGUMENT, 98 | MSG_PROCESS_NOT_FINISHED, 99 | MSG_ADDR_NOT_4BYTE_ALIGNED, 100 | 101 | MSG_NO_MEM = 14, 102 | MSG_ECG_LEN_TOO_SHORT = 15, 103 | 104 | MSG_HR_READY = 0x30, 105 | MSG_SIGNAL_POOR = 0x40 106 | } pah8series_msg_code_t; 107 | 108 | 109 | #endif // PAH8002_DATA_H__ 110 | 111 | -------------------------------------------------------------------------------- /PAH8002driver/PAH8002.h: -------------------------------------------------------------------------------- 1 | /* 2 | Arduino Library for the PixArt PAH8002 HR sensor 3 | based on precompiled library for nrf52 4 | 5 | */ 6 | #ifndef PAH8002_h 7 | #define PAH8002_h 8 | #include 9 | #include "Arduino.h" 10 | #include 11 | 12 | #define TOTAL_CHANNELS 4 //Using channel numbers 13 | #define HEART_RATE_MODE_SAMPLES_PER_CH_READ (20) //Numbers of PPG data per channel 14 | #define HEART_RATE_MODE_SAMPLES_PER_READ (TOTAL_CHANNELS* HEART_RATE_MODE_SAMPLES_PER_CH_READ) 15 | #define TOTAL_CHANNELS_FOR_ALG 3 16 | #define MEMS_ZERO 0 //Default Accelerometer data are all zero 17 | #define PPG_MODE_ONLY 18 | 19 | #define I2C_ID_PAH8002 0x15 //I2C 7-bit ID 20 | 21 | 22 | 23 | enum { 24 | SUSPEND_MODE = 0, 25 | TOUCH_MODE, 26 | NORMAL_MODE, 27 | NORMAL_LONG_ET_MODE, 28 | STRESS_MODE, 29 | NONE, 30 | }; 31 | 32 | //typedef struct { 33 | //uint8_t frame_count; //Frame Count 34 | //uint32_t time; //FIFO Data Ready Interval, unit ms 35 | //uint8_t touch_flag; //Touch Status, 1 for Touch and 0 for De-Touch 36 | //uint32_t nf_ppg_channel; //Using channel numbers, ex.3 37 | //uint32_t nf_ppg_per_channel; //Numbers of PPG data per channel, ex.20 38 | //int32_t *ppg_data; //Pointer to FIFO Raw Data 39 | //uint32_t nf_mems; //Numbers of Accelerometer data(X,Y,Z), must larger or equal 40 | // //than numbers of PPG data per channel, ex.25 41 | //int16_t *mems_data; //Pointer to Accelerometer data 42 | //} pah8series_data_t; 43 | 44 | static uint8_t _mode = NONE ; 45 | static uint8_t pah8002_ppg_data[HEART_RATE_MODE_SAMPLES_PER_READ * 4] ; 46 | static uint8_t _touch_flag = 0 ; 47 | static volatile uint8_t _pah8002_interrupt = 0 ; 48 | static pah8series_data_t _pah8002_data; 49 | static uint32_t _timestamp = 0 ; 50 | 51 | #ifdef MEMS_ZERO 52 | static int16_t _mems_data[HEART_RATE_MODE_SAMPLES_PER_READ * 3] ; 53 | #endif 54 | 55 | static uint8_t _ir_dac = 0 ; 56 | static uint8_t _ir_expo = 0 ; 57 | static uint8_t _chip_id = 0 ; 58 | 59 | 60 | static void *_pah8002_alg_buffer = NULL; 61 | static bool pah8002_sw_reset(void); 62 | static bool pah8002_start(void); 63 | static int pah8002_wakeup(void); 64 | static int pah8002_check(void); 65 | 66 | //static bool pah8002_sw_reset(); 67 | //static bool pah8002_start(); 68 | //static bool pah8002_touch_mode_init(); 69 | //static bool pah8002_normal_mode_init(); 70 | //static bool pah8002_stress_mode_init(); 71 | //static uint8_t pah8002_get_touch_flag_ppg_mode(); 72 | //static bool pah8002_enter_normal_mode(); 73 | //static bool pah8002_enter_stress_mode() ; 74 | //static bool pah8002_enter_touch_mode(); 75 | //static int pah8002_wakeup(); 76 | //static int pah8002_check(); 77 | //static bool pah8002_enter_suspend_mode(); 78 | //static bool _pah8002_task(); 79 | //static bool pah8002_normal_long_et_mode_init() ; 80 | //static bool pah8002_enter_normal_long_et_mode(); 81 | //static void pah8002_dyn_switch_ppg_mode() ; 82 | //static bool pah8002_init(void); 83 | //static void pah8002_deinit(void); 84 | //static void pah8002_log(void) ; 85 | //static void data_convert_4ch_to_3ch(uint32_t *pdata, uint32_t len); 86 | //static void pah8002_task(void) ; 87 | //static void pah8002_intr_isr(void); 88 | uint8_t pah8002_write_reg(uint8_t addr, uint8_t data); 89 | uint8_t pah8002_read_reg(uint8_t addr, uint8_t *data); 90 | uint8_t pah8002_burst_read_reg(uint8_t addr, uint8_t *data, uint32_t rx_size) ; 91 | 92 | 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /PAH8002driver/PAH8002.ino: -------------------------------------------------------------------------------- 1 | // #include "pah8002.h" 2 | // #include "pah8002_comm.h" 3 | // #include "pah8002_api_c.h" 4 | // #include "board.h" 5 | // #include "dd_vendor_1.h" 6 | // #include "uart.h" 7 | // #include "main.h" 8 | // #include "accelerometer.h" 9 | // #include 10 | // #include 11 | // #include 12 | 13 | #include "PAH8002.h" 14 | 15 | 16 | 17 | const uint8_t init_touch_register_INT_array[][2] = { 18 | {0x7f, 0x01}, //switch to bank1 19 | {0xe6, 0xC8}, 20 | {0xe7, 0x00}, 21 | {0xF1, 0x00}, 22 | {0x07, 0x01}, 23 | {0xAE, 0x06}, 24 | {0xAF, 0x07}, 25 | {0xBA, 0x7C}, 26 | {0x6C, 0x10}, 27 | {0x6D, 0x10}, 28 | {0x7A, 0x01}, 29 | {0x6F, 0x10}, 30 | {0x7F, 0x00}, //switch to bank0 31 | {0x08, 0xFF}, 32 | {0x09, 0x03}, 33 | {0x5A, 0x01}, 34 | {0x5C, 0x58}, //Touch Threshold 35 | {0x5D, 0x02}, 36 | {0x60, 0x00}, 37 | {0x61, 0x02}, 38 | {0x64, 0x01}, 39 | {0x65, 0x01}, 40 | {0x35, 0x80}, 41 | {0x36, 0x02}, 42 | {0x8C, 0x00}, 43 | {0x8E, 0x00}, 44 | {0xDE, 0x00}, 45 | {0xD9, 0x01}, 46 | {0xDD, 0x04}, 47 | {0x3B, 0x01}, 48 | {0x43, 0x00}, 49 | {0x47, 0x01}, 50 | {0x48, 0x00}, 51 | {0x49, 0x00}, 52 | {0x4A, 0x01}, 53 | {0x4D, 0x01}, 54 | {0x16, 0x01}, 55 | {0x13, 0x01}, 56 | {0x14, 0x01}, 57 | {0x15, 0x01}, 58 | {0x50, 0x01}, 59 | {0x51, 0x01}, 60 | {0x59, 0x00}, 61 | {0x57, 0x00}, 62 | {0x6B, 0x00}, 63 | {0x6C, 0x00}, 64 | {0x3E, 0x00}, 65 | {0x0D, 0x78}, 66 | {0x0E, 0x00}, 67 | {0x7F, 0x02}, //switch to bank2 68 | {0x17, 0x00}, 69 | {0x18, 0x00}, 70 | {0x1B, 0x01}, 71 | {0x1C, 0x01}, 72 | {0x25, 0x02}, 73 | {0x29, 0x00}, 74 | {0x2d, 0x01}, 75 | {0x4F, 0x0C}, 76 | {0x66, 0x01}, 77 | {0x67, 0x01}, 78 | {0x68, 0x01}, 79 | {0x69, 0x01}, 80 | {0x6A, 0x01}, 81 | //{0x6B, 0x01}, 82 | //{0x6C, 0x01}, 83 | {0x6D, 0x01}, 84 | {0x6E, 0x01}, 85 | {0x6F, 0x01}, 86 | {0x70, 0x01}, 87 | {0x74, 0x01}, 88 | {0x76, 0x00}, 89 | {0x7A, 0x01}, 90 | {0x7B, 0xFF}, 91 | {0x8D, 0x01}, 92 | {0x8F, 0x01}, 93 | {0x92, 0x00}, 94 | {0x7F, 0x01}, //switch to bank1 95 | {0xA2, 0x40}, 96 | {0x7C, 0x01}, 97 | {0x4C, 0x01}, 98 | {0x4F, 0x07}, 99 | {0x3F, 0x04}, 100 | {0x0C, 0x05}, 101 | {0x4D, 0x05}, 102 | {0x52, 0x05}, 103 | {0x86, 0x50}, 104 | {0x92, 0x1C}, 105 | {0x98, 0x1D}, 106 | {0x9A, 0x42}, 107 | {0x81, 0x01}, 108 | {0x3B, 0x00}, 109 | {0xEA, 0xC9}, 110 | {0xA4, 0x50}, 111 | {0xA5, 0x00}, 112 | {0xA6, 0x52}, 113 | {0xA7, 0x00}, 114 | {0xA8, 0x53}, 115 | {0xA9, 0x00}, 116 | {0xAD, 0x00}, 117 | {0xD6, 0xFF}, 118 | {0xD7, 0x1F}, 119 | {0xD8, 0x01}, 120 | {0xD9, 0x00}, 121 | {0xDA, 0x10}, 122 | {0xDB, 0x00}, 123 | {0xDC, 0x16}, 124 | {0xDD, 0x00}, 125 | {0xDE, 0x17}, 126 | {0xDF, 0x00}, 127 | {0xE0, 0xFE}, 128 | {0xE1, 0x1F}, 129 | }; 130 | #define INIT_TOUCH_INT_ARRAY_SIZE (sizeof(init_touch_register_INT_array)/sizeof(init_touch_register_INT_array[0])) 131 | 132 | const uint8_t init_touch_register_array[][2] = { 133 | {0x7f, 0x01}, //switch to bank1 134 | {0x4C, 0x00}, 135 | {0xe6, 0xC8}, 136 | {0xe7, 0x00}, 137 | {0xF1, 0x00}, 138 | {0x07, 0x01}, 139 | {0xAE, 0x06}, 140 | {0xAF, 0x07}, 141 | {0xBA, 0x7C}, 142 | {0x6C, 0x10}, 143 | {0x6D, 0x10}, 144 | {0x7A, 0x01}, 145 | {0x6F, 0x10}, 146 | {0x7F, 0x00}, //switch to bank0 147 | {0x08, 0xFF}, 148 | {0x09, 0x03}, 149 | {0x5A, 0x01}, 150 | {0x5C, 0x58}, //Touch Threshold 151 | {0x5D, 0x02}, 152 | {0x60, 0x00}, 153 | {0x61, 0x02}, 154 | {0x64, 0x01}, 155 | {0x65, 0x01}, 156 | {0x35, 0x80}, 157 | {0x36, 0x02}, 158 | {0x84, 0x78}, 159 | {0x8C, 0x00}, 160 | {0x8E, 0x00}, 161 | {0xDE, 0x00}, 162 | {0xD9, 0x01}, 163 | {0xDD, 0x04}, 164 | {0x3B, 0x01}, 165 | {0x43, 0x00}, 166 | {0x47, 0x01}, 167 | {0x48, 0x00}, 168 | {0x49, 0x00}, 169 | {0x4A, 0x01}, 170 | {0x4D, 0x01}, 171 | {0x16, 0x01}, 172 | {0x13, 0x01}, 173 | {0x14, 0x01}, 174 | {0x15, 0x01}, 175 | {0x50, 0x01}, 176 | {0x51, 0x01}, 177 | {0x59, 0x00}, 178 | {0x57, 0x00}, 179 | {0x6B, 0x00}, 180 | {0x6C, 0x00}, 181 | {0x3E, 0x00}, 182 | {0x0D, 0x78}, 183 | {0x0E, 0x00}, 184 | {0x7F, 0x02}, //switch to bank2 185 | {0x17, 0x00}, 186 | {0x18, 0x00}, 187 | {0x1B, 0x01}, 188 | {0x1C, 0x01}, 189 | {0x25, 0x04}, 190 | {0x29, 0x00}, 191 | {0x2d, 0x01}, 192 | {0x4F, 0x0C}, 193 | {0x66, 0x01}, 194 | {0x67, 0x01}, 195 | {0x68, 0x01}, 196 | {0x69, 0x01}, 197 | {0x6A, 0x01}, 198 | //{0x6B, 0x01}, 199 | //{0x6C, 0x01}, 200 | {0x6D, 0x01}, 201 | {0x6E, 0x01}, 202 | {0x6F, 0x01}, 203 | {0x70, 0x01}, 204 | {0x74, 0x01}, 205 | {0x76, 0x01}, 206 | {0x8D, 0x01}, 207 | {0x8F, 0x01}, 208 | {0x92, 0x00}, 209 | {0x7F, 0x01}, //switch to bank1 210 | {0xA2, 0x40}, 211 | {0x7C, 0x01}, 212 | {0x4F, 0x07}, 213 | {0x3F, 0x04}, 214 | {0x0C, 0x05}, 215 | {0x4D, 0x05}, 216 | {0x52, 0x05}, 217 | {0x86, 0x50}, 218 | {0x92, 0x1C}, 219 | {0x98, 0x1D}, 220 | {0x9A, 0x42}, 221 | {0x81, 0x01}, 222 | {0x3B, 0x00}, 223 | {0xEA, 0xC9}, 224 | {0xA4, 0x50}, 225 | {0xA5, 0x00}, 226 | {0xA6, 0x52}, 227 | {0xA7, 0x00}, 228 | {0xA8, 0x53}, 229 | {0xA9, 0x00}, 230 | {0xAD, 0x00}, 231 | {0xD6, 0xFF}, 232 | {0xD7, 0x1F}, 233 | {0xD8, 0x01}, 234 | {0xD9, 0x00}, 235 | {0xDA, 0x10}, 236 | {0xDB, 0x00}, 237 | {0xDC, 0x16}, 238 | {0xDD, 0x00}, 239 | {0xDE, 0x17}, 240 | {0xDF, 0x00}, 241 | {0xE0, 0xFE}, 242 | {0xE1, 0x1F}, 243 | }; 244 | #define INIT_TOUCH_REG_ARRAY_SIZE (sizeof(init_touch_register_array)/sizeof(init_touch_register_array[0])) 245 | 246 | 247 | const uint8_t init_ppg_register_array[][2] = { 248 | {0x7f, 0x01}, //switch to bank1 249 | {0xE6, 0xC8}, 250 | {0xE7, 0x00}, 251 | {0x07, 0x01}, 252 | {0xAE, 0x06}, 253 | {0xAF, 0x07}, 254 | {0x4D, 0x00}, 255 | {0xBA, 0x7C}, 256 | {0xBB, 0x7C}, 257 | {0xBC, 0x7C}, 258 | {0xBD, 0x06}, 259 | {0xBE, 0x06}, 260 | {0xBF, 0x06}, 261 | {0xB1, 0x06}, 262 | {0xB2, 0x06}, 263 | {0xB3, 0x06}, 264 | {0x6A, 0x00}, 265 | {0x6B, 0x01}, 266 | {0x6C, 0x10}, 267 | {0x6D, 0x10}, 268 | {0x7A, 0x00}, 269 | {0x6F, 0x10}, 270 | {0x7F, 0x00}, //switch to bank0 271 | {0x08, 0xFF}, 272 | {0x09, 0x03}, 273 | {0x4F, 0x0C}, 274 | {0xE6, 0x07}, 275 | {0x8C, 0x00}, 276 | {0xAE, 0x01}, 277 | {0xD0, 0x01}, 278 | {0x8E, 0x00}, 279 | {0xD2, 0x01}, 280 | {0xB0, 0x01}, 281 | {0x27, 0x80}, 282 | {0x28, 0x12}, 283 | {0x35, 0xC0}, 284 | {0x36, 0x12}, 285 | {0x37, 0xC0}, 286 | {0x38, 0x12}, 287 | {0x39, 0xC0}, 288 | {0x3A, 0x12}, 289 | {0xDE, 0x00}, 290 | {0xD9, 0x01}, 291 | {0xDD, 0x04}, 292 | {0x3B, 0x01}, 293 | {0x3C, 0x15}, 294 | {0x3D, 0x15}, 295 | {0x47, 0x01}, 296 | {0x48, 0x01}, 297 | {0x49, 0x01}, 298 | {0x4A, 0x01}, 299 | {0x4B, 0x00}, 300 | {0x4C, 0x00}, 301 | {0x4D, 0x00}, 302 | {0x16, 0x00}, 303 | {0x13, 0x01}, 304 | {0x14, 0x01}, 305 | {0x15, 0x01}, 306 | {0x50, 0x00}, 307 | {0x59, 0x00}, 308 | {0x56, 0x00}, 309 | {0x57, 0x00}, 310 | {0x6B, 0x01}, 311 | {0x6C, 0x00}, 312 | {0x8F, 0x01}, 313 | {0xB1, 0x01}, 314 | {0x3E, 0x02}, 315 | {0x3F, 0x04}, 316 | {0x40, 0x04}, 317 | {0x0D, 0x78}, 318 | {0x0E, 0x00}, 319 | {0x0F, 0xF0}, 320 | {0x10, 0x00}, 321 | {0x11, 0xF0}, 322 | {0x12, 0x00}, 323 | {0x6D, 0xF0}, 324 | {0x6E, 0x00}, 325 | {0x6F, 0x00}, 326 | {0x70, 0x02}, 327 | {0x71, 0x10}, 328 | {0x72, 0x00}, 329 | {0x77, 0x00}, 330 | {0x78, 0x0C}, 331 | {0x79, 0x00}, 332 | {0x7A, 0x08}, 333 | {0x7B, 0x00}, 334 | {0x7C, 0x0B}, 335 | {0x7D, 0x00}, 336 | {0x7E, 0x09}, 337 | {0x80, 0x00}, 338 | {0x81, 0x0D}, 339 | {0x82, 0x00}, 340 | {0x83, 0x07}, 341 | {0x85, 0x01}, 342 | {0x90, 0xF0}, 343 | {0x91, 0x00}, 344 | {0x92, 0x20}, 345 | {0x93, 0x12}, 346 | {0x94, 0x10}, 347 | {0x95, 0x00}, 348 | {0x9A, 0x00}, 349 | {0x9B, 0x0C}, 350 | {0x9C, 0x00}, 351 | {0x9D, 0x08}, 352 | {0x9E, 0x00}, 353 | {0x9F, 0x0B}, 354 | {0xA0, 0x00}, 355 | {0xA1, 0x09}, 356 | {0xA2, 0x00}, 357 | {0xA3, 0x0D}, 358 | {0xA4, 0x00}, 359 | {0xA5, 0x07}, 360 | {0xA7, 0x01}, 361 | {0xB2, 0xF0}, 362 | {0xB3, 0x00}, 363 | {0xB4, 0x20}, 364 | {0xB5, 0x12}, 365 | {0xB6, 0x10}, 366 | {0xB7, 0x00}, 367 | {0xBC, 0x00}, 368 | {0xBD, 0x0C}, 369 | {0xBE, 0x00}, 370 | {0xBF, 0x08}, 371 | {0xC0, 0x00}, 372 | {0xC1, 0x0B}, 373 | {0xC2, 0x00}, 374 | {0xC3, 0x09}, 375 | {0xC4, 0x00}, 376 | {0xC5, 0x0D}, 377 | {0xC6, 0x00}, 378 | {0xC7, 0x07}, 379 | {0xC9, 0x01}, 380 | {0x7F, 0x02}, //switch to bank2 381 | {0x17, 0x00}, 382 | {0x18, 0x00}, 383 | {0x1B, 0x01}, 384 | {0x1C, 0x01}, 385 | {0x25, 0x02}, 386 | {0x29, 0x00}, 387 | {0x2d, 0x01}, 388 | {0x4F, 0x10}, 389 | {0x66, 0x01}, 390 | {0x67, 0x01}, 391 | {0x68, 0x01}, 392 | {0x69, 0x01}, 393 | {0x6A, 0x01}, 394 | //{0x6B, 0x01}, 395 | //{0x6C, 0x01}, 396 | {0x6D, 0x01}, 397 | {0x6E, 0x01}, 398 | {0x70, 0x01}, 399 | {0x7B, 0xFF}, 400 | {0x7F, 0x01}, //switch to bank1 401 | {0xA2, 0x40}, 402 | {0x7C, 0x01}, 403 | {0x4F, 0x07}, 404 | {0x3F, 0x04}, 405 | {0x0C, 0x05}, 406 | {0x4D, 0x05}, 407 | {0x52, 0x05}, 408 | {0x86, 0x50}, 409 | {0x92, 0x1C}, 410 | {0x98, 0x1D}, 411 | {0x9A, 0x42}, 412 | {0x81, 0x01}, 413 | {0x3B, 0x00}, 414 | {0xEA, 0xC9}, 415 | {0xA4, 0x50}, 416 | {0xA5, 0x00}, 417 | {0xA6, 0x52}, 418 | {0xA7, 0x00}, 419 | {0xA8, 0x53}, 420 | {0xA9, 0x00}, 421 | {0xD6, 0x40}, 422 | {0xD7, 0x06}, 423 | {0xD8, 0x01}, 424 | {0xD9, 0x00}, 425 | {0xDA, 0x11}, 426 | {0xDB, 0x00}, 427 | {0xDC, 0x84}, 428 | {0xDD, 0x02}, 429 | {0xDE, 0x85}, 430 | {0xDF, 0x02}, 431 | {0xE0, 0x3F}, 432 | {0xE1, 0x06}, 433 | }; 434 | #define INIT_PPG_REG_ARRAY_SIZE (sizeof(init_ppg_register_array)/sizeof(init_ppg_register_array[0])) 435 | 436 | const uint8_t init_ppg_long_register_array[][2] = { 437 | {0x7f, 0x01}, //switch to bank1 438 | {0xE6, 0xC8}, 439 | {0xE7, 0x00}, 440 | {0x07, 0x01}, 441 | {0xAE, 0x06}, 442 | {0xAF, 0x07}, 443 | {0x4D, 0x00}, 444 | {0xBA, 0x7C}, 445 | {0xBB, 0x7C}, 446 | {0xBC, 0x7C}, 447 | {0xBD, 0x06}, 448 | {0xBE, 0x06}, 449 | {0xBF, 0x06}, 450 | {0xB1, 0x06}, 451 | {0xB2, 0x06}, 452 | {0xB3, 0x06}, 453 | {0x6A, 0x00}, 454 | {0x6B, 0x01}, 455 | {0x6C, 0x10}, 456 | {0x6D, 0x10}, 457 | {0x7A, 0x00}, 458 | {0x6F, 0x10}, 459 | {0x7F, 0x00}, //switch to bank0 460 | {0x08, 0xFF}, 461 | {0x09, 0x03}, 462 | {0x4F, 0x0C}, 463 | {0xE6, 0x07}, 464 | {0x8C, 0x00}, 465 | {0xAE, 0x01}, 466 | {0xD0, 0x01}, 467 | {0x8E, 0x00}, 468 | {0xD2, 0x01}, 469 | {0xB0, 0x01}, 470 | {0x27, 0x40}, 471 | {0x28, 0x25}, 472 | {0x35, 0x80}, 473 | {0x36, 0x25}, 474 | {0x37, 0x80}, 475 | {0x38, 0x25}, 476 | {0x39, 0x80}, 477 | {0x3A, 0x25}, 478 | {0xDE, 0x00}, 479 | {0xD9, 0x01}, 480 | {0xDD, 0x04}, 481 | {0x3B, 0x01}, 482 | {0x3C, 0x0A}, 483 | {0x3D, 0x0A}, 484 | {0x47, 0x01}, 485 | {0x48, 0x01}, 486 | {0x49, 0x01}, 487 | {0x4A, 0x01}, 488 | {0x4B, 0x00}, 489 | {0x4C, 0x00}, 490 | {0x4D, 0x00}, 491 | {0x16, 0x00}, 492 | {0x13, 0x01}, 493 | {0x14, 0x01}, 494 | {0x15, 0x01}, 495 | {0x50, 0x00}, 496 | {0x59, 0x00}, 497 | {0x56, 0x00}, 498 | {0x57, 0x00}, 499 | {0x6B, 0x01}, 500 | {0x6C, 0x00}, 501 | {0x8F, 0x01}, 502 | {0xB1, 0x01}, 503 | {0x3E, 0x02}, 504 | {0x3F, 0x02}, 505 | {0x40, 0x02}, 506 | {0x0D, 0x78}, 507 | {0x0E, 0x00}, 508 | {0x0F, 0xC0}, 509 | {0x10, 0x12}, 510 | {0x11, 0xC0}, 511 | {0x12, 0x12}, 512 | {0x6D, 0xF0}, 513 | {0x6E, 0x00}, 514 | {0x6F, 0x00}, 515 | {0x70, 0x02}, 516 | {0x71, 0x10}, 517 | {0x72, 0x00}, 518 | {0x77, 0x00}, 519 | {0x78, 0x0C}, 520 | {0x79, 0x00}, 521 | {0x7A, 0x08}, 522 | {0x7B, 0x00}, 523 | {0x7C, 0x0B}, 524 | {0x7D, 0x00}, 525 | {0x7E, 0x09}, 526 | {0x80, 0x00}, 527 | {0x81, 0x0D}, 528 | {0x82, 0x00}, 529 | {0x83, 0x07}, 530 | {0x85, 0x01}, 531 | {0x90, 0xF0}, 532 | {0x91, 0x00}, 533 | {0x92, 0x00}, 534 | {0x93, 0x25}, 535 | {0x94, 0x10}, 536 | {0x95, 0x00}, 537 | {0x9A, 0x00}, 538 | {0x9B, 0x0C}, 539 | {0x9C, 0x00}, 540 | {0x9D, 0x08}, 541 | {0x9E, 0x00}, 542 | {0x9F, 0x0B}, 543 | {0xA0, 0x00}, 544 | {0xA1, 0x09}, 545 | {0xA2, 0x00}, 546 | {0xA3, 0x0D}, 547 | {0xA4, 0x00}, 548 | {0xA5, 0x07}, 549 | {0xA7, 0x01}, 550 | {0xB2, 0xF0}, 551 | {0xB3, 0x00}, 552 | {0xB4, 0x00}, 553 | {0xB5, 0x25}, 554 | {0xB6, 0x10}, 555 | {0xB7, 0x00}, 556 | {0xBC, 0x00}, 557 | {0xBD, 0x0C}, 558 | {0xBE, 0x00}, 559 | {0xBF, 0x08}, 560 | {0xC0, 0x00}, 561 | {0xC1, 0x0B}, 562 | {0xC2, 0x00}, 563 | {0xC3, 0x09}, 564 | {0xC4, 0x00}, 565 | {0xC5, 0x0D}, 566 | {0xC6, 0x00}, 567 | {0xC7, 0x07}, 568 | {0xC9, 0x01}, 569 | {0x7F, 0x02}, //switch to bank2 570 | {0x17, 0x00}, 571 | {0x18, 0x00}, 572 | {0x1B, 0x01}, 573 | {0x1C, 0x01}, 574 | {0x25, 0x02}, 575 | {0x29, 0x00}, 576 | {0x2d, 0x01}, 577 | {0x4F, 0x10}, 578 | {0x66, 0x01}, 579 | {0x67, 0x01}, 580 | {0x68, 0x01}, 581 | {0x69, 0x01}, 582 | {0x6A, 0x01}, 583 | //{0x6B, 0x01}, 584 | //{0x6C, 0x01}, 585 | {0x6D, 0x01}, 586 | {0x6E, 0x01}, 587 | {0x70, 0x01}, 588 | {0x7B, 0xFF}, 589 | {0x7F, 0x01}, //switch to bank1 590 | {0xA2, 0x40}, 591 | {0x7C, 0x01}, 592 | {0x4F, 0x07}, 593 | {0x3F, 0x04}, 594 | {0x0C, 0x05}, 595 | {0x4D, 0x05}, 596 | {0x52, 0x05}, 597 | {0x86, 0x50}, 598 | {0x92, 0x1C}, 599 | {0x98, 0x1D}, 600 | {0x9A, 0x42}, 601 | {0x81, 0x01}, 602 | {0x3B, 0x00}, 603 | {0xEA, 0xC9}, 604 | {0xA4, 0x50}, 605 | {0xA5, 0x00}, 606 | {0xA6, 0x52}, 607 | {0xA7, 0x00}, 608 | {0xA8, 0x53}, 609 | {0xA9, 0x00}, 610 | {0xD6, 0x40}, 611 | {0xD7, 0x06}, 612 | {0xD8, 0x01}, 613 | {0xD9, 0x00}, 614 | {0xDA, 0x11}, 615 | {0xDB, 0x00}, 616 | {0xDC, 0xC4}, 617 | {0xDD, 0x02}, 618 | {0xDE, 0xC5}, 619 | {0xDF, 0x02}, 620 | {0xE0, 0x3F}, 621 | {0xE1, 0x06}, 622 | }; 623 | #define INIT_PPG_LONG_REG_ARRAY_SIZE (sizeof(init_ppg_long_register_array)/sizeof(init_ppg_long_register_array[0])) 624 | 625 | const uint8_t init_stress_register_array[][2] = { 626 | {0x7f, 0x01}, //switch to bank1 627 | {0xE6, 0xC8}, 628 | {0xE7, 0x00}, 629 | {0x07, 0x01}, 630 | {0xAE, 0x06}, 631 | {0xAF, 0x07}, 632 | {0x4D, 0x00}, 633 | {0xBA, 0x7C}, 634 | {0xBB, 0x7C}, 635 | {0xBC, 0x7C}, 636 | {0xBD, 0x06}, 637 | {0xBE, 0x06}, 638 | {0xBF, 0x06}, 639 | {0xB1, 0x06}, 640 | {0xB2, 0x06}, 641 | {0xB3, 0x06}, 642 | {0x6A, 0x00}, 643 | {0x6B, 0x01}, 644 | {0x6C, 0x10}, 645 | {0x6D, 0x10}, 646 | {0x7A, 0x00}, 647 | {0x6F, 0x08}, 648 | {0x7F, 0x00}, //switch to bank0 649 | {0x08, 0xFF}, 650 | {0x09, 0x03}, 651 | {0x4F, 0x0C}, 652 | {0xE6, 0x07}, 653 | {0x8C, 0x00}, 654 | {0xAE, 0x01}, 655 | {0xD0, 0x01}, 656 | {0x8E, 0x00}, 657 | {0xD2, 0x01}, 658 | {0xB0, 0x01}, 659 | {0x27, 0x60}, 660 | {0x28, 0x0F}, 661 | {0x35, 0xA0}, 662 | {0x36, 0x0F}, 663 | {0x37, 0xA0}, 664 | {0x38, 0x0F}, 665 | {0x39, 0xA0}, 666 | {0x3A, 0x0F}, 667 | {0xDE, 0x00}, 668 | {0xD9, 0x01}, 669 | {0xDD, 0x04}, 670 | {0x3B, 0x01}, 671 | {0x3C, 0x02}, 672 | {0x3D, 0x02}, 673 | {0x43, 0x00}, 674 | {0x44, 0x00}, 675 | {0x45, 0x00}, 676 | {0x47, 0x01}, 677 | {0x48, 0x01}, 678 | {0x49, 0x01}, 679 | {0x4A, 0x01}, 680 | {0x4B, 0x00}, 681 | {0x4C, 0x00}, 682 | {0x4D, 0x00}, 683 | {0x16, 0x00}, 684 | {0x13, 0x01}, 685 | {0x14, 0x01}, 686 | {0x15, 0x01}, 687 | {0x50, 0x00}, 688 | {0x59, 0x00}, 689 | {0x56, 0x00}, 690 | {0x57, 0x00}, 691 | {0x6B, 0x01}, 692 | {0x6C, 0x00}, 693 | {0x8F, 0x01}, 694 | {0xB1, 0x01}, 695 | {0x3E, 0x00}, 696 | {0x3F, 0x00}, 697 | {0x40, 0x00}, 698 | {0x0D, 0x78}, 699 | {0x0E, 0x00}, 700 | {0x0F, 0xF0}, 701 | {0x10, 0x00}, 702 | {0x11, 0xF0}, 703 | {0x12, 0x00}, 704 | {0x6D, 0xF0}, 705 | {0x6E, 0x00}, 706 | {0x6F, 0x00}, 707 | {0x70, 0x02}, 708 | {0x71, 0x10}, 709 | {0x72, 0x00}, 710 | {0x77, 0x00}, 711 | {0x78, 0x0C}, 712 | {0x79, 0x00}, 713 | {0x7A, 0x08}, 714 | {0x7B, 0x00}, 715 | {0x7C, 0x0B}, 716 | {0x7D, 0x00}, 717 | {0x7E, 0x09}, 718 | {0x80, 0x00}, 719 | {0x81, 0x0D}, 720 | {0x82, 0x00}, 721 | {0x83, 0x07}, 722 | {0x85, 0x01}, 723 | {0x90, 0xF0}, 724 | {0x91, 0x00}, 725 | {0x92, 0x40}, 726 | {0x93, 0x0F}, 727 | {0x94, 0x10}, 728 | {0x95, 0x00}, 729 | {0x9A, 0x00}, 730 | {0x9B, 0x0C}, 731 | {0x9C, 0x00}, 732 | {0x9D, 0x08}, 733 | {0x9E, 0x00}, 734 | {0x9F, 0x0B}, 735 | {0xA0, 0x00}, 736 | {0xA1, 0x09}, 737 | {0xA2, 0x00}, 738 | {0xA3, 0x0D}, 739 | {0xA4, 0x00}, 740 | {0xA5, 0x07}, 741 | {0xA7, 0x01}, 742 | {0xB2, 0xF0}, 743 | {0xB3, 0x00}, 744 | {0xB4, 0x40}, 745 | {0xB5, 0x0F}, 746 | {0xB6, 0x10}, 747 | {0xB7, 0x00}, 748 | {0xBC, 0x00}, 749 | {0xBD, 0x0C}, 750 | {0xBE, 0x00}, 751 | {0xBF, 0x08}, 752 | {0xC0, 0x00}, 753 | {0xC1, 0x0B}, 754 | {0xC2, 0x00}, 755 | {0xC3, 0x09}, 756 | {0xC4, 0x00}, 757 | {0xC5, 0x0D}, 758 | {0xC6, 0x00}, 759 | {0xC7, 0x07}, 760 | {0xC9, 0x01}, 761 | {0x7F, 0x02}, //switch to bank2 762 | {0x17, 0x00}, 763 | {0x18, 0x00}, 764 | {0x1B, 0x01}, 765 | {0x1C, 0x01}, 766 | {0x25, 0x02}, 767 | {0x29, 0x00}, 768 | {0x2d, 0x01}, 769 | {0x4F, 0x0C}, 770 | {0x66, 0x01}, 771 | {0x67, 0x01}, 772 | {0x68, 0x01}, 773 | {0x69, 0x01}, 774 | {0x6A, 0x01}, 775 | {0x6D, 0x01}, 776 | {0x6E, 0x01}, 777 | {0x70, 0x01}, 778 | {0x7B, 0xFF}, 779 | {0x7F, 0x01}, //switch to bank1 780 | {0x22, 0x50}, 781 | {0x48, 0x50}, 782 | {0xA2, 0x40}, 783 | {0x7C, 0x01}, 784 | {0x4F, 0x07}, 785 | {0x3F, 0x04}, 786 | {0x0C, 0x05}, 787 | {0x4D, 0x05}, 788 | {0x52, 0x05}, 789 | {0x86, 0x50}, 790 | {0x92, 0x1C}, 791 | {0x98, 0x1D}, 792 | {0x9A, 0x42}, 793 | {0x81, 0x01}, 794 | {0x3B, 0x00}, 795 | {0xEA, 0xC9}, 796 | {0xA4, 0x50}, 797 | {0xA5, 0x00}, 798 | {0xA6, 0x52}, 799 | {0xA7, 0x00}, 800 | {0xA8, 0x53}, 801 | {0xA9, 0x00}, 802 | {0xD6, 0xA0}, 803 | {0xD7, 0x00}, 804 | {0xD8, 0x01}, 805 | {0xD9, 0x00}, 806 | {0xDA, 0x2C}, 807 | {0xDB, 0x00}, 808 | {0xDC, 0x8B}, 809 | {0xDD, 0x00}, 810 | {0xDE, 0x8C}, 811 | {0xDF, 0x00}, 812 | {0xE0, 0x9F}, 813 | {0xE1, 0x00}, 814 | }; 815 | #define INIT_STRESS_REG_ARRAY_SIZE (sizeof(init_stress_register_array)/sizeof(init_stress_register_array[0])) 816 | 817 | 818 | const uint8_t suspend_register_array[][2] = { 819 | {0x7f, 0x01}, //switch to bank1 820 | {0x09, 0x01}, 821 | {0x23, 0x01}, 822 | {0xB4, 0x01}, 823 | {0xB7, 0x01}, 824 | {0xE6, 0xC8}, 825 | {0xE7, 0x00}, 826 | {0xF1, 0x00}, 827 | {0x07, 0x01}, 828 | {0xAE, 0x06}, 829 | {0xAF, 0x07}, 830 | {0xBA, 0x7C}, 831 | {0x6C, 0x10}, 832 | {0x6D, 0x10}, 833 | {0x7A, 0x00}, 834 | {0x6F, 0x10}, 835 | {0x7F, 0x00}, //switch to bank0 836 | {0x08, 0xFF}, 837 | {0x09, 0x03}, 838 | {0xD6, 0x01}, 839 | {0x5C, 0x00}, 840 | {0x5D, 0x05}, 841 | {0x60, 0x00}, 842 | {0x61, 0x03}, 843 | {0x64, 0x05}, 844 | {0x65, 0x05}, 845 | {0x35, 0x80}, 846 | {0x36, 0x02}, 847 | {0x8C, 0x00}, 848 | {0x8E, 0x00}, 849 | {0xDE, 0x00}, 850 | {0xD9, 0x01}, 851 | {0xDD, 0x04}, 852 | {0x3B, 0x01}, 853 | {0x47, 0x01}, 854 | {0x48, 0x00}, 855 | {0x49, 0x00}, 856 | {0x4A, 0x00}, 857 | {0x4D, 0x00}, 858 | {0x16, 0x00}, 859 | {0x13, 0x01}, 860 | {0x14, 0x01}, 861 | {0x15, 0x01}, 862 | {0x50, 0x01}, 863 | {0x51, 0x01}, 864 | {0x59, 0x00}, 865 | {0x57, 0x00}, 866 | {0x6B, 0x00}, 867 | {0x6C, 0x00}, 868 | {0x3E, 0x00}, 869 | {0x43, 0x00}, 870 | {0x0D, 0x78}, 871 | {0x0E, 0x00}, 872 | {0x7F, 0x02}, //switch to bank2 873 | {0x17, 0x00}, 874 | {0x18, 0x00}, 875 | {0x1B, 0x01}, 876 | {0x1C, 0x01}, 877 | {0x1F, 0x00}, 878 | {0x29, 0x00}, 879 | {0x2d, 0x01}, 880 | {0x2B, 0x00}, 881 | {0x2C, 0x00}, 882 | {0x31, 0x00}, 883 | {0x4F, 0x10}, 884 | {0x66, 0x01}, 885 | {0x67, 0x01}, 886 | {0x68, 0x01}, 887 | {0x69, 0x01}, 888 | {0x6A, 0x01}, 889 | {0x6B, 0x01}, 890 | {0x6C, 0x01}, 891 | {0x6D, 0x01}, 892 | {0x6E, 0x01}, 893 | {0x6F, 0x01}, 894 | {0x70, 0x01}, 895 | {0x74, 0x00}, 896 | {0x76, 0x01}, 897 | {0x78, 0x01}, 898 | {0x7A, 0x01}, 899 | {0x7B, 0xFF}, 900 | {0x8D, 0x01}, 901 | {0x8F, 0x01}, 902 | {0x92, 0x00}, 903 | {0x7F, 0x01}, //switch to bank1 904 | {0xA2, 0x40}, 905 | {0x7C, 0x01}, 906 | {0x4C, 0x01}, 907 | {0x4F, 0x07}, 908 | {0x3F, 0x04}, 909 | {0x0C, 0x05}, 910 | {0x4D, 0x05}, 911 | {0x52, 0x05}, 912 | {0x86, 0x50}, 913 | {0x92, 0x1C}, 914 | {0x98, 0x1D}, 915 | {0x9A, 0x42}, 916 | {0x81, 0x01}, 917 | {0x3B, 0x00}, 918 | {0xEA, 0xC9}, 919 | {0xA4, 0x50}, 920 | {0xA5, 0x00}, 921 | {0xA6, 0x52}, 922 | {0xA7, 0x00}, 923 | {0xA8, 0x53}, 924 | {0xA9, 0x00}, 925 | {0xD6, 0xFF}, 926 | {0xD7, 0x1F}, 927 | {0xD8, 0x01}, 928 | {0xD9, 0x00}, 929 | {0xDA, 0x10}, 930 | {0xDB, 0x00}, 931 | {0xDC, 0x13}, 932 | {0xDD, 0x00}, 933 | {0xDE, 0x14}, 934 | {0xDF, 0x00}, 935 | {0xE0, 0xFE}, 936 | {0xE1, 0x1F}, 937 | {0x7F, 0x01}, //switch to bank1 938 | {0xd5, 0x01}, 939 | }; 940 | #define SUSPEND_REG_ARRAY_SIZE (sizeof(suspend_register_array)/sizeof(suspend_register_array[0])) 941 | 942 | 943 | //++++++++++++++++++++++PAH8002 functions++++++++++++++++++++++++++++++++++++++++++++++++++++ 944 | static bool pah8002_sw_reset() { 945 | uint8_t data ; 946 | //debug_printf(">>>pah8002_sw_reset\r\n"); 947 | pah8002_wakeup(); 948 | if (0 != pah8002_write_reg(0x7f, 0x00)) { 949 | goto RTN; 950 | } 951 | if (0 != pah8002_read_reg(0, &data)) { 952 | goto RTN; 953 | } 954 | //debug_printf("ID=%d\r\n", data); 955 | if (data != 0x02) { 956 | goto RTN; 957 | } 958 | if (0 != pah8002_write_reg(0xe1, 0)) //write 0 to trigger Software Reset 959 | { 960 | goto RTN; //delay 5ms 961 | } 962 | delay(5); 963 | //debug_printf("<<>>pah8002_start \r\n"); 972 | pah8002_wakeup(); 973 | if (0 != pah8002_write_reg(0x7f, 0x01)) { 974 | goto RTN; 975 | } 976 | else if (0 != pah8002_write_reg(0xea, (samples_per_read + 1))) { 977 | goto RTN; 978 | } 979 | else if (0 != pah8002_write_reg(0xd5, 1)) //TG enable. REQTIMER_ENABLE 980 | { 981 | goto RTN; 982 | } 983 | else if (0 != pah8002_read_reg(0xd5, &data)) //TG enable. REQTIMER_ENABLE 984 | { 985 | goto RTN; 986 | } 987 | pah8002_check(); 988 | //debug_printf("<<< pah8002_start %d\r\n", data); 989 | return true; 990 | RTN: return false; 991 | } 992 | 993 | static bool pah8002_touch_mode_init() { 994 | int i = 0 ; 995 | //debug_printf(">>> pah8002_touch_mode_init \r\n"); 996 | pah8002_wakeup(); 997 | for (i = 0; i < INIT_TOUCH_REG_ARRAY_SIZE; i++) { 998 | if ( pah8002_write_reg( init_touch_register_array[i][0], init_touch_register_array[i][1]) != 0 ) { 999 | goto RTN; 1000 | } 1001 | } 1002 | //debug_printf("<<< pah8002_touch_mode_init \r\n"); 1003 | return true; 1004 | RTN: return false; 1005 | 1006 | 1007 | } 1008 | 1009 | static bool pah8002_normal_mode_init() { 1010 | int i = 0 ; 1011 | //debug_printf(">>> pah8002_normal_mode_init \r\n"); 1012 | pah8002_wakeup(); 1013 | for (i = 0; i < INIT_PPG_REG_ARRAY_SIZE; i++) { 1014 | if ( pah8002_write_reg( init_ppg_register_array[i][0], init_ppg_register_array[i][1]) != 0 ) { 1015 | goto RTN; 1016 | } 1017 | } 1018 | //debug_printf("<<< pah8002_normal_mode_init \r\n"); 1019 | return true; 1020 | RTN: return false; 1021 | } 1022 | 1023 | static bool pah8002_stress_mode_init() { 1024 | int i = 0 ; 1025 | //debug_printf(">>> pah8002_stress_mode_init \r\n"); 1026 | pah8002_wakeup(); 1027 | for (i = 0; i < INIT_STRESS_REG_ARRAY_SIZE; i++) { 1028 | if ( pah8002_write_reg( init_stress_register_array[i][0], init_stress_register_array[i][1]) != 0 ) { 1029 | goto RTN; 1030 | } 1031 | } 1032 | //debug_printf("<<< pah8002_stress_mode_init \r\n"); 1033 | return true; 1034 | RTN: return false; 1035 | } 1036 | 1037 | static uint8_t pah8002_get_touch_flag_ppg_mode() { 1038 | static uint8_t touch_sts_output = 1 ; 1039 | int32_t *s = (int32_t *)pah8002_ppg_data ; 1040 | int32_t ch0 ; 1041 | int32_t ch1 ; 1042 | int64_t ir_rawdata; 1043 | int i; 1044 | static int touch_cnt = 0, no_touch_cnt = 0 ; 1045 | 1046 | #define TouchDetection_Upper_TH (600) 1047 | #define TouchDetection_Lower_TH (512) 1048 | #define TouchDetection_Count_TH (3) //(3+1)*50ms = 200ms 1049 | #define NoTouchDetection_Count_TH (3) //(3+1)*50ms = 200ms 1050 | 1051 | for (i = 0; i < HEART_RATE_MODE_SAMPLES_PER_READ; i += TOTAL_CHANNELS) { 1052 | ch0 = *s; ch1 = *(s + 1); 1053 | ir_rawdata = ch0 - ch1 ; 1054 | ir_rawdata = (ir_rawdata * _ir_dac * _ir_expo) >> 20 ; 1055 | if ( ir_rawdata > TouchDetection_Upper_TH) { 1056 | touch_cnt++; no_touch_cnt = 0; 1057 | } else if ( ir_rawdata < TouchDetection_Lower_TH) { 1058 | no_touch_cnt++; touch_cnt = 0 ; 1059 | } else { 1060 | touch_cnt = 0 ; no_touch_cnt = 0; 1061 | } 1062 | s += TOTAL_CHANNELS; 1063 | } 1064 | if (touch_cnt > TouchDetection_Count_TH) { 1065 | touch_sts_output = 1; 1066 | } else if ( no_touch_cnt > NoTouchDetection_Count_TH) { 1067 | touch_sts_output = 0; 1068 | } 1069 | //debug_printf("<<< pah8002_get_touch_flag_ppg_mode %d, %d\n", touch_cnt, no_touch_cnt); 1070 | //debug_printf("<<< pah8002_get_touch_flag_ppg_mode %d\n", touch_sts_output); 1071 | return touch_sts_output; 1072 | } 1073 | 1074 | static bool pah8002_enter_normal_mode() { 1075 | //debug_printf(">>> pah8002_enter_normal_mode\r\n"); 1076 | if (_mode == NORMAL_MODE) return true; 1077 | //1. software reset 1078 | if ( !pah8002_sw_reset()) goto RTN; 1079 | //2. load registers for normal mode 1080 | if ( !pah8002_normal_mode_init()) goto RTN; 1081 | pah8002_write_reg(0x7f, 0x00); //Bank0 1082 | pah8002_read_reg(0x0D, &_ir_expo); // IR Exposure Time 1083 | pah8002_write_reg(0x7f, 0x01); //Bank1 1084 | pah8002_read_reg(0xBA, &_ir_dac); //IR Led DAC 1085 | //3. enable sensor 1086 | if ( !pah8002_start()) goto RTN; 1087 | _mode = NORMAL_MODE; 1088 | //debug_printf("<<< pah8002_enter_normal_mode ir_dac %x, ir_expo %x\r\n", _ir_dac, _ir_expo); 1089 | return true; 1090 | RTN: return false ; 1091 | } 1092 | 1093 | static bool pah8002_enter_stress_mode() { 1094 | //debug_printf(">>> pah8002_enter_stress_mode\r\n"); 1095 | if (_mode == STRESS_MODE) return true; 1096 | //1. software reset 1097 | if ( !pah8002_sw_reset()) goto RTN; 1098 | //2. load registers for normal mode 1099 | if ( !pah8002_stress_mode_init()) goto RTN; 1100 | pah8002_write_reg(0x7f, 0x00); //Bank0 1101 | pah8002_read_reg(0x0D, &_ir_expo); // IR Exposure Time 1102 | pah8002_write_reg(0x7f, 0x01); //Bank1 1103 | pah8002_read_reg(0xBA, &_ir_dac); //IR Led DAC 1104 | //3. enable sensor 1105 | if ( !pah8002_start()) goto RTN; 1106 | _mode = STRESS_MODE; 1107 | //debug_printf("<<< pah8002_enter_stress_mode \r\n"); 1108 | return true; 1109 | RTN: return false ; 1110 | } 1111 | 1112 | static bool pah8002_enter_touch_mode() { 1113 | //debug_printf(">>> pah8002_enter_touch_mode\r\n"); 1114 | if (_mode == TOUCH_MODE) return true; 1115 | //1. software reset 1116 | if ( !pah8002_sw_reset() ) goto RTN; 1117 | //2. load registers for touch mode 1118 | if ( !pah8002_touch_mode_init()) goto RTN; 1119 | //3. enable sensor 1120 | if ( !pah8002_start()) goto RTN; 1121 | _mode = TOUCH_MODE; 1122 | //debug_printf("<<< pah8002_enter_touch_mode\r\n"); 1123 | return true; 1124 | RTN: return false ; 1125 | } 1126 | 1127 | static bool pah8002_get_touch_flag( uint8_t *touch_flag) { 1128 | //debug_printf(">>> pah8002_touch_status \r\n"); 1129 | pah8002_wakeup(); 1130 | if (0 != pah8002_write_reg(0x7f, 0x02)) { 1131 | goto RTN; 1132 | } else if (0 != pah8002_read_reg(0x45, touch_flag)) // 1133 | { 1134 | goto RTN; 1135 | } 1136 | //debug_printf("<<< pah8002_touch_status %d\r\n", *touch_flag); 1137 | return true; 1138 | RTN: return false; 1139 | } 1140 | 1141 | static int pah8002_wakeup() { 1142 | int retry = 0 ; 1143 | int success = 0 ; 1144 | uint8_t data = 0 ; 1145 | pah8002_read_reg(0, &data); 1146 | pah8002_read_reg(0, &data); 1147 | do { 1148 | pah8002_write_reg(0x7f, 0x00); 1149 | pah8002_read_reg(0, &data); 1150 | if (data == 0x02) success++; else success = 0 ; 1151 | if (success >= 2) break; 1152 | retry ++; 1153 | } while (retry < 20); 1154 | 1155 | if (_chip_id == 0) { 1156 | pah8002_read_reg(0x02, &data); 1157 | _chip_id = data & 0xF0 ; 1158 | if (_chip_id != 0xD0) { 1159 | //debug_printf("Not support anymore\r\n"); 1160 | while (1) {}; 1161 | } 1162 | } 1163 | pah8002_write_reg(0x7f, 0x02); 1164 | pah8002_write_reg(0x70, 0x00); 1165 | //debug_printf("pah8002_wakeup retry %d \r\n", retry); 1166 | return retry; 1167 | } 1168 | 1169 | static int pah8002_check() { 1170 | int retry = 0 ; 1171 | int success = 0 ; 1172 | uint8_t data = 0 ; 1173 | uint8_t b1_0xd5 = 0 ; 1174 | uint8_t b1_0xe6 = 0 ; 1175 | pah8002_read_reg(0, &data); 1176 | pah8002_read_reg(0, &data); 1177 | do { 1178 | pah8002_write_reg(0x7f, 0x00); 1179 | pah8002_read_reg(0, &data); 1180 | if (data == 0x02) success++; else success = 0 ; 1181 | if (success >= 2) break; 1182 | retry ++; 1183 | } while (retry < 20); 1184 | pah8002_write_reg(0x7f, 0x01); 1185 | 1186 | pah8002_read_reg(0xd5, &b1_0xd5); 1187 | pah8002_read_reg(0xe6, &b1_0xe6); 1188 | //debug_printf("pah8002_check retry %d \r\n", retry); 1189 | if (b1_0xd5 != 1) 1190 | //debug_printf("pah8002_check error Bank1 0xD5 0x%x \r\n", b1_0xd5); 1191 | if (b1_0xe6 != 0xC8) 1192 | //debug_printf("pah8002_check error Bank1 0xE6 0x%x \r\n", b1_0xe6); 1193 | return retry; 1194 | } 1195 | 1196 | static bool pah8002_enter_suspend_mode() { 1197 | int i = 0 ; 1198 | //debug_printf("pah8002_enter_suspend_mode"); 1199 | pah8002_sw_reset(); 1200 | for (i = 0; i < SUSPEND_REG_ARRAY_SIZE; i++) { 1201 | if ( pah8002_write_reg(suspend_register_array[i][0], suspend_register_array[i][1]) != 0 ) { 1202 | return false; 1203 | } 1204 | } 1205 | _mode = SUSPEND_MODE; 1206 | pah8002_check(); 1207 | return true; 1208 | } 1209 | 1210 | static bool _pah8002_task() { 1211 | uint8_t cks[4] ; 1212 | uint8_t int_req = 0; 1213 | //debug_printf(">>> pah8002_task\n"); 1214 | pah8002_wakeup(); 1215 | if (0 != pah8002_write_reg(0x7f, 0x02)) { 1216 | 1217 | } else if (0 != pah8002_read_reg(0x73, &int_req)) { 1218 | 1219 | } else { 1220 | if ( (int_req & 0x04) != 0) { //overflow 1221 | while (1); 1222 | } 1223 | if ( (int_req & 0x02) != 0) 1224 | { //touch 1225 | //debug_printf("touch interrupt\n"); 1226 | } if ( (int_req & 0x08) != 0) { 1227 | //overflow 1228 | while (1); 1229 | } 1230 | if ( (int_req & 0x01) != 0) { 1231 | int samples_per_read = HEART_RATE_MODE_SAMPLES_PER_READ ; 1232 | //debug_printf("FIFO interrupt\n"); 1233 | //pah8002_get_touch_flag(&state->pah8002_touch_flag); 1234 | if (0 != pah8002_write_reg(0x7f, 0x03)) { 1235 | 1236 | } else if (0 != pah8002_burst_read_reg(0, pah8002_ppg_data, samples_per_read * 4)) { 1237 | 1238 | } else if (0 != pah8002_write_reg(0x7f, 0x02)) { 1239 | 1240 | } else if (0 != pah8002_burst_read_reg(0x80, cks, 4)) { 1241 | 1242 | } else if (0 != pah8002_write_reg(0x75, 0x01)) //read fifo first, then clear SRAM FIFO interrupt 1243 | {} else if (0 != pah8002_write_reg(0x75, 0x00)) { 1244 | 1245 | } else { 1246 | uint32_t *s = (uint32_t *)pah8002_ppg_data; 1247 | uint32_t cks_cal = *s; 1248 | uint32_t cks_rx = *((uint32_t *)cks) ; 1249 | uint32_t i ; //checksum compare 1250 | for (i = 1; i < samples_per_read; i++) { 1251 | cks_cal = cks_cal ^ (*(s + i)) ; 1252 | } 1253 | if (cks_cal != cks_rx) { 1254 | //debug_printf("checksum error\r\n"); 1255 | } else { 1256 | //debug_printf("checksum OK %d\r\n", cks_cal); 1257 | } 1258 | _touch_flag = pah8002_get_touch_flag_ppg_mode(); 1259 | } 1260 | } else { 1261 | //debug_printf("not fifo interrupt%d\r\n", int_req); 1262 | } 1263 | } 1264 | //debug_printf("<<< pah8002_task\n"); 1265 | return true; 1266 | } 1267 | 1268 | static bool pah8002_normal_long_et_mode_init() { 1269 | int i = 0 ; 1270 | //debug_printf(">>> pah8002_normal_long_et_mode_init \r\n"); 1271 | pah8002_wakeup(); 1272 | for (i = 0; i < INIT_PPG_LONG_REG_ARRAY_SIZE; i++) { 1273 | if ( pah8002_write_reg( init_ppg_long_register_array[i][0], init_ppg_long_register_array[i][1]) != 0 ) { 1274 | goto RTN; 1275 | } 1276 | } 1277 | //debug_printf("<<< pah8002_normal_long_et_mode_init \r\n"); 1278 | return true; 1279 | RTN: return false; 1280 | } 1281 | 1282 | static bool pah8002_enter_normal_long_et_mode() { 1283 | //debug_printf(">>> pah8002_enter_normal_long_et_mode\r\n"); 1284 | if (_mode == NORMAL_LONG_ET_MODE) return true; //1. software reset 1285 | if ( !pah8002_sw_reset()) 1286 | goto RTN; 1287 | //2. load registers for normal mode 1288 | if ( !pah8002_normal_long_et_mode_init()) 1289 | goto RTN; 1290 | pah8002_write_reg(0x7f, 0x00); //Bank0 1291 | pah8002_read_reg(0x0D, &_ir_expo); // IR Exposure Time 1292 | pah8002_write_reg(0x7f, 0x01); //Bank1 1293 | pah8002_read_reg(0xBA, &_ir_dac); //IR Led DAC 1294 | //3. enable sensor 1295 | if ( !pah8002_start()) goto RTN; 1296 | _mode = NORMAL_LONG_ET_MODE; 1297 | //debug_printf("<<< pah8002_enter_normal_long_et_mode ir_dac %x, ir_expo %x\r\n", _ir_dac, _ir_expo); 1298 | return true; 1299 | RTN: return false ; 1300 | } 1301 | 1302 | static void pah8002_dyn_switch_ppg_mode() { 1303 | uint8_t by2a4, by2a5 ; 1304 | uint16_t value ; 1305 | pah8002_wakeup(); 1306 | pah8002_write_reg(0x7F, 0x02); 1307 | pah8002_read_reg(0xa4, &by2a4); 1308 | pah8002_read_reg(0xa5, &by2a5); 1309 | value = by2a5 ; 1310 | value <<= 8 ; 1311 | value += by2a4 ; 1312 | if (value > 4639) { 1313 | pah8002_enter_normal_long_et_mode(); 1314 | } 1315 | } 1316 | 1317 | //---------------------------------------PAH8002 functions----------------------------------------------- 1318 | 1319 | bool pah8002_init(void) { 1320 | 1321 | uint8_t ret = 0; 1322 | uint32_t open_size = 0; 1323 | //Algorithm initialization 1324 | _pah8002_data.frame_count = 0 ; 1325 | _pah8002_data.nf_ppg_channel = TOTAL_CHANNELS_FOR_ALG; 1326 | _pah8002_data.nf_ppg_per_channel = HEART_RATE_MODE_SAMPLES_PER_CH_READ; 1327 | _pah8002_data.ppg_data = (int32_t *)pah8002_ppg_data; 1328 | #ifdef MEMS_ZERO 1329 | memset(_mems_data, 0, sizeof(_mems_data)); 1330 | _pah8002_data.nf_mems = HEART_RATE_MODE_SAMPLES_PER_CH_READ; 1331 | _pah8002_data.mems_data = _mems_data; 1332 | #endif 1333 | open_size = pah8series_query_open_size(); 1334 | _pah8002_alg_buffer = malloc(open_size); 1335 | ret = pah8series_open(_pah8002_alg_buffer); 1336 | if (ret != MSG_SUCCESS) return false; // Set 0: +/-2G, 1: +/-4G, 2: +/-8G, 3: +/-16G 1337 | if (MSG_SUCCESS != pah8series_set_param(PAH8SERIES_PARAM_IDX_GSENSOR_MODE, 1)) return false; 1338 | //log_printf("PPG CH#, %d\n", TOTAL_CHANNELS_FOR_ALG); 1339 | delay(300); 1340 | #ifdef PPG_MODE_ONLY 1341 | return pah8002_enter_normal_mode(); 1342 | #else 1343 | return pah8002_enter_touch_mode(); 1344 | #endif 1345 | } 1346 | 1347 | void pah8002_deinit(void) { 1348 | pah8002_enter_suspend_mode(); 1349 | pah8series_close(); 1350 | if (_pah8002_alg_buffer) { 1351 | free(_pah8002_alg_buffer); 1352 | _pah8002_alg_buffer = NULL; 1353 | } 1354 | } 1355 | 1356 | 1357 | void pah8002_log(void) { 1358 | int i = 0 ; 1359 | uint32_t *ppg_data = (uint32_t *)_pah8002_data.ppg_data ; 1360 | int16_t *mems_data = _pah8002_data.mems_data ; 1361 | //log_printf("Frame Count, %d \n", _pah8002_data.frame_count); 1362 | //log_printf("Time, %d \n", _pah8002_data.time); 1363 | //log_printf("PPG, %d, %d, ", _pah8002_data.touch_flag, _pah8002_data.nf_ppg_per_channel); 1364 | for (i = 0; i < _pah8002_data.nf_ppg_channel * _pah8002_data.nf_ppg_per_channel; i++) { 1365 | //log_printf("%d, ", *ppg_data); 1366 | ppg_data ++; 1367 | } 1368 | //log_printf("\n"); 1369 | //log_printf("MEMS, %d, ", _pah8002_data.nf_mems); 1370 | for (i = 0; i < _pah8002_data.nf_mems * 3; i++) { 1371 | //log_printf("%d, ", *mems_data); 1372 | mems_data ++; 1373 | } 1374 | //log_printf("\n"); 1375 | } 1376 | 1377 | static void data_convert_4ch_to_3ch(uint32_t *pdata, uint32_t len) { 1378 | uint32_t i = 0, j = 0; 1379 | for (i = 0, j = 2; j < len; i += 3, j += 4) { 1380 | *(pdata + i + 1) = *(pdata + j); 1381 | *(pdata + i + 2) = *(pdata + j + 1); 1382 | } 1383 | } 1384 | 1385 | void pah8002_task(void) { 1386 | uint8_t ret; 1387 | float hr = 0 ; 1388 | uint32_t sys_tick; 1389 | if (_pah8002_interrupt == 1) { 1390 | _pah8002_interrupt = 0; 1391 | if (_mode == TOUCH_MODE) { 1392 | pah8002_enter_normal_mode(); 1393 | _timestamp = millis(); 1394 | //accelerometer_start(); 1395 | } else if (_mode == NORMAL_MODE || _mode == NORMAL_LONG_ET_MODE) { 1396 | _pah8002_task(); 1397 | pah8002_dyn_switch_ppg_mode(); 1398 | 1399 | #ifdef PPG_MODE_ONLY 1400 | 1401 | #else 1402 | if (_touch_flag == 0) { 1403 | pah8002_enter_touch_mode(); 1404 | //accelerometer_stop(); 1405 | } 1406 | #endif 1407 | //process algorithm 1408 | #ifdef MEMS_ZERO 1409 | #else 1410 | //accelerometer_get_fifo(&_pah8002_data.mems_data, &_pah8002_data.nf_mems); 1411 | #endif 1412 | sys_tick = millis(); 1413 | _pah8002_data.time = sys_tick - _timestamp; 1414 | _timestamp = sys_tick; 1415 | _pah8002_data.touch_flag = _touch_flag; 1416 | data_convert_4ch_to_3ch((uint32_t *)pah8002_ppg_data, HEART_RATE_MODE_SAMPLES_PER_READ); 1417 | // log 3ch ppg_data before pah8002_entrance() 1418 | pah8002_log(); 1419 | ret = pah8series_entrance(&_pah8002_data); 1420 | if ((ret & 0x0f) != 0) { 1421 | switch (ret) //check error status 1422 | { case MSG_ALG_NOT_OPEN: 1423 | //debug_printf("Algorithm is not initialized.\r\n"); 1424 | break; 1425 | case MSG_MEMS_LEN_TOO_SHORT: 1426 | //debug_printf("MEMS data length is shorter than PPG data length.\r\n"); 1427 | break; 1428 | case MSG_NO_TOUCH: 1429 | //debug_printf("PPG is no touch.\r\n"); 1430 | break; 1431 | case MSG_PPG_LEN_TOO_SHORT: 1432 | //debug_printf("PPGdata length is too short.\r\n"); 1433 | break; 1434 | case MSG_FRAME_LOSS: 1435 | //debug_printf("Frame count is not continuous.\r\n"); 1436 | break; 1437 | } 1438 | } 1439 | if ((ret & 0xf0) == MSG_HR_READY) { 1440 | pah8series_get_hr(&hr) ; 1441 | //debug_printf("HR = %d\r\n", (int)(hr)); 1442 | // display.setCursor(10, 40); 1443 | // display.setTextColor(LCD_COLOR_BLACK); 1444 | // display.setTextSize(2); 1445 | // display.print("HR: "); 1446 | // display.print(hr); 1447 | // display.refresh(); 1448 | } 1449 | _pah8002_data.frame_count++; 1450 | } 1451 | } 1452 | } 1453 | 1454 | void pah8002_intr_isr(void) { 1455 | _pah8002_interrupt = 1 ; 1456 | } 1457 | 1458 | //pah8002_comm_i2c.c 1459 | 1460 | 1461 | uint8_t pah8002_write_reg(uint8_t addr, uint8_t data) { 1462 | //return i2c_write_reg(I2C_ID_PAH8002, addr, data); 1463 | Wire.beginTransmission(I2C_ID_PAH8002); 1464 | Wire.write(addr); 1465 | Wire.write(data); 1466 | Wire.endTransmission(); 1467 | return 1; 1468 | } 1469 | 1470 | uint8_t pah8002_read_reg(uint8_t addr, uint8_t *data) { 1471 | //return i2c_read_reg(I2C_ID_PAH8002, addr, data); 1472 | Wire.beginTransmission(I2C_ID_PAH8002); 1473 | Wire.write(addr); 1474 | Wire.endTransmission(); 1475 | Wire.requestFrom(I2C_ID_PAH8002, 1); 1476 | int intData = Wire.read(); 1477 | uint8_t byteData =lowByte(intData); 1478 | data=&byteData; 1479 | return 1; 1480 | } 1481 | 1482 | uint8_t pah8002_burst_read_reg(uint8_t addr, uint8_t *data, uint32_t rx_size) { 1483 | //return i2c_burst_read_reg(I2C_ID_PAH8002, addr, data, rx_size); 1484 | 1485 | Wire.beginTransmission(I2C_ID_PAH8002); 1486 | Wire.write(addr); 1487 | Wire.endTransmission(); 1488 | 1489 | Wire.requestFrom(I2C_ID_PAH8002, rx_size); 1490 | 1491 | int i = 0; 1492 | while (Wire.available() && i < rx_size) { 1493 | data[i] = Wire.read(); 1494 | // int intData = Wire.read(); 1495 | // uint8_t byteData =highByte(intData); 1496 | // data[i]=&byteData; 1497 | i++; 1498 | } 1499 | 1500 | if (i != rx_size) return 0; else return 1; //success or failure 1501 | return 1; 1502 | 1503 | } 1504 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SMA-Q2-Firmware-3 2 | 3rd version for a SMA-Q2 Arduino firmware based on the sandeepmistry nrf5 core and the pfod_lp_nrf52.zip low power core mods 3 | see https://www.forward.com.au/pfod/BLE/LowPower/index.html for more info 4 | 5 | Implements basic watch functionality with over 1 week always on and advertising standby time. 6 | Basic menue structure with "Apps" is implemented. Since the pfod low power core does not implement interrupts, we have to poll the buttons. The lp_comparator does not work for me. 7 | Press the DFU bootloader combo inside the watch screen. 8 | In order to set date and time send a string in the format yyyymmddhhmmss while you're in the main menu. 9 | 10 | 11 | Preparation 12 | 13 | 1. install the sandeepmistry nrf5 core 14 | 2. replace the hardware folder inside C:\Users\yourComputer\AppData\Local\Arduino15\packages\sandeepmistry with the provided one 15 | 3. put the entire sketch folder into your sktch directory 16 | 17 | If you want to experiment with the precomiled lib for the HR sensor (compiles but does not work yet, also raises the default power consumption from 50µA to 500µA): 18 | Source: https://os.mbed.com/users/pixus_mbed/code/PixArt_PAH8011_HeartRate_nRF52/ 19 | 1. put the folder PAH8002HRmon into C:\Users\yourComputer\AppData\Local\Arduino15\packages\sandeepmistry\hardware\nRF5\0.6.0\libraries 20 | 3. put the content of the PAH8002driver folder into the sketch directory 21 | 4. uncomment the necessary lines in the SMAQ2_FW3.ino main sketch file (the includes, the PAH8002HRmon HRsensor; line, Wire.h stuff......) 22 | 5. inside C:\Users\yourComputer\AppData\Local\Arduino15\packages\sandeepmistry\hardware\nRF5\0.6.0 replace the original platform.txt file 23 | with the "platform [for precompiled libs]].txt" naming it accordingly or uncomment the two necessary lines inside the original file... 24 | 6. try to get it up and running 25 | 26 | The following tricks accelerate your development cycles (the necessary nrfutil.exe is included): 27 | press ctrl + alt + 'S' to compile and export hex 28 | C:\Users\yourComputer\Documents\Arduino\SMAQ2_FW3\nrfutil dfu genpkg --application C:\Users\yourComputer\Documents\Arduino\SMAQ2_FW3\SMAQ2_FW3.ino.SMA_Q2.hex --sd-req 0x88 C:\Users\yourComputer\Documents\Arduino\SMAQ2_FW3\SMAQ2_FW3.zip 29 | if you have http://bluetoothinstaller.com/bluetooth-command-line-tools/btobex.html installed: btobex -n "Galaxy S5" "C:\Users\yourComputer\Documents\Arduino\SMAQ2_FW3\SMAQ2_FW3.zip" 30 | -------------------------------------------------------------------------------- /SMAQ2_FW3.ino: -------------------------------------------------------------------------------- 1 | /* 2 | 3rd version for a SMA-Q2 Arduino firmware based on the sandeepmistry nrf5 core and the pfod_lp_nrf52.zip low power core mods 3 | see https://www.forward.com.au/pfod/BLE/LowPower/index.html for more info 4 | 5 | Implements basic watch functionality with over 1 week always on and advertising standby time. 6 | Basic menue structure with "Apps" is implemented. Since the pfod low power core does not implement interrupts, we have to poll the buttons. The lp_comparator does not work for me. 7 | Press the DFU bootloader combo inside the watch screen. 8 | In order to set date and time send a string via the Nordic BLE Uart App (available in the App Stores) in the format yyyymmddhhmmss while you're in the main menu. 9 | 10 | Setup: 11 | 1. install the sandeepmistry nrf5 core 12 | 2. replace the hardware folder inside C:\Users\yourComputer\AppData\Local\Arduino15\packages\sandeepmistry with the provided one 13 | 3. put the entire sketch folder into your sktch directory 14 | 15 | The following tricks accelerate your development cycles: 16 | press ctrl + alt + 'S' to compile and export hex 17 | C:\Users\Kauzdesktop\Documents\Arduino\SMAQ2_FW3\nrfutil dfu genpkg --application C:\Users\Kauzdesktop\Documents\Arduino\SMAQ2_FW3\SMAQ2_FW3.ino.SMA_Q2.hex --sd-req 0x88 C:\Users\Kauzdesktop\Documents\Arduino\SMAQ2_FW3\SMAQ2_FW3.zip 18 | btobex -n "Galaxy S5" "C:\Users\Kauzdesktop\Documents\Arduino\SMAQ2_FW3\SMAQ2_FW3.zip 19 | 20 | A. Jordan 2019 21 | */ 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "Adafruit_GFX.h" 27 | #include "ColorMemLCD.h" 28 | //#include "RTCInt.h" 29 | #include "TimeLib.h" 30 | #include 31 | #include //interrupt controller stuff 32 | #include 33 | #include 34 | #include 35 | #include "i2csoft.h" 36 | //#include //the pre-compiled library for the HR sensor PAH8002 37 | //#include "PAH8002.h" //the driver from the manufacturer's application note 38 | //#include 39 | 40 | // SMA-Q2 pins 41 | #define SCK 2 42 | #define MOSI 3 43 | #define SS 5 44 | #define EXTCOMIN 6 45 | #define DISP 7 46 | 47 | #define VBAT 4 //with a 1/4 divider 48 | 49 | #define VIBRO 30 50 | #define BCKLT 8 51 | 52 | #define BTN_BCK 9 53 | #define BTN_UP 27 54 | #define BTN_OK 28 55 | #define BTN_DOWN 29 56 | 57 | #define FLASH_MISO 11 58 | #define FLASH_MOSI 13 59 | #define FLASH_CS 12 60 | #define FLASH_CLK 14 61 | 62 | #define ACCEL_PWR 16 63 | #define ACCEL_INT 15 64 | #define ACCEL_SDA 17 65 | #define ACCEL_SCL 18 66 | 67 | #define HRM_SDA 19 68 | #define HRM_SCL 20 69 | #define HRM_INT 22 70 | #define HRM_RST 10 71 | #define HRM_PWR 31 72 | 73 | #define CHG_STAT 23 74 | #define CHG_PGOOD 24 75 | 76 | #define UART_RXD 25 77 | #define UART_TXD 26 78 | 79 | 80 | bool bleConnected, charging, lastConnStat= false; 81 | bool bltOn = false; 82 | bool extcominState = false; 83 | 84 | lp_timer accelSamplingTimer; 85 | lp_timer clockTimer; 86 | lp_timer EXCOMINtimer; 87 | lp_timer debounceTimerOK, debounceTimerDOWN; 88 | //RTCInt rtc; //create an RTCInt type object 89 | lp_BLESerial ble; 90 | ColorMemLCD display(SCK, MOSI, SS, EXTCOMIN); 91 | //PAH8002HRmon HRsensor; //create instance of precompiled HR sensor lib 92 | 93 | uint32_t debounceTimeOut = 20; // mS 94 | const unsigned long DELAY_TIME = 1000; // mS == 1sec 95 | int lastButtonStateBCK, lastButtonStateOK, lastButtonStateUP, lastButtonStateDOWN = -1; // not set initially 96 | int buttonStateBCK, buttonStateOK, buttonStateUP, buttonStateDOWN = -1; // not set initially 97 | char bleInString[100]; 98 | int bleInIndx = 0; 99 | 100 | 101 | long buttonTimer, buttonTimerOK = 0; 102 | const int longPressTime = 3000; 103 | boolean buttonActive, buttonActiveOK = false; 104 | boolean longPressActive, longPressActiveOK = false; 105 | 106 | bool insideMenue = false; 107 | 108 | // the setup routine runs once when you press reset: 109 | void setup() { 110 | pinMode(BCKLT, OUTPUT); 111 | pinMode(VIBRO, OUTPUT); 112 | pinMode(ACCEL_PWR, OUTPUT); 113 | pinMode(HRM_PWR, OUTPUT); 114 | pinMode(CHG_STAT, INPUT); 115 | 116 | pinMode(BTN_BCK, INPUT_PULLUP); 117 | pinMode(BTN_UP, INPUT_PULLUP); 118 | pinMode(BTN_OK, INPUT_PULLUP); 119 | pinMode(BTN_DOWN, INPUT_PULLUP); 120 | pinMode(DISP, OUTPUT); 121 | //attachInterrupt(BTN_UP, btn_up_isr, FALLING); 122 | digitalWrite(DISP, HIGH); 123 | digitalWrite(BCKLT, LOW); 124 | digitalWrite(VIBRO, LOW); 125 | digitalWrite(ACCEL_PWR, HIGH); //turn on the accelerometer 126 | digitalWrite(HRM_PWR, LOW); 127 | 128 | display.begin(); 129 | 130 | delay(100); 131 | display.clearDisplay(); 132 | // display.setFont(&FreeSerifItalic9pt7b); 133 | display.refresh(); 134 | display.setTextSize(3); 135 | display.setTextColor(LCD_COLOR_RED); 136 | display.setCursor(20, 85); 137 | display.println("FW 3.0"); 138 | display.refresh(); 139 | delay(100); 140 | //Wire.begin(); 141 | initi2c(); 142 | delay(10); 143 | initkx023(); 144 | delay(10); 145 | // initialize the digital pin as an output. 146 | 147 | 148 | ble.setName("SMA-Q2custom"); // set advertised name, default name is "Nordic BLE UART" 149 | ble.setConnectedHandler(handleConnection); // when a connection is made 150 | ble.setDisconnectedHandler(handleDisconnection); // when the connection is dropped or goes out of range 151 | ble.begin(); // start advertising and be ready to accept connections 152 | 153 | ble.setTxPower(-8); 154 | 155 | 156 | // comparator pins for nRF52832 157 | // INPUT0 P0_02 158 | // INPUT1 P0_03 159 | // INPUT2 P0_04 160 | // INPUT3 P0_05 161 | // INPUT4 P0_28 OK 162 | // INPUT5 P0_29 DOWN 163 | // INPUT6 P0_30 164 | // INPUT7 P0_31 165 | 166 | //lp comparator stuff does not work 167 | lp_comparator_start(BTN_OK, REF_8_16Vdd, handlePinLevelChangeOK); // always triggers pin LOW first, then if pin HIGH, will trigger HIGH 168 | lp_comparator_start(BTN_DOWN, REF_8_16Vdd, handlePinLevelChangeDOWN); //EIGENTLICH btn_dOWN1111 169 | //attachInterrupt(BTN_BCK, handlePinLevelChangeBCK, FALLING); 170 | 171 | 172 | clockTimer.startTimer(60000, displayTime); //this is the timer for the watch face refresh 173 | EXCOMINtimer.startTimer(500, disp_toggle); //this toggles the EXTCOMIN pin 174 | 175 | 176 | 177 | } 178 | 179 | void loop() { 180 | wdt_feed(); //keep this in your main loop to reset automatically after 10s if something goes wrong within loop() 181 | sleep(); // just sleep here waiting for the timer to trigger 182 | 183 | readBckBtn(); //keep this in your main loop 184 | readOkBtn(); 185 | readDFUcombo(); 186 | 187 | if (digitalRead(CHG_STAT) == LOW) { 188 | charging = true; 189 | 190 | } else charging = false; 191 | 192 | } //LOOP 193 | 194 | 195 | 196 | 197 | //implements long press for power off and short press for backlight toggle 198 | void readBckBtn() { 199 | 200 | if (digitalRead(BTN_BCK) == LOW) { 201 | if (buttonActive == false) { 202 | buttonActive = true; 203 | buttonTimer = millis(); 204 | } 205 | if ((millis() - buttonTimer > longPressTime) && (longPressActive == false)) { 206 | longPressActive = true; 207 | //button has been pressed for a long time 208 | //set any pin and wakeup logic here. 209 | // systemOff(BTN_UP, LOW); //implemented here: 210 | // digitalWrite(VIBRO, HIGH); 211 | // delay(60); 212 | // digitalWrite(VIBRO, LOW); 213 | 214 | digitalWrite(DISP, LOW); 215 | digitalWrite(BCKLT, LOW); 216 | digitalWrite(VIBRO, LOW); 217 | digitalWrite(ACCEL_PWR, LOW); //turn on the accelerometer 218 | digitalWrite(HRM_PWR, LOW); 219 | delay(2000); 220 | NRF_SAADC ->ENABLE = 0; //disable ADC 221 | NRF_PWM0 ->ENABLE = 0; //disable all pwm instance 222 | NRF_PWM1 ->ENABLE = 0; 223 | NRF_PWM2 ->ENABLE = 0; 224 | //nrf_gpiote_event_clear(NRF_GPIOTE_EVENTS_PORT); 225 | // nrf_drv_gpiote_in_uninit(); 226 | powerOff(); //here the watch draws about as much as in "on" mode - not quite useful except for reset purposes!! 227 | while (1); 228 | } 229 | } else { 230 | if (buttonActive == true) { 231 | if (longPressActive == true) { 232 | longPressActive = false; 233 | } else { 234 | //button has been pressed for a short time 235 | toggleBLT(); 236 | 237 | 238 | 239 | } 240 | buttonActive = false; 241 | } 242 | } 243 | 244 | } 245 | 246 | 247 | //implements long press for power off and short press for backlight toggle 248 | void readOkBtn() { 249 | 250 | if (digitalRead(BTN_OK) == LOW) { 251 | if (buttonActiveOK == false) { 252 | buttonActiveOK = true; 253 | buttonTimerOK = millis(); 254 | } 255 | if ((millis() - buttonTimerOK > longPressTime) && (longPressActiveOK == false)) { 256 | longPressActiveOK = true; 257 | //button has been pressed for a long time 258 | //set any pin and wakeup logic here. 259 | // systemOff(BTN_UP, LOW); //implemented here: 260 | digitalWrite(VIBRO, HIGH); 261 | delay(60); 262 | digitalWrite(VIBRO, LOW); 263 | } 264 | } else { 265 | if (buttonActiveOK == true) { 266 | if (longPressActiveOK == true) { 267 | longPressActiveOK = false; 268 | } else { 269 | //button has been pressed for a short time 270 | menue(); 271 | 272 | } 273 | buttonActiveOK = false; 274 | } 275 | } 276 | 277 | } 278 | 279 | 280 | 281 | void displayTime() { 282 | if (!insideMenue) { 283 | display.clearDisplay(); 284 | display.fillRect(0, 40, 176, 176, LCD_COLOR_YELLOW); 285 | display.setTextSize(5); 286 | display.setTextColor(LCD_COLOR_BLUE); 287 | //display.setFont(&DSEG7_dig_only); 288 | display.setCursor(15, 70); 289 | if (hour() < 10) display.print("0"); 290 | display.print(hour()); 291 | display.setTextColor(LCD_COLOR_GREEN); 292 | display.print(":"); 293 | //display.setCursor(4, 50); 294 | //display.setCursor(10, 45); 295 | 296 | display.setTextColor(LCD_COLOR_MAGENTA); 297 | if (minute() < 10) display.print("0"); 298 | display.print(minute()); 299 | //display.print(":"); 300 | //display.setFont(NULL); 301 | //display.setCursor(4, 70); 302 | // if (second() < 10) display.print("0"); 303 | // display.print(second()); 304 | // display.print(" "); 305 | 306 | display.setCursor(40, 150); 307 | display.setTextSize(2); 308 | display.setTextColor(LCD_COLOR_RED); 309 | if (day() < 10) display.print("0"); 310 | display.print(day()); 311 | display.print("."); 312 | if (month() < 10) display.print("0"); 313 | display.print(month()); 314 | display.print("."); 315 | display.println(year()); 316 | display.setTextColor(LCD_COLOR_BLACK); 317 | display.setTextSize(1); 318 | display.setCursor(0, 11); 319 | display.print(getBatteryLevel()); 320 | display.println("%"); 321 | display.setCursor(168, 11); 322 | if (bleConnected) display.print("B"); else display.print(" "); 323 | display.setCursor(65, 11); 324 | if (charging) display.print("charging"); else display.print(" "); 325 | display.refresh(); 326 | } 327 | } 328 | 329 | 330 | 331 | 332 | //button handling stuff 333 | // called when pin changes state, pinState is state detected, HIGH or LOW 334 | void handlePinLevelChangeOK(int pinStateOK) { 335 | 336 | if (pinStateOK != lastButtonStateOK) { 337 | lastButtonStateOK = pinStateOK; 338 | debounceTimerOK.stop(); // stop last timer if any 339 | debounceTimerOK.startDelay(debounceTimeOut, handleDebounceTimeoutOK); 340 | } 341 | } 342 | 343 | 344 | void handlePinLevelChangeDOWN(int pinStateDOWN) { 345 | 346 | if (pinStateDOWN != lastButtonStateDOWN) { 347 | lastButtonStateDOWN = pinStateDOWN; 348 | debounceTimerDOWN.stop(); // stop last timer if any 349 | debounceTimerDOWN.startDelay(debounceTimeOut, handleDebounceTimeoutDOWN); 350 | } 351 | } 352 | 353 | void handleDebounceTimeoutOK() { 354 | buttonStateOK = lastButtonStateOK; // input has settled 355 | // ble.print("maxQ:"); ble.print(app_sched_queue_utilization_get()); //needs #define SCHEDULER_PROFILER in utility/app_schedule.h 356 | //ble.print(' '); ble.print((buttonState == HIGH) ? 'H' : 'L'); 357 | //digitalWrite(led, buttonState); // turn the LED on when input HIGH 358 | //menue(); 359 | //toggleBLT(); 360 | } 361 | 362 | void handleDebounceTimeoutDOWN() { 363 | buttonStateDOWN = lastButtonStateDOWN; // input has settled 364 | // ble.print("maxQ:"); ble.print(app_sched_queue_utilization_get()); //needs #define SCHEDULER_PROFILER in utility/app_schedule.h 365 | //ble.print(' '); ble.print((buttonState == HIGH) ? 'H' : 'L'); 366 | //digitalWrite(led, buttonState); // turn the LED on when input HIGH 367 | 368 | toggleBLT(); 369 | } 370 | -------------------------------------------------------------------------------- /SMAQ2_FW3.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigCorvus/SMA-Q2-Firmware-3/3ab7506ee2e191e48584a77ff89700214d9b11ac/SMAQ2_FW3.zip -------------------------------------------------------------------------------- /Time.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | time.c - low level time and date functions 3 | Copyright (c) Michael Margolis 2009-2014 4 | 5 | This library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with this library; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | 19 | 1.0 6 Jan 2010 - initial release 20 | 1.1 12 Feb 2010 - fixed leap year calculation error 21 | 1.2 1 Nov 2010 - fixed setTime bug (thanks to Korman for this) 22 | 1.3 24 Mar 2012 - many edits by Paul Stoffregen: fixed timeStatus() to update 23 | status, updated examples for Arduino 1.0, fixed ARM 24 | compatibility issues, added TimeArduinoDue and TimeTeensy3 25 | examples, add error checking and messages to RTC examples, 26 | add examples to DS1307RTC library. 27 | 1.4 5 Sep 2014 - compatibility with Arduino 1.5.7 28 | */ 29 | 30 | #if ARDUINO >= 100 31 | #include 32 | #else 33 | #include 34 | #endif 35 | 36 | #include "TimeLib.h" 37 | 38 | static tmElements_t tm; // a cache of time elements 39 | static time_t cacheTime; // the time the cache was updated 40 | static uint32_t syncInterval = 300; // time sync will be attempted after this many seconds 41 | 42 | void refreshCache(time_t t) { 43 | if (t != cacheTime) { 44 | breakTime(t, tm); 45 | cacheTime = t; 46 | } 47 | } 48 | 49 | int hour() { // the hour now 50 | return hour(now()); 51 | } 52 | 53 | int hour(time_t t) { // the hour for the given time 54 | refreshCache(t); 55 | return tm.Hour; 56 | } 57 | 58 | int hourFormat12() { // the hour now in 12 hour format 59 | return hourFormat12(now()); 60 | } 61 | 62 | int hourFormat12(time_t t) { // the hour for the given time in 12 hour format 63 | refreshCache(t); 64 | if( tm.Hour == 0 ) 65 | return 12; // 12 midnight 66 | else if( tm.Hour > 12) 67 | return tm.Hour - 12 ; 68 | else 69 | return tm.Hour ; 70 | } 71 | 72 | uint8_t isAM() { // returns true if time now is AM 73 | return !isPM(now()); 74 | } 75 | 76 | uint8_t isAM(time_t t) { // returns true if given time is AM 77 | return !isPM(t); 78 | } 79 | 80 | uint8_t isPM() { // returns true if PM 81 | return isPM(now()); 82 | } 83 | 84 | uint8_t isPM(time_t t) { // returns true if PM 85 | return (hour(t) >= 12); 86 | } 87 | 88 | int minute() { 89 | return minute(now()); 90 | } 91 | 92 | int minute(time_t t) { // the minute for the given time 93 | refreshCache(t); 94 | return tm.Minute; 95 | } 96 | 97 | int second() { 98 | return second(now()); 99 | } 100 | 101 | int second(time_t t) { // the second for the given time 102 | refreshCache(t); 103 | return tm.Second; 104 | } 105 | 106 | int day(){ 107 | return(day(now())); 108 | } 109 | 110 | int day(time_t t) { // the day for the given time (0-6) 111 | refreshCache(t); 112 | return tm.Day; 113 | } 114 | 115 | int weekday() { // Sunday is day 1 116 | return weekday(now()); 117 | } 118 | 119 | int weekday(time_t t) { 120 | refreshCache(t); 121 | return tm.Wday; 122 | } 123 | 124 | int month(){ 125 | return month(now()); 126 | } 127 | 128 | int month(time_t t) { // the month for the given time 129 | refreshCache(t); 130 | return tm.Month; 131 | } 132 | 133 | int year() { // as in Processing, the full four digit year: (2009, 2010 etc) 134 | return year(now()); 135 | } 136 | 137 | int year(time_t t) { // the year for the given time 138 | refreshCache(t); 139 | return tmYearToCalendar(tm.Year); 140 | } 141 | 142 | /*============================================================================*/ 143 | /* functions to convert to and from system time */ 144 | /* These are for interfacing with time serivces and are not normally needed in a sketch */ 145 | 146 | // leap year calulator expects year argument as years offset from 1970 147 | #define LEAP_YEAR(Y) ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) ) 148 | 149 | static const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31}; // API starts months from 1, this array starts from 0 150 | 151 | void breakTime(time_t timeInput, tmElements_t &tm){ 152 | // break the given time_t into time components 153 | // this is a more compact version of the C library localtime function 154 | // note that year is offset from 1970 !!! 155 | 156 | uint8_t year; 157 | uint8_t month, monthLength; 158 | uint32_t time; 159 | unsigned long days; 160 | 161 | time = (uint32_t)timeInput; 162 | tm.Second = time % 60; 163 | time /= 60; // now it is minutes 164 | tm.Minute = time % 60; 165 | time /= 60; // now it is hours 166 | tm.Hour = time % 24; 167 | time /= 24; // now it is days 168 | tm.Wday = ((time + 4) % 7) + 1; // Sunday is day 1 169 | 170 | year = 0; 171 | days = 0; 172 | while((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) { 173 | year++; 174 | } 175 | tm.Year = year; // year is offset from 1970 176 | 177 | days -= LEAP_YEAR(year) ? 366 : 365; 178 | time -= days; // now it is days in this year, starting at 0 179 | 180 | days=0; 181 | month=0; 182 | monthLength=0; 183 | for (month=0; month<12; month++) { 184 | if (month==1) { // february 185 | if (LEAP_YEAR(year)) { 186 | monthLength=29; 187 | } else { 188 | monthLength=28; 189 | } 190 | } else { 191 | monthLength = monthDays[month]; 192 | } 193 | 194 | if (time >= monthLength) { 195 | time -= monthLength; 196 | } else { 197 | break; 198 | } 199 | } 200 | tm.Month = month + 1; // jan is month 1 201 | tm.Day = time + 1; // day of month 202 | } 203 | 204 | time_t makeTime(tmElements_t &tm){ 205 | // assemble time elements into time_t 206 | // note year argument is offset from 1970 (see macros in time.h to convert to other formats) 207 | // previous version used full four digit year (or digits since 2000),i.e. 2009 was 2009 or 9 208 | 209 | int i; 210 | uint32_t seconds; 211 | 212 | // seconds from 1970 till 1 jan 00:00:00 of the given year 213 | seconds= tm.Year*(SECS_PER_DAY * 365); 214 | for (i = 0; i < tm.Year; i++) { 215 | if (LEAP_YEAR(i)) { 216 | seconds += SECS_PER_DAY; // add extra days for leap years 217 | } 218 | } 219 | 220 | // add days for this year, months start from 1 221 | for (i = 1; i < tm.Month; i++) { 222 | if ( (i == 2) && LEAP_YEAR(tm.Year)) { 223 | seconds += SECS_PER_DAY * 29; 224 | } else { 225 | seconds += SECS_PER_DAY * monthDays[i-1]; //monthDay array starts from 0 226 | } 227 | } 228 | seconds+= (tm.Day-1) * SECS_PER_DAY; 229 | seconds+= tm.Hour * SECS_PER_HOUR; 230 | seconds+= tm.Minute * SECS_PER_MIN; 231 | seconds+= tm.Second; 232 | return (time_t)seconds; 233 | } 234 | /*=====================================================*/ 235 | /* Low level system time functions */ 236 | 237 | static uint32_t sysTime = 0; 238 | static uint32_t prevMillis = 0; 239 | static uint32_t nextSyncTime = 0; 240 | static timeStatus_t Status = timeNotSet; 241 | 242 | getExternalTime getTimePtr; // pointer to external sync function 243 | //setExternalTime setTimePtr; // not used in this version 244 | 245 | #ifdef TIME_DRIFT_INFO // define this to get drift data 246 | time_t sysUnsyncedTime = 0; // the time sysTime unadjusted by sync 247 | #endif 248 | 249 | 250 | time_t now() { 251 | // calculate number of seconds passed since last call to now() 252 | while (millis() - prevMillis >= 1000) { 253 | // millis() and prevMillis are both unsigned ints thus the subtraction will always be the absolute value of the difference 254 | sysTime++; 255 | prevMillis += 1000; 256 | #ifdef TIME_DRIFT_INFO 257 | sysUnsyncedTime++; // this can be compared to the synced time to measure long term drift 258 | #endif 259 | } 260 | if (nextSyncTime <= sysTime) { 261 | if (getTimePtr != 0) { 262 | time_t t = getTimePtr(); 263 | if (t != 0) { 264 | setTime(t); 265 | } else { 266 | nextSyncTime = sysTime + syncInterval; 267 | Status = (Status == timeNotSet) ? timeNotSet : timeNeedsSync; 268 | } 269 | } 270 | } 271 | return (time_t)sysTime; 272 | } 273 | 274 | void setTime(time_t t) { 275 | #ifdef TIME_DRIFT_INFO 276 | if(sysUnsyncedTime == 0) 277 | sysUnsyncedTime = t; // store the time of the first call to set a valid Time 278 | #endif 279 | 280 | sysTime = (uint32_t)t; 281 | nextSyncTime = (uint32_t)t + syncInterval; 282 | Status = timeSet; 283 | prevMillis = millis(); // restart counting from now (thanks to Korman for this fix) 284 | } 285 | 286 | void setTime(int hr,int min,int sec,int dy, int mnth, int yr){ 287 | // year can be given as full four digit year or two digts (2010 or 10 for 2010); 288 | //it is converted to years since 1970 289 | if( yr > 99) 290 | yr = yr - 1970; 291 | else 292 | yr += 30; 293 | tm.Year = yr; 294 | tm.Month = mnth; 295 | tm.Day = dy; 296 | tm.Hour = hr; 297 | tm.Minute = min; 298 | tm.Second = sec; 299 | setTime(makeTime(tm)); 300 | } 301 | 302 | void adjustTime(long adjustment) { 303 | sysTime += adjustment; 304 | } 305 | 306 | // indicates if time has been set and recently synchronized 307 | timeStatus_t timeStatus() { 308 | now(); // required to actually update the status 309 | return Status; 310 | } 311 | 312 | void setSyncProvider( getExternalTime getTimeFunction){ 313 | getTimePtr = getTimeFunction; 314 | nextSyncTime = sysTime; 315 | now(); // this will sync the clock 316 | } 317 | 318 | void setSyncInterval(time_t interval){ // set the number of seconds between re-sync 319 | syncInterval = (uint32_t)interval; 320 | nextSyncTime = sysTime + syncInterval; 321 | } 322 | -------------------------------------------------------------------------------- /Time.h: -------------------------------------------------------------------------------- 1 | #include "TimeLib.h" 2 | -------------------------------------------------------------------------------- /TimeLib.h: -------------------------------------------------------------------------------- 1 | /* 2 | time.h - low level time and date functions 3 | */ 4 | 5 | /* 6 | July 3 2011 - fixed elapsedSecsThisWeek macro (thanks Vincent Valdy for this) 7 | - fixed daysToTime_t macro (thanks maniacbug) 8 | */ 9 | 10 | #ifndef _Time_h 11 | #ifdef __cplusplus 12 | #define _Time_h 13 | 14 | #include 15 | #ifndef __AVR__ 16 | #include // for __time_t_defined, but avr libc lacks sys/types.h 17 | #endif 18 | 19 | 20 | #if !defined(__time_t_defined) // avoid conflict with newlib or other posix libc 21 | typedef unsigned long time_t; 22 | #endif 23 | 24 | 25 | // This ugly hack allows us to define C++ overloaded functions, when included 26 | // from within an extern "C", as newlib's sys/stat.h does. Actually it is 27 | // intended to include "time.h" from the C library (on ARM, but AVR does not 28 | // have that file at all). On Mac and Windows, the compiler will find this 29 | // "Time.h" instead of the C library "time.h", so we may cause other weird 30 | // and unpredictable effects by conflicting with the C library header "time.h", 31 | // but at least this hack lets us define C++ functions as intended. Hopefully 32 | // nothing too terrible will result from overriding the C library header?! 33 | extern "C++" { 34 | typedef enum {timeNotSet, timeNeedsSync, timeSet 35 | } timeStatus_t ; 36 | 37 | typedef enum { 38 | dowInvalid, dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday 39 | } timeDayOfWeek_t; 40 | 41 | typedef enum { 42 | tmSecond, tmMinute, tmHour, tmWday, tmDay,tmMonth, tmYear, tmNbrFields 43 | } tmByteFields; 44 | 45 | typedef struct { 46 | uint8_t Second; 47 | uint8_t Minute; 48 | uint8_t Hour; 49 | uint8_t Wday; // day of week, sunday is day 1 50 | uint8_t Day; 51 | uint8_t Month; 52 | uint8_t Year; // offset from 1970; 53 | } tmElements_t, TimeElements, *tmElementsPtr_t; 54 | 55 | //convenience macros to convert to and from tm years 56 | #define tmYearToCalendar(Y) ((Y) + 1970) // full four digit year 57 | #define CalendarYrToTm(Y) ((Y) - 1970) 58 | #define tmYearToY2k(Y) ((Y) - 30) // offset is from 2000 59 | #define y2kYearToTm(Y) ((Y) + 30) 60 | 61 | typedef time_t(*getExternalTime)(); 62 | //typedef void (*setExternalTime)(const time_t); // not used in this version 63 | 64 | 65 | /*==============================================================================*/ 66 | /* Useful Constants */ 67 | #define SECS_PER_MIN ((time_t)(60UL)) 68 | #define SECS_PER_HOUR ((time_t)(3600UL)) 69 | #define SECS_PER_DAY ((time_t)(SECS_PER_HOUR * 24UL)) 70 | #define DAYS_PER_WEEK ((time_t)(7UL)) 71 | #define SECS_PER_WEEK ((time_t)(SECS_PER_DAY * DAYS_PER_WEEK)) 72 | #define SECS_PER_YEAR ((time_t)(SECS_PER_WEEK * 52UL)) 73 | #define SECS_YR_2000 ((time_t)(946684800UL)) // the time at the start of y2k 74 | 75 | /* Useful Macros for getting elapsed time */ 76 | #define numberOfSeconds(_time_) (_time_ % SECS_PER_MIN) 77 | #define numberOfMinutes(_time_) ((_time_ / SECS_PER_MIN) % SECS_PER_MIN) 78 | #define numberOfHours(_time_) (( _time_% SECS_PER_DAY) / SECS_PER_HOUR) 79 | #define dayOfWeek(_time_) ((( _time_ / SECS_PER_DAY + 4) % DAYS_PER_WEEK)+1) // 1 = Sunday 80 | #define elapsedDays(_time_) ( _time_ / SECS_PER_DAY) // this is number of days since Jan 1 1970 81 | #define elapsedSecsToday(_time_) (_time_ % SECS_PER_DAY) // the number of seconds since last midnight 82 | // The following macros are used in calculating alarms and assume the clock is set to a date later than Jan 1 1971 83 | // Always set the correct time before settting alarms 84 | #define previousMidnight(_time_) (( _time_ / SECS_PER_DAY) * SECS_PER_DAY) // time at the start of the given day 85 | #define nextMidnight(_time_) ( previousMidnight(_time_) + SECS_PER_DAY ) // time at the end of the given day 86 | #define elapsedSecsThisWeek(_time_) (elapsedSecsToday(_time_) + ((dayOfWeek(_time_)-1) * SECS_PER_DAY) ) // note that week starts on day 1 87 | #define previousSunday(_time_) (_time_ - elapsedSecsThisWeek(_time_)) // time at the start of the week for the given time 88 | #define nextSunday(_time_) ( previousSunday(_time_)+SECS_PER_WEEK) // time at the end of the week for the given time 89 | 90 | 91 | /* Useful Macros for converting elapsed time to a time_t */ 92 | #define minutesToTime_t ((M)) ( (M) * SECS_PER_MIN) 93 | #define hoursToTime_t ((H)) ( (H) * SECS_PER_HOUR) 94 | #define daysToTime_t ((D)) ( (D) * SECS_PER_DAY) // fixed on Jul 22 2011 95 | #define weeksToTime_t ((W)) ( (W) * SECS_PER_WEEK) 96 | 97 | /*============================================================================*/ 98 | /* time and date functions */ 99 | int hour(); // the hour now 100 | int hour(time_t t); // the hour for the given time 101 | int hourFormat12(); // the hour now in 12 hour format 102 | int hourFormat12(time_t t); // the hour for the given time in 12 hour format 103 | uint8_t isAM(); // returns true if time now is AM 104 | uint8_t isAM(time_t t); // returns true the given time is AM 105 | uint8_t isPM(); // returns true if time now is PM 106 | uint8_t isPM(time_t t); // returns true the given time is PM 107 | int minute(); // the minute now 108 | int minute(time_t t); // the minute for the given time 109 | int second(); // the second now 110 | int second(time_t t); // the second for the given time 111 | int day(); // the day now 112 | int day(time_t t); // the day for the given time 113 | int weekday(); // the weekday now (Sunday is day 1) 114 | int weekday(time_t t); // the weekday for the given time 115 | int month(); // the month now (Jan is month 1) 116 | int month(time_t t); // the month for the given time 117 | int year(); // the full four digit year: (2009, 2010 etc) 118 | int year(time_t t); // the year for the given time 119 | 120 | time_t now(); // return the current time as seconds since Jan 1 1970 121 | void setTime(time_t t); 122 | void setTime(int hr,int min,int sec,int day, int month, int yr); 123 | void adjustTime(long adjustment); 124 | 125 | /* date strings */ 126 | #define dt_MAX_STRING_LEN 9 // length of longest date string (excluding terminating null) 127 | char* monthStr(uint8_t month); 128 | char* dayStr(uint8_t day); 129 | char* monthShortStr(uint8_t month); 130 | char* dayShortStr(uint8_t day); 131 | 132 | /* time sync functions */ 133 | timeStatus_t timeStatus(); // indicates if time has been set and recently synchronized 134 | void setSyncProvider( getExternalTime getTimeFunction); // identify the external time provider 135 | void setSyncInterval(time_t interval); // set the number of seconds between re-sync 136 | 137 | /* low level functions to convert to and from system time */ 138 | void breakTime(time_t time, tmElements_t &tm); // break time_t into elements 139 | time_t makeTime(tmElements_t &tm); // convert time elements into time_t 140 | 141 | } // extern "C++" 142 | #endif // __cplusplus 143 | #endif /* _Time_h */ 144 | 145 | -------------------------------------------------------------------------------- /gfxfont.h: -------------------------------------------------------------------------------- 1 | // Font structures for newer Adafruit_GFX (1.1 and later). 2 | // Example fonts are included in 'Fonts' directory. 3 | // To use a font in your Arduino sketch, #include the corresponding .h 4 | // file and pass address of GFXfont struct to setFont(). Pass NULL to 5 | // revert to 'classic' fixed-space bitmap font. 6 | 7 | #ifndef _GFXFONT_H_ 8 | #define _GFXFONT_H_ 9 | 10 | /// Font data stored PER GLYPH 11 | typedef struct { 12 | uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap 13 | uint8_t width; ///< Bitmap dimensions in pixels 14 | uint8_t height; ///< Bitmap dimensions in pixels 15 | uint8_t xAdvance; ///< Distance to advance cursor (x axis) 16 | int8_t xOffset; ///< X dist from cursor pos to UL corner 17 | int8_t yOffset; ///< Y dist from cursor pos to UL corner 18 | } GFXglyph; 19 | 20 | /// Data stored for FONT AS A WHOLE 21 | typedef struct { 22 | uint8_t *bitmap; ///< Glyph bitmaps, concatenated 23 | GFXglyph *glyph; ///< Glyph array 24 | uint8_t first; ///< ASCII extents (first char) 25 | uint8_t last; ///< ASCII extents (last char) 26 | uint8_t yAdvance; ///< Newline distance (y axis) 27 | } GFXfont; 28 | 29 | #endif // _GFXFONT_H_ 30 | -------------------------------------------------------------------------------- /glcdfont.c: -------------------------------------------------------------------------------- 1 | // This is the 'classic' fixed-space bitmap font for Adafruit_GFX since 1.0. 2 | // See gfxfont.h for newer custom bitmap font info. 3 | 4 | #ifndef FONT5X7_H 5 | #define FONT5X7_H 6 | 7 | #ifdef __AVR__ 8 | #include 9 | #include 10 | #elif defined(ESP8266) 11 | #include 12 | #else 13 | #define PROGMEM 14 | #endif 15 | 16 | // Standard ASCII 5x7 font 17 | 18 | static const unsigned char font[] PROGMEM = { 19 | 0x00, 0x00, 0x00, 0x00, 0x00, 20 | 0x3E, 0x5B, 0x4F, 0x5B, 0x3E, 21 | 0x3E, 0x6B, 0x4F, 0x6B, 0x3E, 22 | 0x1C, 0x3E, 0x7C, 0x3E, 0x1C, 23 | 0x18, 0x3C, 0x7E, 0x3C, 0x18, 24 | 0x1C, 0x57, 0x7D, 0x57, 0x1C, 25 | 0x1C, 0x5E, 0x7F, 0x5E, 0x1C, 26 | 0x00, 0x18, 0x3C, 0x18, 0x00, 27 | 0xFF, 0xE7, 0xC3, 0xE7, 0xFF, 28 | 0x00, 0x18, 0x24, 0x18, 0x00, 29 | 0xFF, 0xE7, 0xDB, 0xE7, 0xFF, 30 | 0x30, 0x48, 0x3A, 0x06, 0x0E, 31 | 0x26, 0x29, 0x79, 0x29, 0x26, 32 | 0x40, 0x7F, 0x05, 0x05, 0x07, 33 | 0x40, 0x7F, 0x05, 0x25, 0x3F, 34 | 0x5A, 0x3C, 0xE7, 0x3C, 0x5A, 35 | 0x7F, 0x3E, 0x1C, 0x1C, 0x08, 36 | 0x08, 0x1C, 0x1C, 0x3E, 0x7F, 37 | 0x14, 0x22, 0x7F, 0x22, 0x14, 38 | 0x5F, 0x5F, 0x00, 0x5F, 0x5F, 39 | 0x06, 0x09, 0x7F, 0x01, 0x7F, 40 | 0x00, 0x66, 0x89, 0x95, 0x6A, 41 | 0x60, 0x60, 0x60, 0x60, 0x60, 42 | 0x94, 0xA2, 0xFF, 0xA2, 0x94, 43 | 0x08, 0x04, 0x7E, 0x04, 0x08, 44 | 0x10, 0x20, 0x7E, 0x20, 0x10, 45 | 0x08, 0x08, 0x2A, 0x1C, 0x08, 46 | 0x08, 0x1C, 0x2A, 0x08, 0x08, 47 | 0x1E, 0x10, 0x10, 0x10, 0x10, 48 | 0x0C, 0x1E, 0x0C, 0x1E, 0x0C, 49 | 0x30, 0x38, 0x3E, 0x38, 0x30, 50 | 0x06, 0x0E, 0x3E, 0x0E, 0x06, 51 | 0x00, 0x00, 0x00, 0x00, 0x00, 52 | 0x00, 0x00, 0x5F, 0x00, 0x00, 53 | 0x00, 0x07, 0x00, 0x07, 0x00, 54 | 0x14, 0x7F, 0x14, 0x7F, 0x14, 55 | 0x24, 0x2A, 0x7F, 0x2A, 0x12, 56 | 0x23, 0x13, 0x08, 0x64, 0x62, 57 | 0x36, 0x49, 0x56, 0x20, 0x50, 58 | 0x00, 0x08, 0x07, 0x03, 0x00, 59 | 0x00, 0x1C, 0x22, 0x41, 0x00, 60 | 0x00, 0x41, 0x22, 0x1C, 0x00, 61 | 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 62 | 0x08, 0x08, 0x3E, 0x08, 0x08, 63 | 0x00, 0x80, 0x70, 0x30, 0x00, 64 | 0x08, 0x08, 0x08, 0x08, 0x08, 65 | 0x00, 0x00, 0x60, 0x60, 0x00, 66 | 0x20, 0x10, 0x08, 0x04, 0x02, 67 | 0x3E, 0x51, 0x49, 0x45, 0x3E, 68 | 0x00, 0x42, 0x7F, 0x40, 0x00, 69 | 0x72, 0x49, 0x49, 0x49, 0x46, 70 | 0x21, 0x41, 0x49, 0x4D, 0x33, 71 | 0x18, 0x14, 0x12, 0x7F, 0x10, 72 | 0x27, 0x45, 0x45, 0x45, 0x39, 73 | 0x3C, 0x4A, 0x49, 0x49, 0x31, 74 | 0x41, 0x21, 0x11, 0x09, 0x07, 75 | 0x36, 0x49, 0x49, 0x49, 0x36, 76 | 0x46, 0x49, 0x49, 0x29, 0x1E, 77 | 0x00, 0x00, 0x14, 0x00, 0x00, 78 | 0x00, 0x40, 0x34, 0x00, 0x00, 79 | 0x00, 0x08, 0x14, 0x22, 0x41, 80 | 0x14, 0x14, 0x14, 0x14, 0x14, 81 | 0x00, 0x41, 0x22, 0x14, 0x08, 82 | 0x02, 0x01, 0x59, 0x09, 0x06, 83 | 0x3E, 0x41, 0x5D, 0x59, 0x4E, 84 | 0x7C, 0x12, 0x11, 0x12, 0x7C, 85 | 0x7F, 0x49, 0x49, 0x49, 0x36, 86 | 0x3E, 0x41, 0x41, 0x41, 0x22, 87 | 0x7F, 0x41, 0x41, 0x41, 0x3E, 88 | 0x7F, 0x49, 0x49, 0x49, 0x41, 89 | 0x7F, 0x09, 0x09, 0x09, 0x01, 90 | 0x3E, 0x41, 0x41, 0x51, 0x73, 91 | 0x7F, 0x08, 0x08, 0x08, 0x7F, 92 | 0x00, 0x41, 0x7F, 0x41, 0x00, 93 | 0x20, 0x40, 0x41, 0x3F, 0x01, 94 | 0x7F, 0x08, 0x14, 0x22, 0x41, 95 | 0x7F, 0x40, 0x40, 0x40, 0x40, 96 | 0x7F, 0x02, 0x1C, 0x02, 0x7F, 97 | 0x7F, 0x04, 0x08, 0x10, 0x7F, 98 | 0x3E, 0x41, 0x41, 0x41, 0x3E, 99 | 0x7F, 0x09, 0x09, 0x09, 0x06, 100 | 0x3E, 0x41, 0x51, 0x21, 0x5E, 101 | 0x7F, 0x09, 0x19, 0x29, 0x46, 102 | 0x26, 0x49, 0x49, 0x49, 0x32, 103 | 0x03, 0x01, 0x7F, 0x01, 0x03, 104 | 0x3F, 0x40, 0x40, 0x40, 0x3F, 105 | 0x1F, 0x20, 0x40, 0x20, 0x1F, 106 | 0x3F, 0x40, 0x38, 0x40, 0x3F, 107 | 0x63, 0x14, 0x08, 0x14, 0x63, 108 | 0x03, 0x04, 0x78, 0x04, 0x03, 109 | 0x61, 0x59, 0x49, 0x4D, 0x43, 110 | 0x00, 0x7F, 0x41, 0x41, 0x41, 111 | 0x02, 0x04, 0x08, 0x10, 0x20, 112 | 0x00, 0x41, 0x41, 0x41, 0x7F, 113 | 0x04, 0x02, 0x01, 0x02, 0x04, 114 | 0x40, 0x40, 0x40, 0x40, 0x40, 115 | 0x00, 0x03, 0x07, 0x08, 0x00, 116 | 0x20, 0x54, 0x54, 0x78, 0x40, 117 | 0x7F, 0x28, 0x44, 0x44, 0x38, 118 | 0x38, 0x44, 0x44, 0x44, 0x28, 119 | 0x38, 0x44, 0x44, 0x28, 0x7F, 120 | 0x38, 0x54, 0x54, 0x54, 0x18, 121 | 0x00, 0x08, 0x7E, 0x09, 0x02, 122 | 0x18, 0xA4, 0xA4, 0x9C, 0x78, 123 | 0x7F, 0x08, 0x04, 0x04, 0x78, 124 | 0x00, 0x44, 0x7D, 0x40, 0x00, 125 | 0x20, 0x40, 0x40, 0x3D, 0x00, 126 | 0x7F, 0x10, 0x28, 0x44, 0x00, 127 | 0x00, 0x41, 0x7F, 0x40, 0x00, 128 | 0x7C, 0x04, 0x78, 0x04, 0x78, 129 | 0x7C, 0x08, 0x04, 0x04, 0x78, 130 | 0x38, 0x44, 0x44, 0x44, 0x38, 131 | 0xFC, 0x18, 0x24, 0x24, 0x18, 132 | 0x18, 0x24, 0x24, 0x18, 0xFC, 133 | 0x7C, 0x08, 0x04, 0x04, 0x08, 134 | 0x48, 0x54, 0x54, 0x54, 0x24, 135 | 0x04, 0x04, 0x3F, 0x44, 0x24, 136 | 0x3C, 0x40, 0x40, 0x20, 0x7C, 137 | 0x1C, 0x20, 0x40, 0x20, 0x1C, 138 | 0x3C, 0x40, 0x30, 0x40, 0x3C, 139 | 0x44, 0x28, 0x10, 0x28, 0x44, 140 | 0x4C, 0x90, 0x90, 0x90, 0x7C, 141 | 0x44, 0x64, 0x54, 0x4C, 0x44, 142 | 0x00, 0x08, 0x36, 0x41, 0x00, 143 | 0x00, 0x00, 0x77, 0x00, 0x00, 144 | 0x00, 0x41, 0x36, 0x08, 0x00, 145 | 0x02, 0x01, 0x02, 0x04, 0x02, 146 | 0x3C, 0x26, 0x23, 0x26, 0x3C, 147 | 0x1E, 0xA1, 0xA1, 0x61, 0x12, 148 | 0x3A, 0x40, 0x40, 0x20, 0x7A, 149 | 0x38, 0x54, 0x54, 0x55, 0x59, 150 | 0x21, 0x55, 0x55, 0x79, 0x41, 151 | 0x22, 0x54, 0x54, 0x78, 0x42, // a-umlaut 152 | 0x21, 0x55, 0x54, 0x78, 0x40, 153 | 0x20, 0x54, 0x55, 0x79, 0x40, 154 | 0x0C, 0x1E, 0x52, 0x72, 0x12, 155 | 0x39, 0x55, 0x55, 0x55, 0x59, 156 | 0x39, 0x54, 0x54, 0x54, 0x59, 157 | 0x39, 0x55, 0x54, 0x54, 0x58, 158 | 0x00, 0x00, 0x45, 0x7C, 0x41, 159 | 0x00, 0x02, 0x45, 0x7D, 0x42, 160 | 0x00, 0x01, 0x45, 0x7C, 0x40, 161 | 0x7D, 0x12, 0x11, 0x12, 0x7D, // A-umlaut 162 | 0xF0, 0x28, 0x25, 0x28, 0xF0, 163 | 0x7C, 0x54, 0x55, 0x45, 0x00, 164 | 0x20, 0x54, 0x54, 0x7C, 0x54, 165 | 0x7C, 0x0A, 0x09, 0x7F, 0x49, 166 | 0x32, 0x49, 0x49, 0x49, 0x32, 167 | 0x3A, 0x44, 0x44, 0x44, 0x3A, // o-umlaut 168 | 0x32, 0x4A, 0x48, 0x48, 0x30, 169 | 0x3A, 0x41, 0x41, 0x21, 0x7A, 170 | 0x3A, 0x42, 0x40, 0x20, 0x78, 171 | 0x00, 0x9D, 0xA0, 0xA0, 0x7D, 172 | 0x3D, 0x42, 0x42, 0x42, 0x3D, // O-umlaut 173 | 0x3D, 0x40, 0x40, 0x40, 0x3D, 174 | 0x3C, 0x24, 0xFF, 0x24, 0x24, 175 | 0x48, 0x7E, 0x49, 0x43, 0x66, 176 | 0x2B, 0x2F, 0xFC, 0x2F, 0x2B, 177 | 0xFF, 0x09, 0x29, 0xF6, 0x20, 178 | 0xC0, 0x88, 0x7E, 0x09, 0x03, 179 | 0x20, 0x54, 0x54, 0x79, 0x41, 180 | 0x00, 0x00, 0x44, 0x7D, 0x41, 181 | 0x30, 0x48, 0x48, 0x4A, 0x32, 182 | 0x38, 0x40, 0x40, 0x22, 0x7A, 183 | 0x00, 0x7A, 0x0A, 0x0A, 0x72, 184 | 0x7D, 0x0D, 0x19, 0x31, 0x7D, 185 | 0x26, 0x29, 0x29, 0x2F, 0x28, 186 | 0x26, 0x29, 0x29, 0x29, 0x26, 187 | 0x30, 0x48, 0x4D, 0x40, 0x20, 188 | 0x38, 0x08, 0x08, 0x08, 0x08, 189 | 0x08, 0x08, 0x08, 0x08, 0x38, 190 | 0x2F, 0x10, 0xC8, 0xAC, 0xBA, 191 | 0x2F, 0x10, 0x28, 0x34, 0xFA, 192 | 0x00, 0x00, 0x7B, 0x00, 0x00, 193 | 0x08, 0x14, 0x2A, 0x14, 0x22, 194 | 0x22, 0x14, 0x2A, 0x14, 0x08, 195 | 0x55, 0x00, 0x55, 0x00, 0x55, // #176 (25% block) missing in old code 196 | 0xAA, 0x55, 0xAA, 0x55, 0xAA, // 50% block 197 | 0xFF, 0x55, 0xFF, 0x55, 0xFF, // 75% block 198 | 0x00, 0x00, 0x00, 0xFF, 0x00, 199 | 0x10, 0x10, 0x10, 0xFF, 0x00, 200 | 0x14, 0x14, 0x14, 0xFF, 0x00, 201 | 0x10, 0x10, 0xFF, 0x00, 0xFF, 202 | 0x10, 0x10, 0xF0, 0x10, 0xF0, 203 | 0x14, 0x14, 0x14, 0xFC, 0x00, 204 | 0x14, 0x14, 0xF7, 0x00, 0xFF, 205 | 0x00, 0x00, 0xFF, 0x00, 0xFF, 206 | 0x14, 0x14, 0xF4, 0x04, 0xFC, 207 | 0x14, 0x14, 0x17, 0x10, 0x1F, 208 | 0x10, 0x10, 0x1F, 0x10, 0x1F, 209 | 0x14, 0x14, 0x14, 0x1F, 0x00, 210 | 0x10, 0x10, 0x10, 0xF0, 0x00, 211 | 0x00, 0x00, 0x00, 0x1F, 0x10, 212 | 0x10, 0x10, 0x10, 0x1F, 0x10, 213 | 0x10, 0x10, 0x10, 0xF0, 0x10, 214 | 0x00, 0x00, 0x00, 0xFF, 0x10, 215 | 0x10, 0x10, 0x10, 0x10, 0x10, 216 | 0x10, 0x10, 0x10, 0xFF, 0x10, 217 | 0x00, 0x00, 0x00, 0xFF, 0x14, 218 | 0x00, 0x00, 0xFF, 0x00, 0xFF, 219 | 0x00, 0x00, 0x1F, 0x10, 0x17, 220 | 0x00, 0x00, 0xFC, 0x04, 0xF4, 221 | 0x14, 0x14, 0x17, 0x10, 0x17, 222 | 0x14, 0x14, 0xF4, 0x04, 0xF4, 223 | 0x00, 0x00, 0xFF, 0x00, 0xF7, 224 | 0x14, 0x14, 0x14, 0x14, 0x14, 225 | 0x14, 0x14, 0xF7, 0x00, 0xF7, 226 | 0x14, 0x14, 0x14, 0x17, 0x14, 227 | 0x10, 0x10, 0x1F, 0x10, 0x1F, 228 | 0x14, 0x14, 0x14, 0xF4, 0x14, 229 | 0x10, 0x10, 0xF0, 0x10, 0xF0, 230 | 0x00, 0x00, 0x1F, 0x10, 0x1F, 231 | 0x00, 0x00, 0x00, 0x1F, 0x14, 232 | 0x00, 0x00, 0x00, 0xFC, 0x14, 233 | 0x00, 0x00, 0xF0, 0x10, 0xF0, 234 | 0x10, 0x10, 0xFF, 0x10, 0xFF, 235 | 0x14, 0x14, 0x14, 0xFF, 0x14, 236 | 0x10, 0x10, 0x10, 0x1F, 0x00, 237 | 0x00, 0x00, 0x00, 0xF0, 0x10, 238 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 239 | 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 240 | 0xFF, 0xFF, 0xFF, 0x00, 0x00, 241 | 0x00, 0x00, 0x00, 0xFF, 0xFF, 242 | 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 243 | 0x38, 0x44, 0x44, 0x38, 0x44, 244 | 0xFC, 0x4A, 0x4A, 0x4A, 0x34, // sharp-s or beta 245 | 0x7E, 0x02, 0x02, 0x06, 0x06, 246 | 0x02, 0x7E, 0x02, 0x7E, 0x02, 247 | 0x63, 0x55, 0x49, 0x41, 0x63, 248 | 0x38, 0x44, 0x44, 0x3C, 0x04, 249 | 0x40, 0x7E, 0x20, 0x1E, 0x20, 250 | 0x06, 0x02, 0x7E, 0x02, 0x02, 251 | 0x99, 0xA5, 0xE7, 0xA5, 0x99, 252 | 0x1C, 0x2A, 0x49, 0x2A, 0x1C, 253 | 0x4C, 0x72, 0x01, 0x72, 0x4C, 254 | 0x30, 0x4A, 0x4D, 0x4D, 0x30, 255 | 0x30, 0x48, 0x78, 0x48, 0x30, 256 | 0xBC, 0x62, 0x5A, 0x46, 0x3D, 257 | 0x3E, 0x49, 0x49, 0x49, 0x00, 258 | 0x7E, 0x01, 0x01, 0x01, 0x7E, 259 | 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 260 | 0x44, 0x44, 0x5F, 0x44, 0x44, 261 | 0x40, 0x51, 0x4A, 0x44, 0x40, 262 | 0x40, 0x44, 0x4A, 0x51, 0x40, 263 | 0x00, 0x00, 0xFF, 0x01, 0x03, 264 | 0xE0, 0x80, 0xFF, 0x00, 0x00, 265 | 0x08, 0x08, 0x6B, 0x6B, 0x08, 266 | 0x36, 0x12, 0x36, 0x24, 0x36, 267 | 0x06, 0x0F, 0x09, 0x0F, 0x06, 268 | 0x00, 0x00, 0x18, 0x18, 0x00, 269 | 0x00, 0x00, 0x10, 0x10, 0x00, 270 | 0x30, 0x40, 0xFF, 0x01, 0x01, 271 | 0x00, 0x1F, 0x01, 0x01, 0x1E, 272 | 0x00, 0x19, 0x1D, 0x17, 0x12, 273 | 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 274 | 0x00, 0x00, 0x00, 0x00, 0x00 // #255 NBSP 275 | }; 276 | #endif // FONT5X7_H 277 | -------------------------------------------------------------------------------- /hardware.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigCorvus/SMA-Q2-Firmware-3/3ab7506ee2e191e48584a77ff89700214d9b11ac/hardware.zip -------------------------------------------------------------------------------- /heartRate.ino: -------------------------------------------------------------------------------- 1 | void heartRate() { 2 | digitalWrite(HRM_PWR, HIGH); 3 | display.clearDisplay(); 4 | display.setCursor(10, 0); 5 | display.setTextColor(LCD_COLOR_BLACK); 6 | display.setTextSize(3); 7 | display.print("HR calc"); 8 | display.refresh(); 9 | 10 | 11 | // EXCOMINtimer.startTimer(500, disp_toggle); //this toggles the EXTCOMIN pin 12 | 13 | //pah8002_init(); 14 | //bool success = pah8002_start(); 15 | 16 | while (digitalRead(BTN_BCK)) { 17 | //sleep(); 18 | if (digitalRead(BTN_UP) == LOW) { 19 | delay(100); 20 | //ble.print("UP"); 21 | 22 | } 23 | if (digitalRead(BTN_DOWN) == LOW) { 24 | delay(100); 25 | //ble.print("DOWN"); 26 | } 27 | if (digitalRead(BTN_OK) == LOW) { 28 | delay(100); 29 | //ble.print("OK"); 30 | } 31 | wdt_feed(); 32 | 33 | 34 | 35 | } //end menue loop 36 | 37 | 38 | 39 | displayMenue(0); 40 | delay(100); //debounce 41 | digitalWrite(HRM_PWR, LOW); 42 | } 43 | -------------------------------------------------------------------------------- /i2csoft.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "i2csoft.h" 3 | 4 | int pinSda; 5 | int pinScl; 6 | int recv_len; 7 | int sda_in_out; 8 | 9 | byte readWhoami() 10 | { 11 | return ReadRegister(0x0F); 12 | } 13 | byte readBuffCount() 14 | { 15 | return ReadRegister(0x3C); 16 | } 17 | 18 | void initkx023() 19 | { 20 | byte answer; 21 | byte counter; 22 | 23 | sendToRegister(0x18, 0);//CNTL1 Standby Mode 24 | delay(10); 25 | sendToRegister(0x19, 0xFF);//CNTL2 SoftwareReset 26 | delay(10); 27 | 28 | 29 | //Mode 3 30 | do 31 | { 32 | answer = ReadRegister(0xC); 33 | counter++; 34 | } 35 | while ( answer != 0x55 && counter < 10 ); 36 | sendToRegister(0x18, 0);//CNTL1 Standby Mode 37 | delay(10); 38 | sendToRegister(0x1B, 2);//ODCNTL 50HZ Outputrate 39 | sendToRegister(0x35, 0xB);//LP_CNTL 8 SampleAverage 40 | sendToRegister(0x1A, 0xC6);//CNTL3 50HZ Tilt,50HZ DirectionalTap.50HZ GeneralMotion 41 | sendToRegister(0x1C, 0x30);//INC1 EnableInt1,ActiveHIGH 30 42 | sendToRegister(0x1F, 2);//INC4 WakeUp Motion Detect on In1 43 | sendToRegister(0x3A, 0x23);//BUF_CNTL1 Threshols to 35 44 | sendToRegister(0x3B, 1);//BUF_CNTL2 BufferInactive, BufferMode Stream 45 | sendToRegister(0x3E, 0);//BUF_CLEAR 46 | sendToRegister(0x30, 0x1C);//ATH MotionDetect Threshold 0xC 47 | sendToRegister(0x23, 2);//WUFC initial CountRegister MotionDetectTimer to 2 48 | sendToRegister(0x18, 0x82);//CNTL1 Operation Mode, 49 | 50 | 51 | /* 52 | //Mode 1 53 | do 54 | { 55 | answer = ReadRegister(0xC); 56 | counter++; 57 | } 58 | while ( answer != 0x55 && counter < 10 ); 59 | sendToRegister(0x18, 0);//CNTL1 Standby Mode 60 | delay(10); 61 | sendToRegister(0x3A, 0x23);//BUF_CNTL1 Bufferthreshold to 35 62 | sendToRegister(0x3B, 0x1);//BUF_CNTL2 BufferEnable,16BitRes,StreamMode,noInteruppt 63 | sendToRegister(0x3E, 0);//BUF_CLEAR 64 | sendToRegister(0x35, 0x3B);//LP_CNTL 8 SampleAverage 65 | sendToRegister(0x18, 0x80);//CNTL1 Opteration Mode 66 | */ 67 | /* 68 | //Mode 2 69 | do 70 | { 71 | answer = ReadRegister(0xC); 72 | counter++; 73 | } 74 | while ( answer != 0x55 && counter < 10 ); 75 | sendToRegister(0x18, 0);//CNTL1 Standby Mode 76 | delay(10); 77 | sendToRegister(0x3A, 0x23);//BUF_CNTL1 Bufferthreshold to 35 78 | sendToRegister(0x3B, 0xC1);//BUF_CNTL2 BufferEnable,16BitRes,StreamMode,noInteruppt 79 | sendToRegister(0x3E, 0);//BUF_CLEAR 80 | sendToRegister(0x35, 0x3B);//LP_CNTL 8 SampleAverage 81 | sendToRegister(0x1C, 0x10);//INC1 Int1 Active HIGH 82 | sendToRegister(0x18, 0x80);//CNTL1 Opteration Mode 83 | */ 84 | /* 85 | //Mode 4 86 | do 87 | { 88 | answer = ReadRegister(0xC); 89 | counter++; 90 | } 91 | while ( answer != 0x55 && counter < 10 ); 92 | sendToRegister(0x18, 0);//CNTL1 Standby Mode 93 | delay(10); 94 | sendToRegister(0x1C, 0x10);//INC1 Int1 Active HIGH 95 | */ 96 | } 97 | 98 | void sendToRegister(byte addr, byte input) 99 | { 100 | softbeginTransmission(0x1F); 101 | softwrite(addr); 102 | softwrite(input); 103 | softendTransmission(); 104 | } 105 | 106 | byte ReadRegister(byte addr) 107 | { 108 | softbeginTransmission(0x1F); 109 | softwrite(addr); 110 | softendTransmission(); 111 | softrequestFrom(0x1F , 1); 112 | return softread(); 113 | } 114 | void initi2c() 115 | { 116 | pinSda = 17; 117 | pinScl = 18; 118 | 119 | pinMode(pinScl, OUTPUT); 120 | pinMode(pinSda, OUTPUT); 121 | sda_in_out = OUTPUT; 122 | digitalWrite(pinScl, HIGH); 123 | digitalWrite(pinSda, HIGH); 124 | } 125 | void softend() 126 | { 127 | pinMode(pinScl, INPUT); 128 | pinMode(pinSda, INPUT); 129 | } 130 | void sdaSet(unsigned char ucDta) 131 | { 132 | 133 | if (sda_in_out != OUTPUT) 134 | { 135 | sda_in_out = OUTPUT; 136 | pinMode(pinSda, OUTPUT); 137 | } 138 | digitalWrite(pinSda, ucDta); 139 | } 140 | 141 | unsigned char getAck() 142 | { 143 | digitalWrite(pinScl, LOW); 144 | pinMode(pinSda, INPUT_PULLUP); 145 | sda_in_out = INPUT_PULLUP; 146 | 147 | digitalWrite(pinScl, HIGH); 148 | unsigned long timer_t = micros(); 149 | while (1) 150 | { 151 | if (!digitalRead(pinSda)) 152 | { 153 | return 1; 154 | } 155 | 156 | if (micros() - timer_t > 100)return 0; 157 | } 158 | } 159 | 160 | void sendStop() 161 | { 162 | digitalWrite(pinScl, LOW); 163 | sdaSet(LOW); 164 | digitalWrite(pinScl, HIGH); 165 | sdaSet(HIGH); 166 | } 167 | 168 | void sendByte(unsigned char ucDta) 169 | { 170 | for (int i = 0; i < 8; i++) 171 | { 172 | digitalWrite(pinScl, LOW); 173 | sdaSet((ucDta & 0x80) != 0); 174 | ucDta <<= 0; 175 | digitalWrite(pinScl, HIGH); 176 | sdaSet((ucDta & 0x80) != 0); 177 | ucDta <<= 1; 178 | } 179 | } 180 | 181 | unsigned char sendByteAck(unsigned char ucDta) 182 | { 183 | sendByte(ucDta); 184 | return getAck(); 185 | } 186 | 187 | unsigned char softbeginTransmission(unsigned char addr) 188 | { 189 | sdaSet(LOW); 190 | unsigned char ret = sendByteAck(addr << 1); 191 | return ret; 192 | } 193 | 194 | unsigned char softendTransmission() 195 | { 196 | sendStop(); 197 | return 0; 198 | } 199 | 200 | unsigned char softwrite(unsigned char dta) 201 | { 202 | return sendByteAck(dta); 203 | } 204 | 205 | unsigned char softrequestFrom(unsigned char addr, unsigned char len) 206 | { 207 | sdaSet(LOW); 208 | recv_len = len; 209 | unsigned char ret = sendByteAck((addr << 1) + 1); 210 | return ret; 211 | } 212 | 213 | unsigned char softread() 214 | { 215 | if (!recv_len)return 0; 216 | 217 | unsigned char ucRt = 0; 218 | 219 | pinMode(pinSda, INPUT_PULLUP); 220 | sda_in_out = INPUT_PULLUP; 221 | 222 | for (int i = 0; i < 8; i++) 223 | { 224 | unsigned char ucBit; 225 | digitalWrite(pinScl, LOW); 226 | digitalWrite(pinScl, HIGH); 227 | ucBit = digitalRead(pinSda); 228 | ucRt = (ucRt << 1) + ucBit; 229 | } 230 | 231 | unsigned char dta = ucRt; 232 | recv_len--; 233 | 234 | if (recv_len > 0) 235 | { 236 | digitalWrite(pinScl, LOW); 237 | sdaSet(LOW); 238 | digitalWrite(pinScl, HIGH); 239 | digitalWrite(pinScl, LOW); 240 | } else { 241 | digitalWrite(pinScl, LOW); 242 | sdaSet(HIGH); 243 | digitalWrite(pinScl, HIGH); 244 | digitalWrite(pinScl, LOW); 245 | sendStop(); 246 | } 247 | return dta; 248 | } 249 | -------------------------------------------------------------------------------- /i2csoft.h: -------------------------------------------------------------------------------- 1 | #ifndef i2csoftCCC 2 | #define i2csoftCCC 3 | 4 | #include 5 | #include 6 | 7 | #define CMD_SIZE 1 // COMMAND OR REGISTER ADDRESS SIZE 8 | #define BUFF_SIZE 256 // BUFFER SIZE 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | void initkx023(); 15 | byte readWhoami(); 16 | byte readBuffCount(); 17 | void sendToRegister(byte addr,byte input); 18 | byte ReadRegister(byte addr); 19 | void initi2c(); 20 | void sdaSet(unsigned char ucDta); 21 | unsigned char getAck(); 22 | void sendStop(); 23 | void sendByte(unsigned char ucDta); 24 | unsigned char sendByteAck(unsigned char ucDta); 25 | unsigned char softbeginTransmission(unsigned char addr); 26 | unsigned char softendTransmission(); 27 | unsigned char softwrite(unsigned char dta); 28 | unsigned char softrequestFrom(unsigned char addr, unsigned char len); 29 | unsigned char softread(); 30 | void softend(); 31 | 32 | #ifdef __cplusplus 33 | } // extern "C" 34 | #endif 35 | #endif 36 | -------------------------------------------------------------------------------- /menue.ino: -------------------------------------------------------------------------------- 1 | void menue() { 2 | insideMenue = true; 3 | byte menueIndex=0; 4 | byte maxMenueIndex=3; 5 | displayMenue(0); 6 | 7 | EXCOMINtimer.startTimer(500, disp_toggle); //this toggles the EXTCOMIN pin 8 | 9 | while (digitalRead(BTN_BCK)) { 10 | 11 | sleep(); 12 | if(digitalRead(BTN_DOWN)==LOW){ 13 | delay(30); 14 | if(menueIndex>=maxMenueIndex) menueIndex = maxMenueIndex; else menueIndex++; 15 | displayMenue(menueIndex); 16 | } 17 | 18 | if(digitalRead(BTN_UP)==LOW){ 19 | delay(30); 20 | if(menueIndex<=0) menueIndex = 0; else menueIndex--; 21 | displayMenue(menueIndex); 22 | } 23 | 24 | if(digitalRead(BTN_OK)==LOW){ 25 | delay(30); 26 | switch(menueIndex){ 27 | case 0: 28 | remote(); 29 | break; 30 | 31 | case 1: 32 | tremor(); 33 | break; 34 | 35 | case 2: 36 | 37 | break; 38 | 39 | case 3: 40 | heartRate(); 41 | break; 42 | 43 | } 44 | } 45 | 46 | wdt_feed(); 47 | 48 | while (ble.available() ) { 49 | int i = ble.read(); 50 | bleInString[bleInIndx] = i; 51 | bleInIndx++; 52 | } 53 | if ( bleInIndx > 0) { 54 | 55 | SetDateTimeString(bleInString); 56 | clockTimer.stop(); 57 | clockTimer.startTimer(60000, displayTime); //this is the timer for the watch face refresh 58 | display.setCursor(10, 140); 59 | display.setTextColor(LCD_COLOR_BLACK); 60 | display.setTextSize(1); 61 | display.println("date and time set to:"); 62 | display.print(bleInString); 63 | 64 | 65 | 66 | bleInIndx = 0; 67 | } 68 | 69 | } //end menue loop 70 | insideMenue = false; 71 | displayTime(); 72 | 73 | delay(100); //debounce 74 | } 75 | 76 | 77 | void displayMenue(int itemIndex) { 78 | display.clearDisplay(); 79 | display.setTextSize(3); 80 | display.setCursor(50, 0); 81 | display.setTextColor(LCD_COLOR_BLACK); 82 | display.println("Menu"); 83 | 84 | switch(itemIndex){ 85 | case 0: 86 | display.setTextColor(LCD_COLOR_BLUE, LCD_COLOR_GREEN); 87 | display.println("Remote"); 88 | display.setTextColor(LCD_COLOR_BLUE, LCD_COLOR_WHITE); 89 | display.println("Tremor"); 90 | display.setTextColor(LCD_COLOR_BLUE, LCD_COLOR_WHITE); 91 | display.println("Timer"); 92 | display.setTextColor(LCD_COLOR_BLUE, LCD_COLOR_WHITE); 93 | display.println("HR"); 94 | break; 95 | case 1: 96 | display.setTextColor(LCD_COLOR_BLUE, LCD_COLOR_WHITE); 97 | display.println("Remote"); 98 | display.setTextColor(LCD_COLOR_BLUE, LCD_COLOR_GREEN); 99 | display.println("Tremor"); 100 | display.setTextColor(LCD_COLOR_BLUE, LCD_COLOR_WHITE); 101 | display.println("Timer"); 102 | display.setTextColor(LCD_COLOR_BLUE, LCD_COLOR_WHITE); 103 | display.println("HR"); 104 | break; 105 | case 2: 106 | display.setTextColor(LCD_COLOR_BLUE, LCD_COLOR_WHITE); 107 | display.println("Remote"); 108 | display.setTextColor(LCD_COLOR_BLUE, LCD_COLOR_WHITE); 109 | display.println("Tremor"); 110 | display.setTextColor(LCD_COLOR_BLUE, LCD_COLOR_GREEN); 111 | display.println("Timer"); 112 | display.setTextColor(LCD_COLOR_BLUE, LCD_COLOR_WHITE); 113 | display.println("HR"); 114 | break; 115 | case 3: 116 | display.setTextColor(LCD_COLOR_BLUE, LCD_COLOR_WHITE); 117 | display.println("Remote"); 118 | display.setTextColor(LCD_COLOR_BLUE, LCD_COLOR_WHITE); 119 | display.println("Tremor"); 120 | display.setTextColor(LCD_COLOR_BLUE, LCD_COLOR_WHITE); 121 | display.println("Timer"); 122 | display.setTextColor(LCD_COLOR_BLUE, LCD_COLOR_GREEN); 123 | display.println("HR"); 124 | break; 125 | } 126 | display.refresh(); 127 | } 128 | -------------------------------------------------------------------------------- /nrfutil.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BigCorvus/SMA-Q2-Firmware-3/3ab7506ee2e191e48584a77ff89700214d9b11ac/nrfutil.exe -------------------------------------------------------------------------------- /remote.ino: -------------------------------------------------------------------------------- 1 | void remote() { 2 | display.clearDisplay(); 3 | display.setCursor(10, 0); 4 | display.setTextColor(LCD_COLOR_BLACK); 5 | display.setTextSize(3); 6 | display.print("Remote"); 7 | display.refresh(); 8 | 9 | initkx023(); 10 | delay(20); 11 | 12 | //digitalWrite(ACCEL_PWR, HIGH); //turn on the accelerometer 13 | 14 | 15 | EXCOMINtimer.startTimer(500, disp_toggle); //this toggles the EXTCOMIN pin 16 | accelSamplingTimer.startTimer(100, sendXYZ); //sample accel and send data 17 | 18 | while (digitalRead(BTN_BCK)) { 19 | sleep(); 20 | 21 | wdt_feed(); 22 | 23 | if (digitalRead(BTN_UP) == LOW) { 24 | 25 | delay(100); 26 | ble.print("UP"); 27 | 28 | } 29 | 30 | if (digitalRead(BTN_DOWN) == LOW) { 31 | 32 | delay(100); 33 | ble.print("DOWN"); 34 | 35 | } 36 | 37 | if (digitalRead(BTN_OK) == LOW) { 38 | 39 | delay(100); 40 | ble.print("OK"); 41 | 42 | } 43 | 44 | if (bleConnected) { 45 | display.setCursor(5, 40); 46 | display.setTextColor(LCD_COLOR_BLACK,LCD_COLOR_WHITE); 47 | display.setTextSize(1); 48 | display.print("streaming accel data"); 49 | display.refresh(); 50 | }else{ 51 | display.setCursor(5, 40); 52 | display.setTextColor(LCD_COLOR_WHITE,LCD_COLOR_WHITE); 53 | display.setTextSize(1); 54 | display.print("streaming accel data"); 55 | display.refresh(); 56 | } 57 | 58 | while (ble.available() ) { 59 | int i = ble.read(); 60 | bleInString[bleInIndx] = i; 61 | bleInIndx++; 62 | } 63 | if ( bleInIndx > 0) { 64 | 65 | display.setCursor(10, 140); 66 | display.setTextColor(LCD_COLOR_BLACK, LCD_COLOR_WHITE); 67 | for (int i = 0; i < bleInIndx + 1; i++) display.print(" "); 68 | display.setCursor(10, 140); 69 | display.setTextSize(1); 70 | display.print(bleInString); 71 | display.refresh(); 72 | bleInIndx = 0; 73 | } 74 | 75 | 76 | } //end menue loop 77 | 78 | accelSamplingTimer.stop(); //stop sampling and sending 79 | // digitalWrite(ACCEL_PWR, LOW); //turn off the accelerometer 80 | accelStdby(); 81 | displayMenue(0); 82 | delay(100); //debounce 83 | } 84 | 85 | void sendXYZ() { 86 | int16_t x, y, z = 0; 87 | 88 | uint8_t res[6]; 89 | softbeginTransmission(0x1F); 90 | softwrite(0x06); 91 | softendTransmission(); 92 | softrequestFrom(0x1F , 6); 93 | res[0] = softread(); 94 | res[1] = softread(); 95 | res[2] = softread(); 96 | res[3] = softread(); 97 | res[4] = softread(); 98 | res[5] = softread(); 99 | 100 | x = (int16_t)((res[1] << 8) | res[0]) ; 101 | y = (int16_t)((res[3] << 8) | res[2]) ; 102 | z = (int16_t)((res[5] << 8) | res[4]) ; 103 | if (bleConnected) { 104 | ble.print(x); 105 | ble.print(","); 106 | ble.print(y); 107 | ble.print(","); 108 | ble.print(z); 109 | ble.print(";"); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /tremor.ino: -------------------------------------------------------------------------------- 1 | void tremor() { 2 | display.clearDisplay(); 3 | display.setCursor(10, 0); 4 | display.setTextColor(LCD_COLOR_BLACK); 5 | display.setTextSize(3); 6 | display.print("Tremor f"); 7 | display.refresh(); 8 | int prevX, prevX2, prevY, prevY2 = 0; 9 | int X = 0; 10 | int16_t x, y, z = 0; 11 | EXCOMINtimer.startTimer(500, disp_toggle); //this toggles the EXTCOMIN pin 12 | initkx023(); 13 | delay(20); 14 | // digitalWrite(ACCEL_PWR, HIGH); //turn on the accelerometer 15 | 16 | while (digitalRead(BTN_BCK)) { 17 | //sleep(); 18 | wdt_feed(); 19 | 20 | if (digitalRead(BTN_UP) == LOW) { 21 | delay(100); 22 | //ble.print("UP"); 23 | 24 | } 25 | 26 | if (digitalRead(BTN_DOWN) == LOW) { 27 | delay(100); 28 | //ble.print("DOWN"); 29 | } 30 | 31 | if (digitalRead(BTN_OK) == LOW) { 32 | delay(100); 33 | // ble.print("OK"); 34 | } 35 | 36 | 37 | while (ble.available() ) { 38 | int i = ble.read(); 39 | bleInString[bleInIndx] = i; 40 | bleInIndx++; 41 | } 42 | if ( bleInIndx > 0) { 43 | 44 | display.setCursor(10, 140); 45 | display.setTextColor(LCD_COLOR_BLACK, LCD_COLOR_WHITE); 46 | for (int i = 0; i < bleInIndx + 1; i++) display.print(" "); 47 | display.setCursor(10, 140); 48 | display.setTextSize(1); 49 | display.print(bleInString); 50 | display.refresh(); 51 | bleInIndx = 0; 52 | } 53 | 54 | //accel test 55 | 56 | 57 | uint8_t res[6]; 58 | softbeginTransmission(0x1F); 59 | softwrite(0x06); 60 | softendTransmission(); 61 | softrequestFrom(0x1F , 6); 62 | res[0] = softread(); 63 | res[1] = softread(); 64 | res[2] = softread(); 65 | res[3] = softread(); 66 | res[4] = softread(); 67 | res[5] = softread(); 68 | 69 | x = (int16_t)((res[1] << 8) | res[0]) ; 70 | y = (int16_t)((res[3] << 8) | res[2]) ; 71 | z = (int16_t)((res[5] << 8) | res[4]) ; 72 | 73 | // display.setCursor(10, 160); 74 | // display.setTextSize(1); 75 | // display.setTextColor(LCD_COLOR_BLACK, LCD_COLOR_WHITE); 76 | // display.print(" "); 77 | // display.setCursor(10, 160); 78 | // display.print(x); 79 | // display.print(","); 80 | // display.print(y); 81 | // display.print(","); 82 | // display.println(z); 83 | // display.refresh(); 84 | 85 | 86 | display.setTextColor(LCD_COLOR_BLACK); 87 | x = map(x, -32767, 32767, 30, 90); 88 | y = map(y, -32767, 32767, 91, 150); 89 | display.fillRect(X, 30, 6, 120, LCD_COLOR_WHITE); 90 | display.drawLine(X - 1, prevY, X, x, LCD_COLOR_BLACK); 91 | //M5.Lcd.fillRect(x, 0, 4,320,COLOR_BLACK); 92 | display.drawLine(X - 1, prevY2, X, y, LCD_COLOR_BLUE); 93 | display.refresh(); 94 | prevY = x; 95 | prevY2 = y; 96 | X++; 97 | if (X >= 175) X = 0; 98 | 99 | delay(4); 100 | 101 | } //end app loop 102 | 103 | 104 | // digitalWrite(ACCEL_PWR, LOW); //turn on the accelerometer 105 | accelStdby(); 106 | displayMenue(1); 107 | delay(100); //debounce 108 | } 109 | -------------------------------------------------------------------------------- /utils.ino: -------------------------------------------------------------------------------- 1 | //housekeeing stuff 2 | 3 | // reload the watchdog timer that has been started by the bootloader and set for 10 seconds 4 | void wdt_feed() { 5 | NRF_WDT->RR[0] = WDT_RR_RR_Reload; 6 | } 7 | 8 | void readDFUcombo(){ 9 | if (digitalRead(BTN_UP) == LOW && digitalRead(BTN_DOWN) == LOW && digitalRead(BTN_BCK) == LOW) { 10 | //press the OTA DFU button combo so the device can reset and boot into OTA DFU mode 11 | NRF_POWER->GPREGRET = 0x01; 12 | sd_nvic_SystemReset(); 13 | } 14 | } 15 | 16 | //This needs to be run at least once per second 17 | void disp_toggle() { 18 | extcominState = !extcominState; 19 | digitalWrite(EXTCOMIN, extcominState); 20 | } 21 | 22 | void toggleBLT() { 23 | bltOn = !bltOn; // toggle state 24 | if (bltOn) { 25 | //digitalWrite(BCKLT, HIGH); // turn the LED on (HIGH is the voltage level) 26 | analogWrite(BCKLT,10); 27 | } else { 28 | //digitalWrite(BCKLT, LOW); // turn the LED off by making the voltage LOW 29 | analogWrite(BCKLT,0); 30 | } 31 | } 32 | 33 | int getBatteryLevel() { 34 | //lp_comparator_stop(); 35 | return map(analogRead(VBAT), 306, 355, 0, 100); //adapted for SMA-Q2 306 at 3,6v and 350 at 4,1v 36 | //lp_comparator_start(BTN_OK, REF_8_16Vdd, handlePinLevelChangeOK); // always triggers pin LOW first, then if pin HIGH, will trigger HIGH 37 | //lp_comparator_start(BTN_DOWN, REF_8_16Vdd, handlePinLevelChangeDOWN); 38 | } 39 | 40 | void handleConnection(BLECentral& central) { 41 | 42 | bleConnected=true; 43 | if(!insideMenue) displayTime(); 44 | } 45 | 46 | void handleDisconnection(BLECentral& central) { 47 | 48 | bleConnected=false; 49 | if(!insideMenue) displayTime(); 50 | } 51 | 52 | 53 | String GetDateTimeString() { 54 | String datetime = String(year()); 55 | if (month() < 10) datetime += "0"; 56 | datetime += String(month()); 57 | if (day() < 10) datetime += "0"; 58 | datetime += String(day()); 59 | if (hour() < 10) datetime += "0"; 60 | datetime += String(hour()); 61 | if (minute() < 10) datetime += "0"; 62 | datetime += String(minute()); 63 | return datetime; 64 | } 65 | 66 | void SetDateTimeString(String datetime) { 67 | int year = datetime.substring(0, 4).toInt(); 68 | int month = datetime.substring(4, 6).toInt(); 69 | int day = datetime.substring(6, 8).toInt(); 70 | int hr = datetime.substring(8, 10).toInt(); 71 | int min = datetime.substring(10, 12).toInt(); 72 | int sec = datetime.substring(12, 14).toInt(); 73 | setTime( hr, min, sec, day, month, year); 74 | } 75 | 76 | 77 | void powerOff() { 78 | //define wake up logic 79 | NRF_GPIO->PIN_CNF[BTN_BCK] &= ~((uint32_t)GPIO_PIN_CNF_SENSE_Msk); 80 | NRF_GPIO->PIN_CNF[BTN_BCK] |= ((uint32_t)GPIO_PIN_CNF_SENSE_Low << GPIO_PIN_CNF_SENSE_Pos); 81 | 82 | if (checkForSoftDevice() == 1) { 83 | // SoftDevice enabled 84 | sd_power_system_off(); 85 | } else { 86 | // No SoftDevice 87 | NRF_POWER->SYSTEMOFF = 1; 88 | } 89 | } 90 | 91 | uint8_t checkForSoftDevice() { 92 | uint8_t check; 93 | sd_softdevice_is_enabled(&check); 94 | 95 | return check; 96 | } 97 | 98 | void accelStdby(){ 99 | sendToRegister(0x18, 0);//CNTL1 Standby Mode 100 | } 101 | --------------------------------------------------------------------------------