re-add f5 variant

This commit is contained in:
Gordon Williams 2019-10-11 14:56:58 +01:00
parent 5fa9c7d770
commit 75bc0132c0
8 changed files with 1183 additions and 174 deletions

72
.gitignore vendored
View File

@ -10,51 +10,51 @@
*.bin
*.pyc
/*.zip
gen/*
.vagrant/*
boards/*.html
boards/*.json
zipcontents
tmp
benchmark/*.txt
benchmark/*.result.json
/gen/*
/.vagrant/*
/boards/*.html
/boards/*.json
/zipcontents
/tmp
/benchmark/*.txt
/benchmark/*.result.json
/espruino
archives/*
diffs/*
.cproject
.project
.settings
MDN_URLS.txt
/archives/*
/diffs/*
/.cproject
/.project
/.settings
/MDN_URLS.txt
node_modules
/build/
esp_iot_sdk_v1.5.0
esp_iot_sdk_v2.0.0.p1
ESP8266_NONOS_SDK-2.2.1
xtensa-lx106-elf
app
esp-idf
/esp_iot_sdk_v1.5.0
/esp_iot_sdk_v2.0.0.p1
/ESP8266_NONOS_SDK-2.2.1
/xtensa-lx106-elf
/app
/esp-idf
xtensa-esp32-elf
gcc-arm-none-eabi*
*.c#
/function_keywords.js
/functions.html
targetlibs/nrf5x_11
targetlibs/nrf5x_12/examples
targetlibs/nrf5x_12/documentation/
targetlibs/nrf5x_14
targetlibs/nrf5x_15/components
targetlibs/nrf5x_15/example_config
targetlibs/nrf5x_15/examples
targetlibs/nrf5x_15/external
targetlibs/nrf5x_15/external_tools
targetlibs/nrf5x_15/integration
targetlibs/nrf5x_15/modules
targetlibs/nrf5x_15_*
targetlibs/nrf5x_15x
targetlibs/nrf5x_mesh
targetlibs/raspberrypi
/targetlibs/nrf5x_11
/targetlibs/nrf5x_12/examples
/targetlibs/nrf5x_12/documentation/
/targetlibs/nrf5x_14
/targetlibs/nrf5x_15/components
/targetlibs/nrf5x_15/example_config
/targetlibs/nrf5x_15/examples
/targetlibs/nrf5x_15/external
/targetlibs/nrf5x_15/external_tools
/targetlibs/nrf5x_15/integration
/targetlibs/nrf5x_15/modules
/targetlibs/nrf5x_15_*
/targetlibs/nrf5x_15x
/targetlibs/nrf5x_mesh
/targetlibs/raspberrypi
/.vscode
/CURRENT_BOARD.make
/topreadonly
/topstrings
misc
/misc

184
boards/BANGLEF5.py Normal file
View File

@ -0,0 +1,184 @@
#!/bin/false
# This file is part of Espruino, a JavaScript interpreter for Microcontrollers
#
# Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# ----------------------------------------------------------------------------------------
# This file contains information for a specific board - the available pins, and where LEDs,
# Buttons, and other in-built peripherals are. It is used to build documentation as well
# as various source and header files for Espruino.
# ----------------------------------------------------------------------------------------
import pinutils;
info = {
'name' : "Espruino Bangle.js",
'link' : [ "http://www.espruino.com/Bangle.js" ],
'espruino_page_link' : 'Bangle.js',
'default_console' : "EV_BLUETOOTH",
'variables' : 2100, # How many variables are allocated for Espruino to use. RAM will be overflowed if this number is too high and code won't compile.
'bootloader' : 1,
'binary_name' : 'espruino_%v_banglef5.hex',
'build' : {
'optimizeflags' : '-Os',
'libraries' : [
'BLUETOOTH',
'TERMINAL',
'GRAPHICS',
'LCD_SPI',
# 'TENSORFLOW'
],
'makefile' : [
'DEFINES += -DCONFIG_NFCT_PINS_AS_GPIOS', # Allow the reset pin to work
'DEFINES += -DBUTTONPRESS_TO_REBOOT_BOOTLOADER',
'DEFINES+=-DBLUETOOTH_NAME_PREFIX=\'"Bangle.js"\'',
'DEFINES+=-DDUMP_IGNORE_VARIABLES=\'"g\\0"\'',
'DEFINES+=-DUSE_FONT_6X8 -DGRAPHICS_PALETTED_IMAGES',
'DFU_PRIVATE_KEY=targets/nrf5x_dfu/dfu_private_key.pem',
'DFU_SETTINGS=--application-version 0xff --hw-version 52 --sd-req 0x8C',
'INCLUDE += -I$(ROOT)/libs/banglejs -I$(ROOT)/libs/misc',
'WRAPPERSOURCES += libs/banglejs/jswrap_banglef5.c',
'SOURCES += libs/misc/nmea.c',
'JSMODULESOURCES += libs/js/graphical_menu.min.js',
'NRF_BL_DFU_INSECURE=1',
'LINKER_BOOTLOADER=targetlibs/nrf5x_12/nrf5x_linkers/banglejs_dfu.ld',
'LINKER_ESPRUINO=targetlibs/nrf5x_12/nrf5x_linkers/banglejs_espruino.ld'
]
}
};
chip = {
'part' : "NRF52832",
'family' : "NRF52",
'package' : "QFN48",
'ram' : 64,
'flash' : 512,
'speed' : 64,
'usart' : 1,
'spi' : 1,
'i2c' : 1,
'adc' : 1,
'dac' : 0,
'saved_code' : {
'address' : 0x40000000, # put this in external flash
'page_size' : 4096,
'pages' : 64, # 256kb - still loads left
'flash_available' : 512 - ((31 + 8 + 2)*4) # Softdevice uses 31 pages of flash, bootloader 8, FS 2. Each page is 4 kb.
},
};
devices = {
'BTN1' : { 'pin' : 'D12', 'pinstate' : 'IN_PULLDOWN' }, # Top right - Pin negated in software
'BTN2' : { 'pin' : 'D13', 'pinstate' : 'IN_PULLDOWN' }, # Bottom right - Pin negated in software
'BTN3' : { 'pin' : 'D16' }, # Touch
# 'BTN4' : { 'pin' : 'D16', 'pinstate' : 'IN_PULLDOWN' }, # Pin negated in software
'LED1' : { 'pin' : 'D14' }, # Pin negated in software
# 'LED2' : { 'pin' : 'D18' }, # Pin negated in software
# 'LED3' : { 'pin' : 'D19' }, # Pin negated in software
# 'LED4' : { 'pin' : 'D20' }, # Pin negated in software
'VIBRATE' : { 'pin' : 'D11' }, # Pin negated in software
'LCD' : {
'width' : 128, 'height' : 96, 'bpp' : 4,
'controller' : 'st7735',
'pin_dc' : 'D22',
'pin_cs' : 'D10',
'pin_rst' : 'D23',
'pin_sck' : 'D9',
'pin_mosi' : 'D8',
'pin_bl' : 'D21',
},
'GPS' : {
'device' : 'M8130-KT',
'pin_en' : 'D0', # inverted
'pin_rx' : 'D5',
'pin_tx' : 'D6'
},
'BAT' : {
'pin_charging' : 'D7', # inverted
'pin_voltage' : 'D4'
},
'HEARTRATE' : {
'pin_led' : 'D14',
'pin_analog' : 'D3'
},
'ACCEL' : {
'device' : 'KX023', 'addr' : 0x1e,
'pin_sda' : 'D1',
'pin_scl' : 'D2'
},
'SPIFLASH' : {
'pin_cs' : 'D18',
'pin_sck' : 'D19',
'pin_mosi' : 'D20',
'pin_miso' : 'D17',
'size' : 2097152
},
'PRESSURE' : {
'device' : 'HP203', 'addr' : 0x76,
'pin_sda' : 'D1',
'pin_scl' : 'D2'
},
};
# left-right, or top-bottom order
board = {
'left' : [],
'right' : [],
'_notes' : {
}
};
board["_css"] = """
#board {
width: 528px;
height: 800px;
top: 0px;
left : 200px;
background-image: url(img/BANGLEF5.jpg);
}
#boardcontainer {
height: 900px;
}
#left {
top: 219px;
right: 466px;
}
#right {
top: 150px;
left: 466px;
}
.leftpin { height: 17px; }
.rightpin { height: 17px; }
""";
def get_pins():
pins = pinutils.generate_pins(0,31) # 32 General Purpose I/O Pins.
pinutils.findpin(pins, "PD0", True)["functions"]["XL1"]=0;
pinutils.findpin(pins, "PD1", True)["functions"]["XL2"]=0;
pinutils.findpin(pins, "PD2", True)["functions"]["ADC1_IN0"]=0;
pinutils.findpin(pins, "PD3", True)["functions"]["ADC1_IN1"]=0;
pinutils.findpin(pins, "PD4", True)["functions"]["ADC1_IN2"]=0;
pinutils.findpin(pins, "PD5", True)["functions"]["ADC1_IN3"]=0;
pinutils.findpin(pins, "PD28", True)["functions"]["ADC1_IN4"]=0;
pinutils.findpin(pins, "PD29", True)["functions"]["ADC1_IN5"]=0;
pinutils.findpin(pins, "PD30", True)["functions"]["ADC1_IN6"]=0;
pinutils.findpin(pins, "PD31", True)["functions"]["ADC1_IN7"]=0;
# negate buttons
pinutils.findpin(pins, "PD12", True)["functions"]["NEGATED"]=0; # btn1
pinutils.findpin(pins, "PD13", True)["functions"]["NEGATED"]=0; # btn2
pinutils.findpin(pins, "PD11", True)["functions"]["NEGATED"]=0; # vibrate
pinutils.findpin(pins, "PD14", True)["functions"]["NEGATED"]=0; # HRM LED
# everything is non-5v tolerant
for pin in pins:
pin["functions"]["3.3"]=0;
#The boot/reset button will function as a reset button in normal operation. Pin reset on PD21 needs to be enabled on the nRF52832 device for this to work.
return pins

View File

@ -30,7 +30,7 @@ info = {
'TERMINAL',
'GRAPHICS',
'LCD_ST7789_8BIT',
#'TENSORFLOW'
'TENSORFLOW'
],
'makefile' : [
'DEFINES += -DCONFIG_NFCT_PINS_AS_GPIOS', # Allow the reset pin to work
@ -40,8 +40,9 @@ info = {
'DEFINES+=-DUSE_FONT_6X8 -DGRAPHICS_PALETTED_IMAGES',
'DFU_PRIVATE_KEY=targets/nrf5x_dfu/dfu_private_key.pem',
'DFU_SETTINGS=--application-version 0xff --hw-version 52 --sd-req 0x8C',
'INCLUDE += -I$(ROOT)/libs/banglejs',
'INCLUDE += -I$(ROOT)/libs/banglejs -I$(ROOT)/libs/misc',
'WRAPPERSOURCES += libs/banglejs/jswrap_bangle.c',
'SOURCES += libs/misc/nmea.c',
'JSMODULESOURCES += libs/js/graphical_menu.min.js',
'NRF_BL_DFU_INSECURE=1',
'LINKER_BOOTLOADER=targetlibs/nrf5x_12/nrf5x_linkers/banglejs_dfu.ld',

View File

@ -36,6 +36,7 @@
#include "jswrap_graphics.h"
#include "lcd_st7789_8bit.h"
#include "nmea.h"
/*JSON{
"type": "class",
@ -161,18 +162,8 @@ If the watch is tapped, this event contains information on the way it was tapped
#define IOEXP_LCD_RESET 0x40
#define IOEXP_HRM 0x80
typedef struct {
double lat,lon,alt;
double speed, course;
int hour,min,sec,ms;
uint8_t day,month,year;
uint8_t quality; // from GGA packet, 0 = no fix
uint8_t satellites; // how many satellites
} NMEAFixInfo;
#define NMEA_MAX_SIZE 82 // 82 is the max for NMEA
uint8_t nmeaCount = 0; // how many characters of NMEA data do we have?
char nmeaIn[NMEA_MAX_SIZE]; // NMEA line being received right now
char nmeaIn[NMEA_MAX_SIZE]; // 82 is the max for NMEA
char nmeaLine[NMEA_MAX_SIZE]; // A line of received NMEA data
NMEAFixInfo gpsFix;
@ -783,27 +774,8 @@ bool jswrap_banglejs_idle() {
}
}
if (bangle && (bangleTasks & JSBT_GPS_DATA)) {
JsVar *o = jsvNewObject();
JsVar *o = nmea_to_jsVar(&gpsFix);
if (o) {
jsvObjectSetChildAndUnLock(o, "lat", jsvNewFromFloat(gpsFix.lat));
jsvObjectSetChildAndUnLock(o, "lon", jsvNewFromFloat(gpsFix.lon));
jsvObjectSetChildAndUnLock(o, "alt", jsvNewFromFloat(gpsFix.alt));
jsvObjectSetChildAndUnLock(o, "speed", jsvNewFromFloat(gpsFix.speed));
jsvObjectSetChildAndUnLock(o, "course", jsvNewFromFloat(gpsFix.course));
CalendarDate date;
date.day = gpsFix.day;
date.month = gpsFix.month;
date.year = 2000+gpsFix.year;
TimeInDay td;
td.daysSinceEpoch = fromCalenderDate(&date);
td.hour = gpsFix.hour;
td.min = gpsFix.min;
td.sec = gpsFix.sec;
td.ms = gpsFix.ms;
td.zone = 0; // jsdGetTimeZone(); - no! GPS time is always in UTC :)
jsvObjectSetChildAndUnLock(o, "time", jswrap_date_from_milliseconds(fromTimeInDay(&td)));
jsvObjectSetChildAndUnLock(o, "satellites", jsvNewFromInteger(gpsFix.satellites));
jsvObjectSetChildAndUnLock(o, "fix", jsvNewFromInteger(gpsFix.quality));
jsiQueueObjectCallbacks(bangle, JS_EVENT_PREFIX"GPS", &o, 1);
jsvUnLock(o);
}
@ -812,7 +784,6 @@ bool jswrap_banglejs_idle() {
JsVar *line = jsvNewFromString(nmeaLine);
if (line) {
jsiQueueObjectCallbacks(bangle, JS_EVENT_PREFIX"GPS-raw", &line, 1);
}
jsvUnLock(line);
}
@ -852,109 +823,6 @@ bool jswrap_banglejs_idle() {
}
char *nmea_next_comma(char *nmea) {
while (*nmea && *nmea!=',') nmea++; // find the comma
return nmea;
}
double nmea_decode_latlon(char *nmea, char *comma) {
if (*nmea==',') return NAN; // no reading
char *dp = nmea;
while (*dp && *dp!='.' && *dp!=',') dp++; // find decimal pt
*comma = 0;
double minutes = stringToFloat(&dp[-2]);
*comma = ',';
dp[-2] = 0;
int x = stringToInt(nmea);
return x+(minutes/60);
}
double nmea_decode_float(char *nmea, char *comma) {
*comma = 0;
double r = stringToFloat(nmea);
*comma = ',';
return r;
}
uint8_t nmea_decode_1(char *nmea) {
return chtod(nmea[0]);
}
uint8_t nmea_decode_2(char *nmea) {
return chtod(nmea[0])*10 + chtod(nmea[1]);
}
bool nmea_decode(const char *nmeaLine) {
char buf[NMEA_MAX_SIZE];
strcpy(buf, nmeaLine);
char *nmea = buf, *nextComma;
if (nmea[0]!='$' || nmea[1]!='G') return false; // not valid
if (nmea[3]=='R' && nmea[4]=='M' && nmea[5]=='C') {
// $GNRMC,161945.00,A,5139.11397,N,00116.07202,W,1.530,,190919,,,A*7E
nmea = nmea_next_comma(nmea)+1;
nextComma = nmea_next_comma(nmea);
// time
gpsFix.hour = nmea_decode_2(&nmea[0]);
gpsFix.min = nmea_decode_2(&nmea[2]);
gpsFix.sec = nmea_decode_2(&nmea[4]);
gpsFix.ms = nmea_decode_2(&nmea[7]);
// status
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);//?
// lat + NS
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// lon + EW
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// speed
gpsFix.speed = nmea_decode_float(nmea, nextComma);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// course
gpsFix.course = nmea_decode_float(nmea, nextComma);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// date
gpsFix.day = nmea_decode_2(&nmea[0]);
gpsFix.month = nmea_decode_2(&nmea[2]);
gpsFix.year = nmea_decode_2(&nmea[4]);
// ....
}
if (nmea[3]=='G' && nmea[4]=='G' && nmea[5]=='A') {
// $GNGGA,161945.00,5139.11397,N,00116.07202,W,1,06,1.29,71.1,M,47.0,M,,*64
nmea = nmea_next_comma(nmea)+1;
nextComma = nmea_next_comma(nmea);
// time
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// LAT
gpsFix.lat = nmea_decode_latlon(nmea, nextComma);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
if (*nmea=='S') gpsFix.lat=-gpsFix.lat;
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// LON
gpsFix.lon = nmea_decode_latlon(nmea, nextComma);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
if (*nmea=='W') gpsFix.lon=-gpsFix.lon;
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// quality
gpsFix.quality = nmea_decode_1(nmea);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// num satellites
gpsFix.satellites = nmea_decode_2(nmea);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// dilution of precision
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// altitude
gpsFix.alt = nmea_decode_float(nmea, nextComma);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// ....
}
if (nmea[3]=='G' && nmea[4]=='S' && nmea[5]=='V') {
// loads of cool data about what satellites we have
}
if (nmea[3]=='G' && nmea[4]=='L' && nmea[5]=='L') {
// Complete set of data received
return true;
}
return false;
}
/*JSON{
"type" : "EV_SERIAL1",
"generate" : "jswrap_banglejs_gps_character"
@ -979,7 +847,7 @@ bool jswrap_banglejs_gps_character(char ch) {
memcpy(nmeaLine, nmeaIn, nmeaCount);
nmeaLine[nmeaCount-1]=0; // just overwriting \n
bangleTasks |= JSBT_GPS_DATA_LINE;
if (nmea_decode(nmeaLine))
if (nmea_decode(&gpsFix, nmeaLine))
bangleTasks |= JSBT_GPS_DATA;
}
nmeaCount = 0;

View File

@ -0,0 +1,748 @@
/*
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
*
* Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* ----------------------------------------------------------------------------
* This file is designed to be parsed during the build process
*
* Contains JavaScript interface for Pixl.js (http://www.espruino.com/Pixl.js)
* ----------------------------------------------------------------------------
*/
#include <jswrap_banglef5.h>
#include "jsinteractive.h"
#include "jsdevices.h"
#include "jsnative.h"
#include "jshardware.h"
#include "jsdevices.h"
#include "jspin.h"
#include "jstimer.h"
#include "jswrap_promise.h"
#include "jswrap_bluetooth.h"
#include "nrf_gpio.h"
#include "nrf_delay.h"
#include "nrf_soc.h"
#include "nrf5x_utils.h"
#include "jsflash.h" // for jsfRemoveCodeFromFlash
#include "bluetooth.h" // for self-test
#include "jsi2c.h" // accelerometer/etc
#include "jswrap_graphics.h"
#include "lcd_spilcd.h"
#include "nmea.h"
#define GPS_UART EV_SERIAL1
uint8_t nmeaCount = 0; // how many characters of NMEA data do we have?
char nmeaIn[NMEA_MAX_SIZE]; // 82 is the max for NMEA
char nmeaLine[NMEA_MAX_SIZE]; // A line of received NMEA data
NMEAFixInfo gpsFix;
/*JSON{
"type": "class",
"class" : "Bangle",
"ifdef" : "BANGLEJS"
}
Class containing utility functions for the [Bangle.js Smart Watch](http://www.espruino.com/Bangle.js)
*/
/*JSON{
"type" : "variable",
"name" : "VIBRATE",
"generate_full" : "VIBRATE_PIN",
"ifdef" : "BANGLEJS",
"return" : ["pin",""]
}
The Bangle.js's vibration motor.
*/
#define ACCEL_POLL_INTERVAL 100 // in msec
/// Internal I2C used for Accelerometer/Pressure
JshI2CInfo internalI2C;
/// Is I2C busy? if so we'll skip one reading in our interrupt so we don't overlap
bool i2cBusy;
/// Promise when pressure is requested
JsVar *promisePressure;
/// counter that counts up if watch has stayed face up or down
unsigned char faceUpCounter;
/// Was the watch face-up? we use this when firing events
bool wasFaceUp;
/// time since LCD contents were last modified
volatile unsigned char flipCounter;
/// Is LCD power automatic? If true this is the number of ms for the timeout, if false it's 0
int lcdPowerTimeout = 100;
/// Is the LCD on?
bool lcdPowerOn;
/// accelerometer data
int accx,accy,accz,accdiff;
/// data on how watch was tapped
unsigned char tapInfo;
typedef enum {
JSBT_NONE,
JSBT_LCD_ON = 1,
JSBT_LCD_OFF = 2,
JSBT_ACCEL_DATA = 4, // need to push xyz data to JS
JSBT_ACCEL_TAPPED = 8, // tap event detected
JSBT_GPS_DATA = 16, ///< we got a complete set of GPS data in 'gpsFix'
JSBT_GPS_DATA_LINE = 32, ///< we got a line of GPS data
} JsBangleTasks;
JsBangleTasks bangleTasks;
/// Send buffer contents to the screen. Usually only the modified data will be output, but if all=true then the whole screen contents is sent
void lcd_flip(JsVar *parent, bool all) {
JsGraphics gfx;
if (!graphicsGetFromVar(&gfx, parent)) return;
if (all) {
gfx.data.modMinX = 0;
gfx.data.modMinY = 0;
gfx.data.modMaxX = LCD_WIDTH-1;
gfx.data.modMaxY = LCD_HEIGHT-1;
}
if (lcdPowerTimeout && !lcdPowerOn) {
// LCD was turned off, turn it back on
jswrap_banglejs_setLCDPower(1);
}
flipCounter = 0;
lcdFlip_SPILCD(&gfx);
graphicsSetVar(&gfx);
}
/*JSON{
"type" : "staticmethod",
"class" : "Bangle",
"name" : "setLCDPower",
"generate" : "jswrap_banglejs_setLCDPower",
"params" : [
["isOn","bool","True if the LCD should be on, false if not"]
]
}
This function can be used to turn Bangle.js's LCD off or on.
*/
void jswrap_banglejs_setLCDPower(bool isOn) {
if (isOn) {
lcdCmd_SPILCD(0x11, 0, NULL); // SLPOUT
jshPinOutput(LCD_BL,0); // backlight
} else {
lcdCmd_SPILCD(0x10, 0, NULL); // SLPIN
jshPinOutput(LCD_BL,1); // backlight
}
if (lcdPowerOn != isOn) {
JsVar *bangle =jsvObjectGetChild(execInfo.root, "Bangle", 0);
if (bangle) {
JsVar *v = jsvNewFromBool(isOn);
jsiQueueObjectCallbacks(bangle, JS_EVENT_PREFIX"lcdPower", &v, 1);
jsvUnLock(v);
}
jsvUnLock(bangle);
}
lcdPowerOn = isOn;
}
/*JSON{
"type" : "staticmethod",
"class" : "Bangle",
"name" : "setLCDTimeout",
"generate" : "jswrap_banglejs_setLCDTimeout",
"params" : [
["isOn","float","The timeout of the display in seconds, or `0`/`undefined` to turn power saving off. Default is 10 seconds."]
]
}
This function can be used to turn Bangle.js's LCD power saving on or off.
With power saving off, the display will remain in the state you set it with `Bangle.setLCDPower`.
With power saving on, the display will turn on if a button is pressed, the watch is turned face up, or the screen is updated. It'll turn off automatically after the given timeout.
*/
void jswrap_banglejs_setLCDTimeout(JsVarFloat timeout) {
if (!isfinite(timeout)) lcdPowerTimeout=0;
else lcdPowerTimeout = timeout*(1000.0/ACCEL_POLL_INTERVAL);
if (lcdPowerTimeout<0) lcdPowerTimeout=0;
}
/*JSON{
"type" : "staticmethod",
"class" : "Bangle",
"name" : "setLCDPalette",
"generate" : "jswrap_banglejs_setLCDPalette",
"params" : [
["palette","JsVar","An array of 24 bit 0xRRGGBB values"]
]
}
Bangle.js's LCD can display colours in 12 bit, but to keep the offscreen
buffer to a reasonable size it uses a 4 bit paletted buffer.
With this, you can change the colour palette that is used.
*/
void jswrap_banglejs_setLCDPalette(JsVar *palette) {
if (jsvIsIterable(palette)) {
uint16_t pal[16];
JsvIterator it;
jsvIteratorNew(&it, palette, JSIF_EVERY_ARRAY_ELEMENT);
int idx = 0;
while (idx<16 && jsvIteratorHasElement(&it)) {
unsigned int rgb = jsvIteratorGetIntegerValue(&it);
unsigned int r = rgb>>16;
unsigned int g = (rgb>>8)&0xFF;
unsigned int b = rgb&0xFF;
pal[idx++] = ((r&0xF0)<<4) | (g&0xF0) | (b>>4);
jsvIteratorNext(&it);
}
jsvIteratorFree(&it);
lcdSetPalette_SPILCD(pal);
} else
lcdSetPalette_SPILCD(0);
}
/*JSON{
"type" : "staticmethod",
"class" : "Bangle",
"name" : "isLCDOn",
"generate" : "jswrap_banglejs_isLCDOn",
"return" : ["bool","Is the display on or not?"]
}
*/
bool jswrap_banglejs_isLCDOn() {
return lcdPowerOn;
}
/*JSON{
"type" : "staticmethod",
"class" : "Bangle",
"name" : "lcdWr",
"generate" : "jswrap_banglejs_lcdWr",
"params" : [
["cmd","int",""],
["data","JsVar",""]
]
}
Writes a command directly to the ST7735 LCD controller
*/
void jswrap_banglejs_lcdWr(JsVarInt cmd, JsVar *data) {
JSV_GET_AS_CHAR_ARRAY(dPtr, dLen, data);
lcdCmd_SPILCD(cmd, dLen, dPtr);
}
/*JSON{
"type" : "staticmethod",
"class" : "Bangle",
"name" : "setGPSPower",
"generate" : "jswrap_banglejs_setGPSPower",
"params" : [
["isOn","bool","True if the GPS should be on, false if not"]
]
}
Set the power to the GPS.
*/
void jswrap_banglejs_setGPSPower(bool isOn) {
if (isOn) {
JshUSARTInfo inf;
jshUSARTInitInfo(&inf);
inf.baudRate = 9600;
inf.pinRX = GPS_PIN_RX;
inf.pinTX = GPS_PIN_TX;
jshUSARTSetup(GPS_UART, &inf);
jshPinOutput(GPS_PIN_EN,1); // GPS on
nmeaCount = 0;
} else {
jshPinOutput(GPS_PIN_EN,0); // GPS off
// setting pins to pullup will cause jshardware.c to disable the UART, saving power
jshPinSetState(GPS_PIN_RX, JSHPINSTATE_GPIO_IN_PULLUP);
jshPinSetState(GPS_PIN_TX, JSHPINSTATE_GPIO_IN_PULLUP);
}
}
// Holding down both buttons will reboot
void watchdogHandler() {
//jshPinOutput(LED1_PININDEX, 1);
// Handle watchdog
if (!(jshPinGetValue(BTN1_PININDEX) && jshPinGetValue(BTN2_PININDEX)))
jshKickWatchDog();
// power on display if a button is pressed
if (lcdPowerTimeout &&
(jshPinGetValue(BTN1_PININDEX) || jshPinGetValue(BTN2_PININDEX) ||
jshPinGetValue(BTN3_PININDEX))) {
flipCounter = 0;
if (!lcdPowerOn)
bangleTasks |= JSBT_LCD_ON;
}
if (flipCounter<255) flipCounter++;
if (lcdPowerTimeout && lcdPowerOn && flipCounter>=lcdPowerTimeout) {
// 10 seconds of inactivity, turn off display
bangleTasks |= JSBT_LCD_OFF;
}
if (i2cBusy) return;
// poll KX023 accelerometer (no other way as IRQ line seems disconnected!)
unsigned char buf[6];
buf[0]=6;
jsi2cWrite(&internalI2C, ACCEL_ADDR, 1, buf, true);
jsi2cRead(&internalI2C, ACCEL_ADDR, 6, buf, true);
int newx = (buf[1]<<8)|buf[0];
int newy = (buf[3]<<8)|buf[2];
int newz = (buf[5]<<8)|buf[4];
if (newx&0x8000) newx-=0x10000;
if (newy&0x8000) newy-=0x10000;
if (newz&0x8000) newz-=0x10000;
int dx = newx-accx;
int dy = newy-accy;
int dz = newz-accz;
accx = newx;
accy = newy;
accz = newz;
accdiff = dx*dx + dy*dy + dz*dz;
bangleTasks |= JSBT_ACCEL_DATA;
// read interrupt source data
buf[0]=0x12;
jsi2cWrite(&internalI2C, ACCEL_ADDR, 1, buf, true);
jsi2cRead(&internalI2C, ACCEL_ADDR, 2, buf, true);
// 0 -> 0x12 INS1 - tap event
// 1 -> 0x13 INS2 - what kind of event
int tapType = (buf[1]>>2)&3;
if (tapType) {
// report tap
tapInfo = buf[0] | (tapType<<6);
bangleTasks |= JSBT_ACCEL_TAPPED;
// clear the IRQ flags
buf[0]=0x17;
jsi2cWrite(&internalI2C, ACCEL_ADDR, 1, buf, true);
jsi2cRead(&internalI2C, ACCEL_ADDR, 1, buf, true);
}
//jshPinOutput(LED1_PININDEX, 0);
}
/*JSON{
"type" : "init",
"generate" : "jswrap_banglejs_init"
}*/
void jswrap_banglejs_init() {
jshPinOutput(GPS_PIN_EN,0); // GPS off
jshPinOutput(VIBRATE_PIN,0); // vibrate off
jshPinOutput(LED1_PININDEX,0); // LED off
lcdPowerOn = true;
// Create backing graphics for LCD
JsVar *graphics = jspNewObject(0, "Graphics");
if (!graphics) return; // low memory
JsGraphics gfx;
graphicsStructInit(&gfx);
gfx.data.type = JSGRAPHICSTYPE_SPILCD;
gfx.data.flags = JSGRAPHICSFLAGS_INVERT_X | JSGRAPHICSFLAGS_INVERT_Y;
gfx.graphicsVar = graphics;
gfx.data.width = LCD_WIDTH;
gfx.data.height = LCD_HEIGHT;
gfx.data.bpp = LCD_BPP;
//gfx.data.fontSize = JSGRAPHICS_FONTSIZE_6X8;
lcdInit_SPILCD(&gfx);
graphicsSetVar(&gfx);
jsvObjectSetChild(execInfo.root, "g", graphics);
jsvObjectSetChild(execInfo.hiddenRoot, JS_GRAPHICS_VAR, graphics);
graphicsGetFromVar(&gfx, graphics);
// Create 'flip' fn
JsVar *fn;
fn = jsvNewNativeFunction((void (*)(void))lcd_flip, JSWAT_VOID|JSWAT_THIS_ARG|(JSWAT_BOOL << (JSWAT_BITS*1)));
jsvObjectSetChildAndUnLock(graphics,"flip",fn);
/* If the button is pressed during reset, perform a self test.
* With bootloader this means apply power while holding button for >3 secs */
static bool firstStart = true;
graphicsClear(&gfx);
int h=6;
jswrap_graphics_drawCString(&gfx,0,h*1," ____ _ ");
jswrap_graphics_drawCString(&gfx,0,h*2,"| __|___ ___ ___ _ _|_|___ ___ ");
jswrap_graphics_drawCString(&gfx,0,h*3,"| __|_ -| . | _| | | | | . |");
jswrap_graphics_drawCString(&gfx,0,h*4,"|____|___| _|_| |___|_|_|_|___|");
jswrap_graphics_drawCString(&gfx,0,h*5," |_| espruino.com");
jswrap_graphics_drawCString(&gfx,0,h*6," "JS_VERSION" (c) 2019 G.Williams");
// Write MAC address in bottom right
JsVar *addr = jswrap_ble_getAddress();
char buf[20];
jsvGetString(addr, buf, sizeof(buf));
jsvUnLock(addr);
jswrap_graphics_drawCString(&gfx,(LCD_WIDTH-1)-strlen(buf)*6,h*8,buf);
lcdFlip_SPILCD(&gfx);
/*
if (firstStart && (jshPinGetValue(BTN1_PININDEX) == BTN1_ONSTATE || jshPinGetValue(BTN4_PININDEX) == BTN4_ONSTATE)) {
// don't do it during a software reset - only first hardware reset
jsiConsolePrintf("SELF TEST\n");
if (pixl_selfTest()) jsiConsolePrintf("Test passed!\n");
}*/
// If the button is *still* pressed, remove all code from flash memory too!
/*if (firstStart && jshPinGetValue(BTN1_PININDEX) == BTN1_ONSTATE) {
jsfRemoveCodeFromFlash();
jsiConsolePrintf("Removed saved code from Flash\n");
}*/
graphicsSetVar(&gfx);
firstStart = false;
jsvUnLock(graphics);
// Setup touchscreen I2C
i2cBusy = true;
jshI2CInitInfo(&internalI2C);
internalI2C.bitrate = 0x7FFFFFFF;
internalI2C.pinSDA = ACCEL_PIN_SDA;
internalI2C.pinSCL = ACCEL_PIN_SCL;
jshPinSetValue(internalI2C.pinSCL, 1);
jshPinSetState(internalI2C.pinSCL, JSHPINSTATE_GPIO_OUT_OPENDRAIN_PULLUP);
jshPinSetValue(internalI2C.pinSDA, 1);
jshPinSetState(internalI2C.pinSDA, JSHPINSTATE_GPIO_OUT_OPENDRAIN_PULLUP);
// accelerometer init
jswrap_banglejs_accelWr(0x18,0x0a); // CNTL1 Off, 4g range, Wakeup
jswrap_banglejs_accelWr(0x19,0x80); // CNTL2 Software reset
jshDelayMicroseconds(2000);
/*jswrap_banglejs_accelWr(0x1b,0x02); // ODCNTL - 50Hz acceleration output data rate, filteringlow-pass ODR/9
jswrap_banglejs_accelWr(0x1a,0xb11011110); // CNTL3
// 50Hz tilt
// 50Hz directional tap
// 50Hz general motion detection and the high-pass filtered outputs
jswrap_banglejs_accelWr(0x1c,0); // INC1 disabled
jswrap_banglejs_accelWr(0x1d,0); // INC2 disabled
jswrap_banglejs_accelWr(0x1e,0); // INC3 disabled
jswrap_banglejs_accelWr(0x1f,0); // INC4 disabled
jswrap_banglejs_accelWr(0x20,0); // INC5 disabled
jswrap_banglejs_accelWr(0x21,0); // INC6 disabled
jswrap_banglejs_accelWr(0x23,3); // WUFC wakeupi detect counter
//jswrap_banglejs_accelWr(0x24,3); // TDTRC Tap detect enable
//jswrap_banglejs_accelWr(0x25, 0x78); // TDTC Tap detect double tap
//jswrap_banglejs_accelWr(0x26, 0x20); // TTH Tap detect threshold high (0xCB recommended)
//jswrap_banglejs_accelWr(0x27, 0x10); // TTH Tap detect threshold low (0x1A recommended)
jswrap_banglejs_accelWr(0x30,1); // ATH low wakeup detect threshold
jswrap_banglejs_accelWr(0x35,0); // LP_CNTL no averaging of samples
jswrap_banglejs_accelWr(0x3e,0); // clear the buffer*/
jswrap_banglejs_accelWr(0x18,0b10001100); // CNTL1 On, ODR/2(high res), 4g range, Wakeup, tap
// pressure init
buf[0]=0x06; jsi2cWrite(&internalI2C, PRESSURE_ADDR, 1, (uint8_t)*buf, true); // SOFT_RST
i2cBusy = false;
// Add watchdog timer to ensure watch always stays usable (hopefully!)
// This gets killed when _kill / _init happens
// - the bootloader probably already set this up so the
// enable will do nothing - but good to try anyway
jshEnableWatchDog(5); // 5 second watchdog
// This timer kicks the watchdog, and does some other stuff as well
JsSysTime t = jshGetTimeFromMilliseconds(ACCEL_POLL_INTERVAL);
jstExecuteFn(watchdogHandler, NULL, jshGetSystemTime()+t, t);
}
/*JSON{
"type" : "kill",
"generate" : "jswrap_banglejs_kill"
}*/
void jswrap_banglejs_kill() {
jstStopExecuteFn(watchdogHandler, 0);
jsvUnLock(promisePressure);
promisePressure = 0;
}
/*JSON{
"type" : "idle",
"generate" : "jswrap_banglejs_idle"
}*/
bool jswrap_banglejs_idle() {
if (bangleTasks == JSBT_NONE) return false;
JsVar *bangle =jsvObjectGetChild(execInfo.root, "Bangle", 0);
if (bangleTasks & JSBT_LCD_OFF) jswrap_banglejs_setLCDPower(0);
if (bangleTasks & JSBT_LCD_ON) jswrap_banglejs_setLCDPower(1);
if (bangleTasks & JSBT_ACCEL_DATA) {
if (bangle && jsiObjectHasCallbacks(bangle, JS_EVENT_PREFIX"accel")) {
JsVar *o = jsvNewObject();
if (o) {
jsvObjectSetChildAndUnLock(o, "x", jsvNewFromFloat(accx/8192.0));
jsvObjectSetChildAndUnLock(o, "y", jsvNewFromFloat(accy/8192.0));
jsvObjectSetChildAndUnLock(o, "z", jsvNewFromFloat(accz/8192.0));
jsvObjectSetChildAndUnLock(o, "mag", jsvNewFromFloat(sqrt(accx*accx + accy*accy + accz*accz)/8192.0));
jsvObjectSetChildAndUnLock(o, "diff", jsvNewFromFloat(sqrt(accdiff)/8192.0));
jsiQueueObjectCallbacks(bangle, JS_EVENT_PREFIX"accel", &o, 1);
jsvUnLock(o);
}
}
bool faceUp = (accz<7000) && abs(accx)<4096 && abs(accy)<4096;
if (faceUp!=wasFaceUp) {
faceUpCounter=0;
wasFaceUp = faceUp;
}
if (faceUpCounter<255) faceUpCounter++;
if (faceUpCounter==2) {
if (bangle) {
JsVar *v = jsvNewFromBool(faceUp);
jsiQueueObjectCallbacks(bangle, JS_EVENT_PREFIX"faceUp", &v, 1);
jsvUnLock(v);
}
if (lcdPowerTimeout && !lcdPowerOn) {
// LCD was turned off, turn it back on
jswrap_banglejs_setLCDPower(1);
flipCounter = 0;
}
}
}
if (bangle && (bangleTasks & JSBT_ACCEL_TAPPED)) {
JsVar *o = jsvNewObject();
if (o) {
const char *string="";
if (tapInfo&1) string="front";
if (tapInfo&2) string="back";
if (tapInfo&4) string="bottom";
if (tapInfo&8) string="top";
if (tapInfo&16) string="right";
if (tapInfo&32) string="left";
int n = (tapInfo&0x80)?2:1;
jsvObjectSetChildAndUnLock(o, "dir", jsvNewFromString(string));
jsvObjectSetChildAndUnLock(o, "double", jsvNewFromBool(tapInfo&0x80));
jsvObjectSetChildAndUnLock(o, "x", jsvNewFromInteger((tapInfo&16)?-n:(tapInfo&32)?n:0));
jsvObjectSetChildAndUnLock(o, "y", jsvNewFromInteger((tapInfo&4)?-n:(tapInfo&8)?n:0));
jsvObjectSetChildAndUnLock(o, "z", jsvNewFromInteger((tapInfo&1)?-n:(tapInfo&2)?n:0));
jsiQueueObjectCallbacks(bangle, JS_EVENT_PREFIX"tap", &o, 1);
jsvUnLock(o);
}
}
if (bangle && (bangleTasks & JSBT_GPS_DATA)) {
JsVar *o = nmea_to_jsVar(&gpsFix);
if (o) {
jsiQueueObjectCallbacks(bangle, JS_EVENT_PREFIX"GPS", &o, 1);
jsvUnLock(o);
}
}
if (bangle && (bangleTasks & JSBT_GPS_DATA_LINE)) {
JsVar *line = jsvNewFromString(nmeaLine);
if (line) {
jsiQueueObjectCallbacks(bangle, JS_EVENT_PREFIX"GPS-raw", &line, 1);
}
jsvUnLock(line);
}
jsvUnLock(bangle);
bangleTasks = JSBT_NONE;
return false;
}
/*JSON{
"type" : "EV_SERIAL1",
"generate" : "jswrap_banglejs_gps_character"
}*/
bool jswrap_banglejs_gps_character(char ch) {
if (ch=='\r') return true; // we don't care
// if too many chars, roll over since it's probably because we skipped a newline
if (nmeaCount>=sizeof(nmeaIn)) nmeaCount=0;
nmeaIn[nmeaCount++]=ch;
if (ch!='\n') return true; // now handled
// Now we have a line of GPS data...
/* $GNRMC,161945.00,A,5139.11397,N,00116.07202,W,1.530,,190919,,,A*7E
$GNVTG,,T,,M,1.530,N,2.834,K,A*37
$GNGGA,161945.00,5139.11397,N,00116.07202,W,1,06,1.29,71.1,M,47.0,M,,*64
$GNGSA,A,3,09,06,23,07,03,29,,,,,,,1.96,1.29,1.48*14
$GPGSV,3,1,12,02,45,293,13,03,10,109,16,05,13,291,,06,56,213,25*73
$GPGSV,3,2,12,07,39,155,18,09,76,074,33,16,08,059,,19,02,218,18*7E
$GPGSV,3,3,12,23,40,066,23,26,08,033,18,29,07,342,20,30,14,180,*7F
$GNGLL,5139.11397,N,00116.07202,W,161945.00,A,A*69 */
// Let's just chuck it over into JS-land for now
if (nmeaCount>1) {
memcpy(nmeaLine, nmeaIn, nmeaCount);
nmeaLine[nmeaCount-1]=0; // just overwriting \n
bangleTasks |= JSBT_GPS_DATA_LINE;
if (nmea_decode(&gpsFix, nmeaLine))
bangleTasks |= JSBT_GPS_DATA;
}
nmeaCount = 0;
return true; // handled
}
/*JSON{
"type" : "staticmethod",
"class" : "Bangle",
"name" : "accelWr",
"generate" : "jswrap_banglejs_accelWr",
"params" : [
["reg","int",""],
["data","int",""]
]
}
Writes a register on the KX023 Accelerometer
*/
void jswrap_banglejs_accelWr(JsVarInt reg, JsVarInt data) {
unsigned char buf[2];
buf[0] = (unsigned char)reg;
buf[1] = (unsigned char)data;
i2cBusy = true;
jsi2cWrite(&internalI2C, ACCEL_ADDR, 2, buf, true);
i2cBusy = false;
}
/*JSON{
"type" : "staticmethod",
"class" : "Bangle",
"name" : "accelRd",
"generate" : "jswrap_banglejs_accelRd",
"params" : [
["reg","int",""]
],
"return" : ["int",""]
}
Reads a register from the KX023 Accelerometer
*/
int jswrap_banglejs_accelRd(JsVarInt reg) {
unsigned char buf[1];
buf[0] = (unsigned char)reg;
i2cBusy = true;
jsi2cWrite(&internalI2C, ACCEL_ADDR, 1, buf, true);
jsi2cRead(&internalI2C, ACCEL_ADDR, 1, buf, true);
i2cBusy = false;
return buf[0];
}
/*JSON{
"type" : "staticmethod",
"class" : "Bangle",
"name" : "getPressure",
"generate" : "jswrap_banglejs_getPressure",
"return" : ["JsVar","A promise that will be resolved with `{temperature, pressure, altitude}`"]
}
Read temperature, pressure and altitude data. A promise is returned
which will be resolved with `{temperature, pressure, altitude}`.
Conversions take roughly 100ms.
```
Bangle.getPressure().then(d=>{
console.log(d);
// {temperature, pressure, altitude}
});
```
*/
void jswrap_banglejs_getPressure_callback() {
JsVar *o = jsvNewObject();
if (o) {
i2cBusy = true;
unsigned char buf[6];
// ADC_CVT - 0b010 01 000 - pressure and temperature channel, OSR = 4096
buf[0] = 0x48; jsi2cWrite(&internalI2C, PRESSURE_ADDR, 1, buf, true);
// wait 100ms
jshDelayMicroseconds(100*1000); // we should really have a callback
// READ_PT
buf[0] = 0x10; jsi2cWrite(&internalI2C, PRESSURE_ADDR, 1, buf, true);
jsi2cRead(&internalI2C, PRESSURE_ADDR, 6, buf, true);
int temperature = (buf[0]<<16)|(buf[1]<<8)|buf[2];
if (temperature&0x800000) temperature-=0x1000000;
int pressure = (buf[3]<<16)|(buf[4]<<8)|buf[5];
jsvObjectSetChildAndUnLock(o,"temperature", jsvNewFromFloat(temperature/100.0));
jsvObjectSetChildAndUnLock(o,"pressure", jsvNewFromFloat(pressure/100.0));
buf[0] = 0x31; jsi2cWrite(&internalI2C, PRESSURE_ADDR, 1, buf, true); // READ_A
jsi2cRead(&internalI2C, PRESSURE_ADDR, 3, buf, true);
int altitude = (buf[0]<<16)|(buf[1]<<8)|buf[2];
if (altitude&0x800000) altitude-=0x1000000;
jsvObjectSetChildAndUnLock(o,"altitude", jsvNewFromFloat(altitude/100.0));
i2cBusy = false;
jspromise_resolve(promisePressure, o);
}
jsvUnLock2(promisePressure,o);
promisePressure = 0;
}
JsVar *jswrap_banglejs_getPressure() {
if (promisePressure) {
jsExceptionHere(JSET_ERROR, "Conversion in progress");
return 0;
}
promisePressure = jspromise_create();
if (!promisePressure) return 0;
jsiSetTimeout(jswrap_banglejs_getPressure_callback, 100);
return jsvLockAgain(promisePressure);
}
/*JSON{
"type" : "staticmethod",
"class" : "Bangle",
"name" : "off",
"generate" : "jswrap_banglejs_off"
}
Turn Bangle.js off. It can only be woken by pressing BTN1.
*/
void jswrap_banglejs_off() {
jsiKill();
jsvKill();
jshKill();
jshPinOutput(GPS_PIN_EN,0); // GPS off
jshPinOutput(VIBRATE_PIN,0); // vibrate off
jshPinOutput(LCD_BL,1); // backlight off
jshPinOutput(LED1_PININDEX,0); // LED off
lcdCmd_SPILCD(0x28, 0, NULL); // display off
nrf_gpio_cfg_sense_set(BTN2_PININDEX, NRF_GPIO_PIN_NOSENSE);
nrf_gpio_cfg_sense_set(BTN3_PININDEX, NRF_GPIO_PIN_NOSENSE);
nrf_gpio_cfg_sense_set(BTN1_PININDEX, NRF_GPIO_PIN_SENSE_LOW);
sd_power_system_off();
}
/*JSON{
"type" : "event",
"class" : "Bangle",
"name" : "accel",
"params" : [["xyz","JsVar",""]],
"ifdef" : "BANGLEJS"
}
Accelerometer data available with `{x,y,z,diff,mag}` object as a parameter
* `x` is X axis (left-right) in `g`
* `y` is Y axis (up-down) in `g`
* `z` is Z axis (in-out) in `g`
* `diff` is difference between this and the last reading in `g`
* `mag` is the magnitude of the acceleration in `g`
*/
/*JSON{
"type" : "event",
"class" : "Bangle",
"name" : "faceUp",
"params" : [["up","bool","`true` if face-up"]],
"ifdef" : "BANGLEJS"
}
Has the watch been moved so that it is face-up, or not face up?
*/
/*JSON{
"type" : "event",
"class" : "Bangle",
"name" : "lcdPower",
"params" : [["on","bool","`true` if screen is on"]],
"ifdef" : "BANGLEJS"
}
Has the screen been turned on or off? Can be used to stop tasks that are no longer useful if nothing is displayed.
*/
/*JSON{
"type" : "event",
"class" : "Bangle",
"name" : "faceUp",
"params" : [["data","JsVar","`{dir, double, x, y, z}`"]],
"ifdef" : "BANGLEJS"
}
If the watch is tapped, this event contains information on the way it was tapped.
`dir` reports the side of the watch that was tapped (not the direction it was tapped in).
```
{
dir : "left/right/top/bottom/front/back",
double : true/false // was this a double-tap?
x : -2 .. 2, // the axis of the tap
y : -2 .. 2, // the axis of the tap
z : -2 .. 2 // the axis of the tap
```
*/

