Espruino/libs/joltjs/jswrap_jolt.c
Gordon Williams 1ca8483db9 On Jolt.js/Puck.js, add option to run the self-test the very first time the board boots up, default to off
2v26 and earlier full hex files would run the self-test at boot (DFU update builds don't)
2025-05-15 09:57:21 +01:00

698 lines
23 KiB
C

/*
* 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 Jolt.js
* ----------------------------------------------------------------------------
FIXME:
See https://docs.google.com/document/d/1Tw7fUkpj9dwBYASBCX9TjPcpgdqEw_7VL6MSRqVUUXY
*/
#include "jswrap_jolt.h"
#include "jsinteractive.h"
#include "jsdevices.h"
#include "jshardware.h"
#include "jsdevices.h"
#include "jspin.h"
#include "jsflags.h"
#include "jstimer.h"
#include "jswrap_bluetooth.h"
#include "nrf_gpio.h"
#include "nrf_delay.h"
#include "nrf5x_utils.h"
#include "ble_gap.h"
#include "jsflash.h" // for jsfRemoveCodeFromFlash
#include "bluetooth.h"
#include "app_timer.h"
/// IO pins on Qwiic headers - check for shorts
const Pin JOLT_IO_PINS[] = {
3,29,7, // q0
2,31,27, // q1
44,45,36,43, // q2
39,38,37,42 // q3
};
typedef enum {
JDM_AUTO, // Automatic (JDM_OUTPUT whenever a pin is set as output mode)
JDM_AUTO_ON, // Automatic (currently on)
JDM_OFF,
JDM_OUTPUT, // Independent bridge
JDM_MOTOR, // 4 pin
JDM_UNKNOWN // just used when parsing to show we don't know
} JoltDriverMode;
#define DRIVERCOUNT 2
JoltDriverMode driverMode[DRIVERCOUNT];
/*JSON{
"type": "class",
"class" : "Jolt",
"ifdef" : "JOLTJS"
}
Class containing utility functions for the [Jolt.js Smart Bluetooth driver](http://www.espruino.com/Jolt.js)
*/
void setJoltProperty(const char *name, JsVar* prop) {
JsVar *jolt = jsvObjectGetChildIfExists(execInfo.root, "Jolt");
if (!jolt) return;
jsvObjectSetChild(jolt, name, prop);
jsvUnLock(jolt);
}
/*JSON{
"type" : "variable",
"name" : "Q0",
"ifdef" : "JOLTJS",
"generate" : "jswrap_jolt_q0",
"return" : ["JsVar","An object containing the pins for the Q0 connector on Jolt.js `{sda,scl,fet}`"],
"return_object" : "Qwiic"
}
`Q0` and `Q1` Qwiic connectors can have their power controlled by a 500mA FET (`Q0.fet`) which switches GND.
The `sda` and `scl` pins on this port are also analog inputs - use `analogRead(Q0.sda)`/etc
To turn this connector on run `Q0.setPower(1)`
*/
/*JSON{
"type" : "staticproperty",
"class" : "Jolt",
"name" : "Q0",
"ifdef" : "JOLTJS",
"generate" : "jswrap_jolt_q0",
"return" : ["JsVar","An object containing the pins for the Q0 connector on Jolt.js `{sda,scl,fet}`"],
"return_object" : "Qwiic"
}
`Q0` and `Q1` Qwiic connectors can have their power controlled by a 500mA FET (`Jolt.Q0.fet`) which switches GND.
The `sda` and `scl` pins on this port are also analog inputs - use `analogRead(Jolt.Q0.sda)`/etc
To turn this connector on run `Jolt.Q0.setPower(1)`
*/
JsVar *jswrap_jolt_q0() {
JsVar *o = jspNewObject(0, "Qwiic");
if (!o) return 0;
jsvObjectSetChildAndUnLock(o, "sda", jsvNewFromPin(QWIIC0_PIN_SDA));
jsvObjectSetChildAndUnLock(o, "scl", jsvNewFromPin(QWIIC0_PIN_SCL));
jsvObjectSetChildAndUnLock(o, "fet", jsvNewFromPin(QWIIC0_PIN_FET));
setJoltProperty("Q0", o);
return o;
}
/*JSON{
"type" : "variable",
"name" : "Q1",
"ifdef" : "JOLTJS",
"generate" : "jswrap_jolt_q1",
"return" : ["JsVar","An object containing the pins for the Q1 connector on Jolt.js `{sda,scl,fet}`"],
"return_object" : "Qwiic"
}
`Q0` and `Q1` Qwiic connectors can have their power controlled by a 500mA FET (`Q1.fet`) which switches GND.
The `sda` and `scl` pins on this port are also analog inputs - use `analogRead(Q1.sda)`/etc
To turn this connector on run `Q1.setPower(1)`
*/
/*JSON{
"type" : "staticproperty",
"class" : "Jolt",
"name" : "Q1",
"ifdef" : "JOLTJS",
"generate" : "jswrap_jolt_q1",
"return" : ["JsVar","An object containing the pins for the Q1 connector on Jolt.js `{sda,scl,fet}`"],
"return_object" : "Qwiic"
}
`Q0` and `Q1` Qwiic connectors can have their power controlled by a 500mA FET (`Jolt.Q1.fet`) which switches GND.
The `sda` and `scl` pins on this port are also analog inputs - use `analogRead(Jolt.Q1.sda)`/etc
To turn this connector on run `Jolt.Q1.setPower(1)`
*/
JsVar *jswrap_jolt_q1() {
JsVar *o = jspNewObject(0, "Qwiic");
if (!o) return 0;
jsvObjectSetChildAndUnLock(o, "sda", jsvNewFromPin(QWIIC1_PIN_SDA));
jsvObjectSetChildAndUnLock(o, "scl", jsvNewFromPin(QWIIC1_PIN_SCL));
jsvObjectSetChildAndUnLock(o, "fet", jsvNewFromPin(QWIIC1_PIN_FET));
setJoltProperty("Q1", o);
return o;
}
/*JSON{
"type" : "variable",
"name" : "Q2",
"ifdef" : "JOLTJS",
"generate" : "jswrap_jolt_q2",
"return" : ["JsVar","An object containing the pins for the Q2 connector on Jolt.js `{sda,scl,gnd,vcc}`"],
"return_object" : "Qwiic"
}
`Q2` and `Q3` have all 4 pins connected to Jolt.js's GPIO (including those usually used for power).
As such only around 8mA of power can be supplied to any connected device.
To use this as a normal Qwiic connector, run `Q2.setPower(1)`
*/
/*JSON{
"type" : "staticproperty",
"class" : "Jolt",
"name" : "Q2",
"ifdef" : "JOLTJS",
"generate" : "jswrap_jolt_q2",
"return" : ["JsVar","An object containing the pins for the Q2 connector on Jolt.js `{sda,scl,gnd,vcc}`"],
"return_object" : "Qwiic"
}
`Q2` and `Q3` have all 4 pins connected to Jolt.js's GPIO (including those usually used for power).
As such only around 8mA of power can be supplied to any connected device.
To use this as a normal Qwiic connector, run `Jolt.Q2.setPower(1)`
*/
JsVar *jswrap_jolt_q2() {
JsVar *o = jspNewObject(0, "Qwiic");
if (!o) return 0;
jsvObjectSetChildAndUnLock(o, "sda", jsvNewFromPin(QWIIC2_PIN_SDA));
jsvObjectSetChildAndUnLock(o, "scl", jsvNewFromPin(QWIIC2_PIN_SCL));
jsvObjectSetChildAndUnLock(o, "gnd", jsvNewFromPin(QWIIC2_PIN_GND));
jsvObjectSetChildAndUnLock(o, "vcc", jsvNewFromPin(QWIIC2_PIN_VCC));
setJoltProperty("Q2", o);
return o;
}
/*JSON{
"type" : "variable",
"name" : "Q3",
"ifdef" : "JOLTJS",
"generate" : "jswrap_jolt_q3",
"return" : ["JsVar","An object containing the pins for the Q3 connector on Jolt.js `{sda,scl,gnd,vcc}`"],
"return_object" : "Qwiic"
}
`Q2` and `Q3` have all 4 pins connected to Jolt.js's GPIO (including those usually used for power).
As such only around 8mA of power can be supplied to any connected device.
To use this as a normal Qwiic connector, run `Q3.setPower(1)`
*/
/*JSON{
"type" : "staticproperty",
"class" : "Jolt",
"name" : "Q3",
"ifdef" : "JOLTJS",
"generate" : "jswrap_jolt_q3",
"return" : ["JsVar","An object containing the pins for the Q3 connector on Jolt.js `{sda,scl,gnd,vcc}`"],
"return_object" : "Qwiic"
}
`Q2` and `Q3` have all 4 pins connected to Jolt.js's GPIO (including those usually used for power).
As such only around 8mA of power can be supplied to any connected device.
To use this as a normal Qwiic connector, run `Jolt.Q3.setPower(1)`
*/
JsVar *jswrap_jolt_q3() {
JsVar *o = jspNewObject(0, "Qwiic");
if (!o) return 0;
jsvObjectSetChildAndUnLock(o, "sda", jsvNewFromPin(QWIIC3_PIN_SDA));
jsvObjectSetChildAndUnLock(o, "scl", jsvNewFromPin(QWIIC3_PIN_SCL));
jsvObjectSetChildAndUnLock(o, "gnd", jsvNewFromPin(QWIIC3_PIN_GND));
jsvObjectSetChildAndUnLock(o, "vcc", jsvNewFromPin(QWIIC3_PIN_VCC));
setJoltProperty("Q3", o);
return o;
}
/// Actually sets the driver mode
static void _jswrap_jolt_setDriverMode_int(int driver, JoltDriverMode dMode) {
if (driver==0) {
if (dMode == JDM_OUTPUT)
jshPinSetState(DRIVER0_PIN_MODE, JSHPINSTATE_GPIO_IN); // Z for independent bridge
else
jshPinOutput(DRIVER0_PIN_MODE, 0); // 0 for 4 pin interface
jshPinOutput(DRIVER0_PIN_NSLEEP, dMode != JDM_OFF);
} else if (driver==1) {
if (dMode == JDM_OUTPUT)
jshPinSetState(DRIVER1_PIN_MODE, JSHPINSTATE_GPIO_IN); // Z for independent bridge
else
jshPinOutput(DRIVER1_PIN_MODE, 0); // 0 for 4 pin interface
jshPinOutput(DRIVER1_PIN_NSLEEP, dMode != JDM_OFF);
} else assert(0);
}
/// Called when drivermode=auto to figure out when to turn the driver on
static void _jswrap_jolt_autoDriverMode(int driver) {
assert((driver>=0) && (driver<2));
// check the state of the 4 pins
bool pinIsOutput = false; // set if >0 pins are outputs
Pin pinBase = JSH_PORTH_OFFSET + driver*4;
for (int i=0;i<4;i++) {
uint32_t ipin = (uint32_t)pinInfo[pinBase + i].pin;
NRF_GPIO_Type *reg = nrf_gpio_pin_port_decode(&ipin);
if ((reg->PIN_CNF[ipin] & GPIO_PIN_CNF_DIR_Msk) ==
(GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos))
pinIsOutput = true;
}
// update driver state if different
bool isCurrentlyOn = driverMode[driver] == JDM_AUTO_ON;
if (pinIsOutput != isCurrentlyOn) {
//jsiConsolePrintf("autoDriverMode %d %d\n", driver, pinIsOutput);
if (pinIsOutput) driverMode[driver] = JDM_AUTO_ON;
else driverMode[driver] = JDM_AUTO;
_jswrap_jolt_setDriverMode_int(driver, pinIsOutput ? JDM_OUTPUT : JDM_OFF);
}
}
/*JSON{
"type" : "staticmethod",
"class" : "Jolt",
"name" : "setDriverMode",
"ifdef" : "JOLTJS",
"generate" : "jswrap_jolt_setDriverMode",
"params" : [
["driver","int","The number of the motor driver (0 or 1)"],
["mode","JsVar","The mode of the motor driver (see below)"]
]
}
Sets the mode of the motor drivers. Jolt.js has two motor drivers,
one (`0`) for outputs H0..H3, and one (`1`) for outputs H4..H7. They
can be controlled independently.
Mode can be:
* `undefined` / `false` / `"off"` - the motor driver is off, all motor driver pins are open circuit (the motor driver still has a ~2.5k pulldown to GND)
* `"auto"` - (default) - if any pin in the set of 4 pins (H0..H3, H4..H7) is set as an output, the driver is turned on. Eg `H0.set()` will
turn the driver on with a high output, `H0.reset()` will pull the output to GND and `H0.read()` (or `H0.mode("input")` to set the state explicitly) is needed to
turn the motor driver off.
* `true` / `"output"` - **[recommended]** driver is set to "Independent bridge" mode. All 4 outputs in the bank are enabled
* `"motor"` - driver is set to "4 pin interface" mode where pins are paired up (H0+H1, H2+H3, etc). If both
in a pair are 0 the output is open circuit (motor coast), if both are 1 both otputs are 0 (motor brake), and
if both are different, those values are on the output:
`output`/`auto` mode:
| H0 | H1 | Out 0 | Out 1 |
|----|----|-------|-------|
| 0 | 0 | Low | Low |
| 0 | 1 | Low | High |
| 1 | 0 | High | Low |
| 1 | 1 | High | High |
`motor` mode
| H0 | H1 | Out 0 | Out 1 |
|----|----|-------|-------|
| 0 | 0 | Open | Open |
| 0 | 1 | Low | High |
| 1 | 0 | High | Low |
| 1 | 1 | Low | Low |
*/
void jswrap_jolt_setDriverMode(int driver, JsVar *mode) {
if (driver<0 || driver>1) {
jsExceptionHere(JSET_ERROR, "Invalid driver %d", driver);
return;
}
JoltDriverMode dMode = JDM_UNKNOWN;
if (jsvIsBoolean(mode) || jsvIsUndefined(mode)) {
dMode = jsvGetBool(mode) ? JDM_OUTPUT : JDM_OFF;
} else if (jsvIsString(mode)) {
if (jsvIsStringEqual(mode, "off")) dMode = JDM_OFF;
else if (jsvIsStringEqual(mode, "auto")) dMode = JDM_AUTO;
else if (jsvIsStringEqual(mode, "output")) dMode = JDM_OUTPUT;
else if (jsvIsStringEqual(mode, "motor")) dMode = JDM_MOTOR;
}
if (dMode == JDM_UNKNOWN) {
jsExceptionHere(JSET_ERROR, "Unknown driver mode %q", mode);
return;
}
driverMode[driver] = dMode;
if (dMode==JDM_AUTO) {
_jswrap_jolt_setDriverMode_int(driver, JDM_OFF); // turn off
_jswrap_jolt_autoDriverMode(driver); // now let autoDriverMode update
} else
_jswrap_jolt_setDriverMode_int(driver, dMode);
}
static void jswrap_jolt_setDriverMode_(int driver, bool mode) {
JsVar *b = jsvNewFromBool(mode);
jswrap_jolt_setDriverMode(driver,b);
jsvUnLock(b);
}
static bool selftest_check_pin(Pin pin, char *err) {
unsigned int i;
char pinStr[4];
pinStr[0]='-';
itostr(pin,&pinStr[1],10);
bool ok = true;
jshPinSetState(pin, JSHPINSTATE_GPIO_OUT);
jshPinSetValue(pin, 1);
jshPinSetState(pin, JSHPINSTATE_GPIO_IN_PULLUP);
if (!jshPinGetValue(pin)) {
pinStr[0]='l';
if (!err[0]) strcpy(err,pinStr);
jsiConsolePrintf("Pin %p forced low\n", pin);
ok = false;
}
for (i=0;i<sizeof(JOLT_IO_PINS)/sizeof(Pin);i++)
if (JOLT_IO_PINS[i]!=pin)
jshPinOutput(JOLT_IO_PINS[i], 0);
if (!jshPinGetValue(pin)) {
pinStr[0]='L';
if (!err[0]) strcpy(err,pinStr);
jsiConsolePrintf("Pin %p shorted low\n", pin);
ok = false;
}
jshPinSetState(pin, JSHPINSTATE_GPIO_OUT);
jshPinSetValue(pin, 0);
jshPinSetState(pin, JSHPINSTATE_GPIO_IN_PULLDOWN);
if (jshPinGetValue(pin)) {
pinStr[0]='h';
if (!err[0]) strcpy(err,pinStr);
jsiConsolePrintf("Pin %p forced high\n", pin);
ok = false;
}
for (i=0;i<sizeof(JOLT_IO_PINS)/sizeof(Pin);i++)
if (JOLT_IO_PINS[i]!=pin)
jshPinOutput(JOLT_IO_PINS[i], 1);
if (jshPinGetValue(pin)) {
pinStr[0]='H';
if (!err[0]) strcpy(err,pinStr);
jsiConsolePrintf("Pin %p shorted high\n", pin);
ok = false;
}
jshPinSetState(pin, JSHPINSTATE_GPIO_IN);
return ok;
}
/*JSON{
"type" : "staticmethod",
"class" : "Jolt",
"name" : "selfTest",
"ifdef" : "JOLTJS",
"generate" : "jswrap_jolt_selfTest",
"return" : ["bool", "True if the self-test passed" ]
}
Run a self-test, and return true for a pass. This checks for shorts between
pins, so your Jolt shouldn't have anything connected to it.
**Note:** This self-test auto starts if you hold the button on your Jolt down
while inserting the battery, leave it pressed for 3 seconds (while the green LED
is lit) and release it soon after all LEDs turn on. 5 red blinks is a fail, 5
green is a pass.
If the self test fails, it'll set the Jolt.js Bluetooth advertising name to
`Jolt.js !ERR` where ERR is a 3 letter error code.
*/
bool _jswrap_jolt_selfTest(bool advertisePassOrFail) {
unsigned int timeout;
JsVarFloat v;
bool ok = true;
char err[4] = {0,0,0,0};
// light up all LEDs white
jshPinOutput(LED1_PININDEX, LED1_ONSTATE);
jshPinOutput(LED2_PININDEX, LED2_ONSTATE);
jshPinOutput(LED3_PININDEX, LED3_ONSTATE);
jshPinSetState(BTN1_PININDEX, BTN1_PINSTATE);
timeout = 2000;
while (jshPinGetValue(BTN1_PININDEX)==BTN1_ONSTATE && timeout--)
nrf_delay_ms(1);
if (jshPinGetValue(BTN1_PININDEX)==BTN1_ONSTATE) {
jsiConsolePrintf("Timeout waiting for button to be released.\n");
if (!err[0]) strcpy(err,"BTN");
ok = false;
}
nrf_delay_ms(100);
jshPinInput(LED1_PININDEX);
jshPinInput(LED2_PININDEX);
jshPinInput(LED3_PININDEX);
nrf_delay_ms(500);
v = jshReadVRef();
if (v<3.2 || v>3.4) {
if (!err[0]) strcpy(err,"VRG");
jsiConsolePrintf("VREG out of range (%fv)\n", v);
ok = false;
}
jshPinSetState(LED1_PININDEX, JSHPINSTATE_GPIO_IN_PULLUP);
nrf_delay_ms(1);
v = jshPinAnalog(LED1_PININDEX);
jshPinSetState(LED1_PININDEX, JSHPINSTATE_GPIO_IN);
if (v<0.2 || v>0.65) {
if (!err[0]) strcpy(err,"LD1");
jsiConsolePrintf("LED1 pullup voltage out of range (%f) - disconnected?\n", v);
ok = false;
}
jshPinSetState(LED2_PININDEX, JSHPINSTATE_GPIO_IN_PULLUP);
nrf_delay_ms(1);
v = jshPinAnalog(LED2_PININDEX);
jshPinSetState(LED2_PININDEX, JSHPINSTATE_GPIO_IN);
if (v<0.55 || v>0.85) {
if (!err[0]) strcpy(err,"LD2");
jsiConsolePrintf("LED2 pullup voltage out of range (%f) - disconnected?\n", v);
ok = false;
}
jshPinSetState(LED3_PININDEX, JSHPINSTATE_GPIO_IN_PULLUP);
nrf_delay_ms(1);
v = jshPinAnalog(LED3_PININDEX);
jshPinSetState(LED3_PININDEX, JSHPINSTATE_GPIO_IN);
if (v<0.55 || v>0.90) {
if (!err[0]) strcpy(err,"LD3");
jsiConsolePrintf("LED3 pullup voltage out of range (%f) - disconnected?\n", v);
ok = false;
}
jswrap_jolt_setDriverMode_(0,true);
jswrap_jolt_setDriverMode_(1,true);
// test every pin on the motor driver one at a time
for (int i=0;i<8;i++) {
for (int p=0;p<8;p++) {
jshPinSetState(JSH_PORTH_OFFSET+p, JSHPINSTATE_GPIO_OUT);
jshPinSetValue(JSH_PORTH_OFFSET+p, p == i);
}
nrf_delay_ms(5);
// we can only read H0/2/4/8
for (int p=0;p<8;p+=2) {
v = jshPinAnalog(JSH_PORTH_OFFSET+p);
if (i==p) {
if (v<2) {
if (!err[0]) { strcpy(err,"OLx"); err[2]='0'+i; }
jsiConsolePrintf("H%d low (%f) when H%d set\n", p, v, i);
ok = false;
}
} else {
if (v>1) {
if (!err[0]) { strcpy(err,"OHx"); err[2]='0'+i; }
jsiConsolePrintf("H%d high (%f) when H%d set\n", p, v, i);
ok = false;
}
}
}
}
// all drivers off
for (int p=0;p<8;p++)
jshPinSetValue(JSH_PORTH_OFFSET+p, 0);
jswrap_jolt_setDriverMode_(0,false);
jswrap_jolt_setDriverMode_(1,false);
// TODO: what about checking motor drivers if there's a short using nFault pins?
for (unsigned int i=0;i<sizeof(JOLT_IO_PINS)/sizeof(Pin);i++)
selftest_check_pin(JOLT_IO_PINS[i], err);
for (unsigned int i=0;i<sizeof(JOLT_IO_PINS)/sizeof(Pin);i++)
jshPinSetState(JOLT_IO_PINS[i], JSHPINSTATE_GPIO_IN);
if (err[0] || advertisePassOrFail) {
char deviceName[BLE_GAP_DEVNAME_MAX_LEN];
if (advertisePassOrFail) {
if (err[0]) {
strcpy(deviceName,"FAIL ");
strcat(deviceName,err);
} else {
strcpy(deviceName,"PASS");
}
} else {
strcpy(deviceName,"Jolt.js !");
strcat(deviceName,err);
}
ble_gap_conn_sec_mode_t sec_mode;
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
sd_ble_gap_device_name_set(&sec_mode,
(const uint8_t *)deviceName,
strlen(deviceName));
jsiConsolePrintf("Error code %s\n",err);
}
// ensure we put motor driver back to sleep
jswrap_jolt_hwinit();
return ok;
}
bool jswrap_jolt_selfTest() {
return _jswrap_jolt_selfTest(false);
}
/*JSON{
"type" : "hwinit",
"generate" : "jswrap_jolt_hwinit"
}*/
void jswrap_jolt_hwinit() {
// ensure motor drivers are off
jshPinOutput(DRIVER0_PIN_NSLEEP, 0);
jshPinOutput(DRIVER1_PIN_NSLEEP, 0);
jshPinOutput(DRIVER0_PIN_MODE, 0);
jshPinOutput(DRIVER1_PIN_MODE, 0);
jshPinOutput(DRIVER0_PIN_TRQ, 0);
jshPinOutput(DRIVER1_PIN_TRQ, 0);
jshPinSetState(DRIVER0_PIN_NFAULT, JSHPINSTATE_GPIO_IN_PULLUP);
jshPinSetState(DRIVER1_PIN_NFAULT, JSHPINSTATE_GPIO_IN_PULLUP);
driverMode[0] = JDM_AUTO;
driverMode[1] = JDM_AUTO;
// set all outputs to 0 by default
Pin driverPins[] = {
DRIVER0_PIN_D0,
DRIVER0_PIN_D1,
DRIVER0_PIN_D2,
DRIVER0_PIN_D3,
DRIVER1_PIN_D0,
DRIVER1_PIN_D1,
DRIVER1_PIN_D2,
DRIVER1_PIN_D3
};
for (int i=0;i<8;i++) {
Pin pin = driverPins[i];
jshPinSetState(pin, JSHPINSTATE_GPIO_IN);
jshSetPinStateIsManual(pin, false);
}
Pin analogPins[] = {
DRIVER0_PIN_D0_ANALOG,
DRIVER0_PIN_D2_ANALOG,
DRIVER1_PIN_D0_ANALOG,
DRIVER1_PIN_D2_ANALOG
};
for (int i=0;i<4;i++) {
Pin pin = analogPins[i];
jshPinSetState(pin, JSHPINSTATE_ADC_IN);
}
}
/*JSON{
"type" : "init",
"generate" : "jswrap_jolt_init"
}*/
void jswrap_jolt_init() {
/* If the button is pressed during reset, perform a self test.
* With bootloader this means apply power while holding button for >3 secs */
bool firstStart = jsiStatus & JSIS_FIRST_BOOT; // is this the first time jswrap_jolt_init was called?
/** If we're NOT doing a DFU update build (eg we're making a full a hex file)
then ensure that the firmware keeps doing a self test until it passes (eg it'll
output 5 red flashes for fail or 5 green for pass). If we're a firmware update
package don't do that as the device might be connected to something which will
make the test keep failing. */
bool firstRunAfterFlash = false;
#ifdef ESPR_TEST_ON_FIRST_RUN
uint32_t firstStartFlagAddr = FLASH_SAVED_CODE_START-4;
if (firstStart) {
// check the 4 bytes *right before* our saved code. If these are 0xFFFFFFFF
// then we have just been programmed...
uint32_t buf;
jshFlashRead(&buf, firstStartFlagAddr, 4);
if (buf==0xFFFFFFFF) {
firstRunAfterFlash = true;
}
}
#endif
if (firstStart && (jshPinGetValue(BTN1_PININDEX) == BTN1_ONSTATE || firstRunAfterFlash)) {
// don't do it during a software reset - only first hardware reset
// if we're doing our first run after being flashed with new firmware, we set the advertising name
// up to say PASS or FAIL, to work with the factory test process.
bool result = _jswrap_jolt_selfTest(firstRunAfterFlash);
#ifdef ESPR_TEST_ON_FIRST_RUN
// if we passed, set the flag in flash so we don't self-test again
if (firstRunAfterFlash && result) {
uint32_t buf = 0;
bool oldFlashStatus = jsfGetFlag(JSF_UNSAFE_FLASH);
jsfSetFlag(JSF_UNSAFE_FLASH, true);
jshFlashWrite(&buf, firstStartFlagAddr, 4);
jsfSetFlag(JSF_UNSAFE_FLASH, oldFlashStatus);
}
#endif
// green if good, red if bad
Pin indicator = result ? LED2_PININDEX : LED1_PININDEX;
int i;
for (i=0;i<5;i++) {
jshPinOutput(indicator, LED1_ONSTATE);
nrf_delay_ms(500);
jshPinOutput(indicator, !LED1_ONSTATE);
nrf_delay_ms(500);
}
jshPinInput(indicator);
// If the button is *still* pressed, remove all code from flash memory too!
if (jshPinGetValue(BTN1_PININDEX) == BTN1_ONSTATE) {
jsfRemoveCodeFromFlash();
}
}
}
/*JSON{
"type" : "kill",
"generate" : "jswrap_jolt_kill"
}*/
void jswrap_jolt_kill() {
// ensure motor drivers are off
jshPinOutput(DRIVER0_PIN_NSLEEP, 0);
jshPinOutput(DRIVER1_PIN_NSLEEP, 0);
}
/*JSON{
"type" : "idle",
"generate" : "jswrap_jolt_idle"
}*/
bool jswrap_jolt_idle() {
bool busy = false;
// handle automatic driver mode
for (int i=0;i<DRIVERCOUNT;i++) {
if (driverMode[i]==JDM_AUTO || driverMode[i]==JDM_AUTO_ON)
_jswrap_jolt_autoDriverMode(i);
}
return busy;
}
/*JSON{
"type" : "powerusage",
"generate" : "jswrap_jolt_powerusage"
}*/
void jswrap_jolt_powerusage(JsVar *devices) {
if (!(driverMode[0]==JDM_OFF || driverMode[0]==JDM_AUTO))
jsvObjectSetChildAndUnLock(devices, "driver0", jsvNewFromInteger(1000));
if (!(driverMode[1]==JDM_OFF || driverMode[1]==JDM_AUTO))
jsvObjectSetChildAndUnLock(devices, "driver1", jsvNewFromInteger(1000));
int v;
v = (int)(jshPinAnalog(JSH_PORTH_OFFSET+0) * 400);
if (v>10) jsvObjectSetChildAndUnLock(devices, "pin0_internal_resistance", jsvNewFromInteger(v));
v = (int)(jshPinAnalog(JSH_PORTH_OFFSET+2) * 400);
if (v>10) jsvObjectSetChildAndUnLock(devices, "pin2_internal_resistance", jsvNewFromInteger(v));
v = (int)(jshPinAnalog(JSH_PORTH_OFFSET+4) * 400);
if (v>10) jsvObjectSetChildAndUnLock(devices, "pin4_internal_resistance", jsvNewFromInteger(v));
v = (int)(jshPinAnalog(JSH_PORTH_OFFSET+6) * 400);
if (v>10) jsvObjectSetChildAndUnLock(devices, "pin6_internal_resistance", jsvNewFromInteger(v));
}