View File

@ -0,0 +1,32 @@
/*
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
*
* Copyright (C) 2016 Gordon Williams <gw@pur3.co.uk>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* ----------------------------------------------------------------------------
* Contains JavaScript interface for Bangle.js (http://www.espruino.com/Bangle.js)
* ----------------------------------------------------------------------------
*/
#include "jspin.h"
void jswrap_banglejs_lcdWr(JsVarInt cmd, JsVar *data);
void jswrap_banglejs_setLCDPower(bool isOn);
void jswrap_banglejs_setLCDTimeout(JsVarFloat timeout);
void jswrap_banglejs_setLCDPalette(JsVar *palette);
bool jswrap_banglejs_isLCDOn();
void jswrap_banglejs_setGPSPower(bool isOn);
void jswrap_banglejs_accelWr(JsVarInt reg, JsVarInt data);
int jswrap_banglejs_accelRd(JsVarInt reg);
JsVar *jswrap_banglejs_getPressure();
void jswrap_banglejs_off();
void jswrap_banglejs_init();
void jswrap_banglejs_kill();
bool jswrap_banglejs_idle();
bool jswrap_banglejs_gps_character(char ch);

146
libs/misc/nmea.c Normal file
View File

@ -0,0 +1,146 @@
/*
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
*
* Copyright (C) 2019 Gordon Williams <gw@pur3.co.uk>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* ----------------------------------------------------------------------------
* NMEA decoder
* ----------------------------------------------------------------------------
*/
#include "nmea.h"
#include "jswrap_date.h"
char *nmea_next_comma(char *nmea) {
while (*nmea && *nmea!=',') nmea++; // find the comma
return nmea;
}
double nmea_decode_latlon(char *nmea, char *comma) {
if (*nmea==',') return NAN; // no reading
char *dp = nmea;
while (*dp && *dp!='.' && *dp!=',') dp++; // find decimal pt
*comma = 0;
double minutes = stringToFloat(&dp[-2]);
*comma = ',';
dp[-2] = 0;
int x = stringToInt(nmea);
return x+(minutes/60);
}
double nmea_decode_float(char *nmea, char *comma) {
*comma = 0;
double r = stringToFloat(nmea);
*comma = ',';
return r;
}
uint8_t nmea_decode_1(char *nmea) {
return chtod(nmea[0]);
}
uint8_t nmea_decode_2(char *nmea) {
return chtod(nmea[0])*10 + chtod(nmea[1]);
}
bool nmea_decode(NMEAFixInfo *gpsFix, const char *nmeaLine) {
char buf[NMEA_MAX_SIZE];
strcpy(buf, nmeaLine);
char *nmea = buf, *nextComma;
if (nmea[0]!='$' || nmea[1]!='G') return false; // not valid
if (nmea[3]=='R' && nmea[4]=='M' && nmea[5]=='C') {
// $GNRMC,161945.00,A,5139.11397,N,00116.07202,W,1.530,,190919,,,A*7E
nmea = nmea_next_comma(nmea)+1;
nextComma = nmea_next_comma(nmea);
// time
gpsFix->hour = nmea_decode_2(&nmea[0]);
gpsFix->min = nmea_decode_2(&nmea[2]);
gpsFix->sec = nmea_decode_2(&nmea[4]);
gpsFix->ms = nmea_decode_2(&nmea[7]);
// status
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);//?
// lat + NS
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// lon + EW
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// speed
gpsFix->speed = nmea_decode_float(nmea, nextComma);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// course
gpsFix->course = nmea_decode_float(nmea, nextComma);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// date
gpsFix->day = nmea_decode_2(&nmea[0]);
gpsFix->month = nmea_decode_2(&nmea[2]);
gpsFix->year = nmea_decode_2(&nmea[4]);
// ....
}
if (nmea[3]=='G' && nmea[4]=='G' && nmea[5]=='A') {
// $GNGGA,161945.00,5139.11397,N,00116.07202,W,1,06,1.29,71.1,M,47.0,M,,*64
nmea = nmea_next_comma(nmea)+1;
nextComma = nmea_next_comma(nmea);
// time
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// LAT
gpsFix->lat = nmea_decode_latlon(nmea, nextComma);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
if (*nmea=='S') gpsFix->lat=-gpsFix->lat;
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// LON
gpsFix->lon = nmea_decode_latlon(nmea, nextComma);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
if (*nmea=='W') gpsFix->lon=-gpsFix->lon;
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// quality
gpsFix->quality = nmea_decode_1(nmea);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// num satellites
gpsFix->satellites = nmea_decode_2(nmea);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// dilution of precision
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// altitude
gpsFix->alt = nmea_decode_float(nmea, nextComma);
nmea = nextComma+1; nextComma = nmea_next_comma(nmea);
// ....
}
if (nmea[3]=='G' && nmea[4]=='S' && nmea[5]=='V') {
// loads of cool data about what satellites we have
}
if (nmea[3]=='G' && nmea[4]=='L' && nmea[5]=='L') {
// Complete set of data received
return true;
}
return false;
}
JsVar *nmea_to_jsVar(NMEAFixInfo *gpsFix) {
JsVar *o = jsvNewObject();
if (o) {
jsvObjectSetChildAndUnLock(o, "lat", jsvNewFromFloat(gpsFix->lat));
jsvObjectSetChildAndUnLock(o, "lon", jsvNewFromFloat(gpsFix->lon));
jsvObjectSetChildAndUnLock(o, "alt", jsvNewFromFloat(gpsFix->alt));
jsvObjectSetChildAndUnLock(o, "speed", jsvNewFromFloat(gpsFix->speed));
jsvObjectSetChildAndUnLock(o, "course", jsvNewFromFloat(gpsFix->course));
CalendarDate date;
date.day = gpsFix->day;
date.month = gpsFix->month;
date.year = 2000+gpsFix->year;
TimeInDay td;
td.daysSinceEpoch = fromCalenderDate(&date);
td.hour = gpsFix->hour;
td.min = gpsFix->min;
td.sec = gpsFix->sec;
td.ms = gpsFix->ms;
td.zone = 0; // jsdGetTimeZone(); - no! GPS time is always in UTC :)
jsvObjectSetChildAndUnLock(o, "time", jswrap_date_from_milliseconds(fromTimeInDay(&td)));
jsvObjectSetChildAndUnLock(o, "satellites", jsvNewFromInteger(gpsFix->satellites));
jsvObjectSetChildAndUnLock(o, "fix", jsvNewFromInteger(gpsFix->quality));
}
return 0;
}

30
libs/misc/nmea.h Normal file
View File

@ -0,0 +1,30 @@
/*
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
*
* Copyright (C) 2019 Gordon Williams <gw@pur3.co.uk>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* ----------------------------------------------------------------------------
* NMEA decoder
* ----------------------------------------------------------------------------
*/
#include "jsvar.h"
typedef struct {
double lat,lon,alt;
double speed, course;
int hour,min,sec,ms;
uint8_t day,month,year;
uint8_t quality; // from GGA packet, 0 = no fix
uint8_t satellites; // how many satellites
} NMEAFixInfo;
#define NMEA_MAX_SIZE 82 // 82 is the max for NMEA
bool nmea_decode(NMEAFixInfo *gpsFix, const char *nmeaLine);
JsVar *nmea_to_jsVar(NMEAFixInfo *gpsFix);