Merge branch 'post1' into nimble

# Conflicts:
#	docs/software/TODO.md
#	docs/software/nrf52-TODO.md
#	platformio.ini
#	src/esp32/MeshBluetoothService.cpp
This commit is contained in:
geeksville
2020-07-21 11:20:09 -07:00
44 changed files with 16941 additions and 345 deletions
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include <Arduino.h>
/**
* Common lib functions for all platforms that have bluetooth
*/
#define MESH_SERVICE_UUID "6ba1b218-15a8-461f-9fa8-5dcae273eafd"
#define TORADIO_UUID "f75c76d2-129e-4dad-a1dd-7866124401e7"
#define FROMRADIO_UUID "8ba2bcc2-ee02-4a55-a531-c525c5e454d5"
#define FROMNUM_UUID "ed9da18c-a800-4f66-a670-aa7547e34453"
/// Given a level between 0-100, update the BLE attribute
void updateBatteryLevel(uint8_t level);
+30 -1
View File
@@ -1,7 +1,13 @@
#include "Thread.h"
#include "timing.h"
#include <assert.h>
namespace concurrency {
#ifdef ARDUINO_ARCH_ESP32
#include "esp_task_wdt.h"
#endif
namespace concurrency
{
void Thread::start(const char *name, size_t stackSize, uint32_t priority)
{
@@ -14,4 +20,27 @@ void Thread::callRun(void *_this)
((Thread *)_this)->doRun();
}
void Thread::serviceWatchdog()
{
#ifdef ARDUINO_ARCH_ESP32
esp_task_wdt_reset();
#endif
}
void Thread::startWatchdog()
{
#ifdef ARDUINO_ARCH_ESP32
auto r = esp_task_wdt_add(taskHandle);
assert(r == ESP_OK);
#endif
}
void Thread::stopWatchdog()
{
#ifdef ARDUINO_ARCH_ESP32
auto r = esp_task_wdt_delete(taskHandle);
assert(r == ESP_OK);
#endif
}
} // namespace concurrency
+3 -12
View File
@@ -1,7 +1,6 @@
#pragma once
#include "freertosinc.h"
#include "esp_task_wdt.h"
namespace concurrency {
@@ -36,17 +35,9 @@ class Thread
*
* this only applies after startWatchdog() has been called. If you need to sleep for a long time call stopWatchdog()
*/
void serviceWatchdog() { esp_task_wdt_reset(); }
void startWatchdog()
{
auto r = esp_task_wdt_add(taskHandle);
assert(r == ESP_OK);
}
void stopWatchdog()
{
auto r = esp_task_wdt_delete(taskHandle);
assert(r == ESP_OK);
}
void serviceWatchdog();
void startWatchdog();
void stopWatchdog();
private:
static void callRun(void *_this);
+65 -38
View File
@@ -55,13 +55,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/// Convert a preprocessor name into a quoted string and if that string is empty use "unset"
#define optstr(s) (xstr(s)[0] ? xstr(s) : "unset")
#ifdef NRF52840_XXAA // All of the NRF52 targets are configured using variant.h, so this section shouldn't need to be
// board specific
#ifdef NRF52_SERIES // All of the NRF52 targets are configured using variant.h, so this section shouldn't need to be
// board specific
//
// Standard definitions for NRF52 targets
//
// Nop definition for these attributes - not used on NRF52
#define EXT_RAM_ATTR
#define IRAM_ATTR
#define NO_ESP32 // Don't use ESP32 libs (mainly bluetooth)
// We bind to the GPS using variant.h instead for this platform (Serial1)
@@ -70,7 +74,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define RTC_DATA_ATTR
#define LED_PIN PIN_LED1 // LED1 on nrf52840-DK
// If the variant filed defines as standard button
#ifdef PIN_BUTTON1
#define BUTTON_PIN PIN_BUTTON1
#endif
// FIXME, use variant.h defs for all of this!!! (even on the ESP32 targets)
#elif defined(CubeCell_BoardPlus)
@@ -105,10 +113,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// LoRa SPI
// -----------------------------------------------------------------------------
#define SCK_GPIO 5
#define MISO_GPIO 19
#define MOSI_GPIO 27
#define NSS_GPIO 18
// NRF52 boards will define this in variant.h
#ifndef RF95_SCK
#define RF95_SCK 5
#define RF95_MISO 19
#define RF95_MOSI 27
#define RF95_NSS 18
#endif
#endif
@@ -148,11 +159,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define BUTTON_PIN_ALT 13 // Alternate GPIO for an external button if needed
#ifndef USE_JTAG
#define RESET_GPIO 14
#define RF95_RESET 14
#endif
#define RF95_IRQ_GPIO 26
#define DIO1_GPIO 33 // Note: not really used on this board
#define DIO2_GPIO 32 // Note: not really used on this board
#define RF95_IRQ 26
#define RF95_DIO1 33 // Note: not really used on this board
#define RF95_DIO2 32 // Note: not really used on this board
// Leave undefined to disable our PMU IRQ handler
#define PMU_IRQ 35
@@ -171,11 +182,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define BUTTON_PIN 39
#ifndef USE_JTAG
#define RESET_GPIO 23
#define RF95_RESET 23
#endif
#define RF95_IRQ_GPIO 26
#define DIO1_GPIO 33 // Note: not really used on this board
#define DIO2_GPIO 32 // Note: not really used on this board
#define RF95_IRQ 26
#define RF95_DIO1 33 // Note: not really used on this board
#define RF95_DIO2 32 // Note: not really used on this board
// This board has different GPS pins than all other boards
#undef GPS_RX_PIN
@@ -206,11 +217,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define BUTTON_PIN 0 // If defined, this will be used for user button presses
#ifndef USE_JTAG
#define RESET_GPIO 14 // If defined, this pin will be used to reset the LORA radio
#define RF95_RESET 14 // If defined, this pin will be used to reset the LORA radio
#endif
#define RF95_IRQ_GPIO 26
#define DIO1_GPIO 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#define DIO2_GPIO 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#define RF95_IRQ 26
#define RF95_DIO1 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#define RF95_DIO2 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#elif defined(TLORA_V1)
// This string must exactly match the case used in release file names or the android updater won't work
#define HW_VENDOR "tlora-v1"
@@ -228,10 +239,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define LED_PIN 2 // If defined we will blink this LED
#define BUTTON_PIN 0 // If defined, this will be used for user button presses
#define RESET_GPIO 14 // If defined, this pin will be used to reset the LORA radio
#define RF95_IRQ_GPIO 26 // IRQ line for the LORA radio
#define DIO1_GPIO 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#define DIO2_GPIO 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#define RF95_RESET 14 // If defined, this pin will be used to reset the LORA radio
#define RF95_IRQ 26 // IRQ line for the LORA radio
#define RF95_DIO1 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#define RF95_DIO2 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#elif defined(TLORA_V2)
// This string must exactly match the case used in release file names or the android updater won't work
#define HW_VENDOR "tlora-v2"
@@ -248,7 +260,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost
#define LED_PIN 25 // If defined we will blink this LED
#define BUTTON_PIN 0 // If defined, this will be used for user button presses, if your board doesn't have a physical switch, you can wire one between this pin and ground
#define BUTTON_PIN \
0 // If defined, this will be used for user button presses, if your board doesn't have a physical switch, you can wire one
// between this pin and ground
#define RESET_GPIO 14 // If defined, this pin will be used to reset the LORA radio
#define RF95_IRQ_GPIO 26 // IRQ line for the LORA radio
@@ -262,7 +276,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#undef GPS_RX_PIN
#undef GPS_TX_PIN
#define GPS_RX_PIN 36
#define GPS_TX_PIN 39
#define GPS_TX_PIN 39
#define I2C_SDA 21 // I2C pins for this board
#define I2C_SCL 22
@@ -271,12 +285,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost
#define LED_PIN 25 // If defined we will blink this LED
#define BUTTON_PIN 12 // If defined, this will be used for user button presses, if your board doesn't have a physical switch, you can wire one between this pin and ground
#define BUTTON_PIN \
12 // If defined, this will be used for user button presses, if your board doesn't have a physical switch, you can wire one
// between this pin and ground
#define RESET_GPIO 23 // If defined, this pin will be used to reset the LORA radio
#define RF95_IRQ_GPIO 26 // IRQ line for the LORA radio
#define DIO1_GPIO 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#define DIO2_GPIO 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#define RF95_RESET 14 // If defined, this pin will be used to reset the LORA radio
#define RF95_IRQ 26 // IRQ line for the LORA radio
#define RF95_DIO1 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#define RF95_DIO2 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#endif
#ifdef ARDUINO_NRF52840_PCA10056
@@ -288,25 +304,25 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#undef LED_INVERTED
#define LED_INVERTED 1
// Uncomment to confirm if we can build the RF95 driver for NRF52
#if 0
#define RESET_GPIO 14 // If defined, this pin will be used to reset the LORA radio
#define RF95_IRQ_GPIO 26 // IRQ line for the LORA radio
#define DIO1_GPIO 35 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#define DIO2_GPIO 34 // DIO1 & DIO2 are not currently used, but they must be assigned to a pin number
#endif
#elif defined(ARDUINO_NRF52840_PPR)
#define HW_VENDOR "ppr"
#elif NRF52_SERIES
#define HW_VENDOR "nrf52unknown" // FIXME - unknown nrf52 board
#endif
// -----------------------------------------------------------------------------
// DEBUG
// -----------------------------------------------------------------------------
#ifdef CONSOLE_MAX_BAUD
#define SERIAL_BAUD CONSOLE_MAX_BAUD
#else
#define SERIAL_BAUD 921600 // Serial debug baud rate
#endif
#include "SerialConsole.h"
@@ -314,13 +330,24 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// What platforms should use SEGGER?
#ifdef NRF52_SERIES
// Always include the SEGGER code on NRF52 - because useful for debugging
#include "SEGGER_RTT.h"
// Debug printing to segger console
#define SEGGER_MSG(...) SEGGER_RTT_printf(0, __VA_ARGS__)
// nrf52 gets its settings via variant files
#ifndef PIN_SERIAL_RX
// No serial ports on this board - ONLY use segger in memory console
#define USE_SEGGER
#endif
#else
#define SERIAL0_RX_GPIO 3 // Always GPIO3 on ESP32
#endif
#ifdef USE_SEGGER
#include "SEGGER_RTT.h"
#define DEBUG_MSG(...) SEGGER_RTT_printf(0, __VA_ARGS__)
#else
#ifdef DEBUG_PORT
+8 -14
View File
@@ -2,6 +2,7 @@
#include <Arduino.h>
#include <assert.h>
#include "BluetoothCommon.h"
#include "GPS.h"
#include "MeshService.h"
#include "NodeDB.h"
@@ -43,26 +44,20 @@ class BluetoothPhoneAPI : public PhoneAPI
}
};
BluetoothPhoneAPI *bluetoothPhoneAPI;
static BluetoothPhoneAPI *bluetoothPhoneAPI;
class ToRadioCharacteristic : public CallbackCharacteristic
{
public:
ToRadioCharacteristic() : CallbackCharacteristic("f75c76d2-129e-4dad-a1dd-7866124401e7", BLECharacteristic::PROPERTY_WRITE) {}
ToRadioCharacteristic() : CallbackCharacteristic(TORADIO_UUID, BLECharacteristic::PROPERTY_WRITE) {}
void onWrite(BLECharacteristic *c)
{
bluetoothPhoneAPI->handleToRadio(c->getData(), c->getValue().length());
}
void onWrite(BLECharacteristic *c) { bluetoothPhoneAPI->handleToRadio(c->getData(), c->getValue().length()); }
};
class FromRadioCharacteristic : public CallbackCharacteristic
{
public:
FromRadioCharacteristic() : CallbackCharacteristic("8ba2bcc2-ee02-4a55-a531-c525c5e454d5", BLECharacteristic::PROPERTY_READ)
{
}
FromRadioCharacteristic() : CallbackCharacteristic(FROMRADIO_UUID, BLECharacteristic::PROPERTY_READ) {}
void onRead(BLECharacteristic *c)
{
@@ -82,9 +77,8 @@ class FromNumCharacteristic : public CallbackCharacteristic
{
public:
FromNumCharacteristic()
: CallbackCharacteristic("ed9da18c-a800-4f66-a670-aa7547e34453", BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_NOTIFY)
: CallbackCharacteristic(FROMNUM_UUID, BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_NOTIFY)
{
// observe(&service.fromNumChanged);
}
@@ -104,7 +98,7 @@ BLEService *createMeshBluetoothService(BLEServer *server)
}
// Create the BLE Service, we need more than the default of 15 handles
BLEService *service = server->createService(BLEUUID("6ba1b218-15a8-461f-9fa8-5dcae273eafd"), 30, 0);
BLEService *service = server->createService(BLEUUID(MESH_SERVICE_UUID), 30, 0);
assert(!meshFromNumCharacteristic);
meshFromNumCharacteristic = new FromNumCharacteristic;
+57
View File
@@ -0,0 +1,57 @@
#include "WiFiServerAPI.h"
#include "PowerFSM.h"
#include "configuration.h"
#include <Arduino.h>
WiFiServerAPI::WiFiServerAPI(WiFiClient &_client) : StreamAPI(&client), client(_client)
{
DEBUG_MSG("Incoming connection from %s\n", client.remoteIP().toString().c_str());
}
WiFiServerAPI::~WiFiServerAPI()
{
client.stop();
// FIXME - delete this if the client dropps the connection!
}
/// Hookable to find out when connection changes
void WiFiServerAPI::onConnectionChanged(bool connected)
{
// FIXME - we really should be doing global reference counting to see if anyone is currently using serial or wifi and if so,
// block sleep
if (connected) { // To prevent user confusion, turn off bluetooth while using the serial port api
powerFSM.trigger(EVENT_SERIAL_CONNECTED);
} else {
powerFSM.trigger(EVENT_SERIAL_DISCONNECTED);
}
}
void WiFiServerAPI::loop()
{
if (client.connected()) {
StreamAPI::loop();
} else {
DEBUG_MSG("Client dropped connection, closing UDP server\n");
delete this;
}
}
#define MESHTASTIC_PORTNUM 4403
WiFiServerPort::WiFiServerPort() : WiFiServer(MESHTASTIC_PORTNUM) {}
void WiFiServerPort::init()
{
DEBUG_MSG("Listening on TCP port %d\n", MESHTASTIC_PORTNUM);
begin();
}
void WiFiServerPort::loop()
{
auto client = available();
if (client) {
new WiFiServerAPI(client);
}
}
+38
View File
@@ -0,0 +1,38 @@
#pragma once
#include "StreamAPI.h"
#include <WiFi.h>
/**
* Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs
* (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs).
*/
class WiFiServerAPI : public StreamAPI
{
private:
WiFiClient client;
public:
WiFiServerAPI(WiFiClient &_client);
virtual ~WiFiServerAPI();
virtual void loop(); // Check for dropped client connections
protected:
/// Hookable to find out when connection changes
virtual void onConnectionChanged(bool connected);
};
/**
* Listens for incoming connections and does accepts and creates instances of WiFiServerAPI as needed
*/
class WiFiServerPort : public WiFiServer
{
public:
WiFiServerPort();
void init();
void loop();
};
+13 -4
View File
@@ -5,12 +5,21 @@
#include <assert.h>
#include <time.h>
// If we have a serial GPS port it will not be null
#ifdef GPS_RX_PIN
HardwareSerial _serial_gps_real(GPS_SERIAL_NUM);
HardwareSerial &GPS::_serial_gps = _serial_gps_real;
HardwareSerial *GPS::_serial_gps = &_serial_gps_real;
#elif defined(NRF52840_XXAA)
// Assume NRF52840
HardwareSerial *GPS::_serial_gps = &Serial1;
#else
// Assume NRF52
HardwareSerial &GPS::_serial_gps = Serial1;
HardwareSerial *GPS::_serial_gps = NULL;
#endif
#ifdef GPS_I2C_ADDRESS
uint8_t GPS::i2cAddress = GPS_I2C_ADDRESS;
#else
uint8_t GPS::i2cAddress = 0;
#endif
bool timeSetFromGPS; // We try to set our time from GPS each time we wake from sleep
@@ -63,7 +72,7 @@ void perhapsSetRTC(struct tm &t)
// DEBUG_MSG("Got time from GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec);
if (t.tm_year < 0 || t.tm_year >= 300)
DEBUG_MSG("Ignoring invalid GPS time\n");
DEBUG_MSG("Ignoring invalid GPS month=%d, year=%d, unixtime=%ld\n", t.tm_mon, t.tm_year, tv.tv_sec);
else
perhapsSetRTC(&tv);
}
+10 -5
View File
@@ -1,8 +1,8 @@
#pragma once
#include "Observer.h"
#include "GPSStatus.h"
#include "../concurrency/PeriodicTask.h"
#include "GPSStatus.h"
#include "Observer.h"
#include "sys/time.h"
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
@@ -30,12 +30,17 @@ class GPS : public Observable<void *>
protected:
bool hasValidLocation = false; // default to false, until we complete our first read
static HardwareSerial &_serial_gps;
public:
/** If !NULL we will use this serial port to construct our GPS */
static HardwareSerial *_serial_gps;
/** If !0 we will attempt to connect to the GPS over I2C */
static uint8_t i2cAddress;
int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double
int32_t altitude = 0;
uint32_t dop = 0; // Diminution of position; PDOP where possible (UBlox), HDOP otherwise (TinyGPS) in 10^2 units (needs scaling before use)
uint32_t dop = 0; // Diminution of position; PDOP where possible (UBlox), HDOP otherwise (TinyGPS) in 10^2 units (needs
// scaling before use)
uint32_t heading = 0; // Heading of motion, in degrees * 10^-5
uint32_t numSatellites = 0;
+2 -3
View File
@@ -13,9 +13,8 @@ static int32_t toDegInt(RawDegrees d)
void NEMAGPS::loop()
{
while (_serial_gps.available() > 0) {
int c = _serial_gps.read();
while (_serial_gps->available() > 0) {
int c = _serial_gps->read();
// Serial.write(c);
reader.encode(c);
}
+62 -28
View File
@@ -7,23 +7,46 @@ UBloxGPS::UBloxGPS() : concurrency::PeriodicTask()
notifySleepObserver.observe(&notifySleep);
}
bool UBloxGPS::tryConnect()
{
isConnected = false;
if (_serial_gps)
isConnected = ublox.begin(*_serial_gps);
if (!isConnected && i2cAddress) {
extern bool neo6M; // Super skanky - if we are talking to the device i2c we assume it is a neo7 on a RAK815, which
// supports the newer API
neo6M = true;
isConnected = ublox.begin(Wire, i2cAddress);
}
return isConnected;
}
bool UBloxGPS::setup()
{
if (_serial_gps) {
#ifdef GPS_RX_PIN
_serial_gps.begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
#else
_serial_gps.begin(GPS_BAUDRATE);
_serial_gps->begin(GPS_BAUDRATE);
#endif
// _serial_gps.setRxBufferSize(1024); // the default is 256
// _serial_gps.setRxBufferSize(1024); // the default is 256
}
#ifdef GPS_POWER_EN
pinMode(GPS_POWER_EN, OUTPUT);
digitalWrite(GPS_POWER_EN, 1);
delay(200); // Give time for the GPS to startup after we gave power
#endif
// ublox.enableDebugging(Serial);
// note: the lib's implementation has the wrong docs for what the return val is
// it is not a bool, it returns zero for success
isConnected = ublox.begin(_serial_gps);
// try a second time, the ublox lib serial parsing is buggy?
if (!isConnected)
isConnected = ublox.begin(_serial_gps);
if (!tryConnect())
tryConnect();
if (isConnected) {
DEBUG_MSG("Connected to UBLOX GPS successfully\n");
@@ -35,14 +58,21 @@ bool UBloxGPS::setup()
// GPS_TX connected)
ublox.factoryReset();
delay(3000);
isConnected = ublox.begin(_serial_gps);
tryConnect();
DEBUG_MSG("Factory reset success=%d\n", isConnected);
ok = ublox.saveConfiguration(3000);
assert(ok);
return false;
} else {
ok = ublox.setUART1Output(COM_TYPE_UBX, 500); // Use native API
assert(ok);
if (_serial_gps) {
ok = ublox.setUART1Output(COM_TYPE_UBX, 500); // Use native API
assert(ok);
}
if (i2cAddress) {
ok = ublox.setI2COutput(COM_TYPE_UBX, 500);
assert(ok);
}
ok = ublox.setNavigationFrequency(1, 500); // Produce 4x/sec to keep the amount of time we stall in getPVT low
assert(ok);
// ok = ublox.setAutoPVT(false); // Not implemented on NEO-6M
@@ -80,20 +110,22 @@ void UBloxGPS::doTask()
// Consume all characters that have arrived
// getPVT automatically calls checkUblox
// if using i2c or serial look too see if any chars are ready
ublox.checkUblox(); // See if new data is available. Process bytes as they come in.
// If we don't have a fix (a quick check), don't try waiting for a solution)
// Hmmm my fix type reading returns zeros for fix, which doesn't seem correct, because it is still sptting out positions
// turn off for now
fixtype = ublox.getFixType(0);
uint16_t maxWait = i2cAddress ? 300 : 0; // If using i2c we must poll with wait
fixtype = ublox.getFixType(maxWait);
DEBUG_MSG("GPS fix type %d\n", fixtype);
// DEBUG_MSG("sec %d\n", ublox.getSecond());
// DEBUG_MSG("lat %d\n", ublox.getLatitude());
// any fix that has time
if (ublox.getT(0)) {
if (ublox.getT(maxWait)) {
/* Convert to unix time
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970
(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
@@ -109,19 +141,20 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
perhapsSetRTC(t);
}
if ((fixtype >= 3 && fixtype <= 4) && ublox.getP(0)) // rd fixes only
{
// we only notify if position has changed
latitude = ublox.getLatitude(0);
longitude = ublox.getLongitude(0);
altitude = ublox.getAltitude(0) / 1000; // in mm convert to meters
dop = ublox.getPDOP(0); // PDOP (an accuracy metric) is reported in 10^2 units so we have to scale down when we use it
heading = ublox.getHeading(0);
numSatellites = ublox.getSIV(0);
latitude = ublox.getLatitude(0);
longitude = ublox.getLongitude(0);
altitude = ublox.getAltitude(0) / 1000; // in mm convert to meters
dop = ublox.getPDOP(0); // PDOP (an accuracy metric) is reported in 10^2 units so we have to scale down when we use it
heading = ublox.getHeading(0);
numSatellites = ublox.getSIV(0);
// bogus lat lon is reported as 0 or 0 (can be bogus just for one)
// Also: apparently when the GPS is initially reporting lock it can output a bogus latitude > 90 deg!
hasValidLocation = (latitude != 0) && (longitude != 0) && (latitude <= 900000000 && latitude >= -900000000);
// bogus lat lon is reported as 0 or 0 (can be bogus just for one)
// Also: apparently when the GPS is initially reporting lock it can output a bogus latitude > 90 deg!
hasValidLocation = (latitude != 0) && (longitude != 0) && (latitude <= 900000000 && latitude >= -900000000) && (numSatellites > 0);
// we only notify if position has changed due to a new fix
if ((fixtype >= 3 && fixtype <= 4) && ublox.getP(maxWait)) // rd fixes only
{
if (hasValidLocation) {
wantNewLocation = false;
notifyObservers(NULL);
@@ -131,7 +164,8 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
wantNewLocation = true;
// Notify any status instances that are observing us
const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop, heading, numSatellites);
const meshtastic::GPSStatus status =
meshtastic::GPSStatus(hasLock(), isConnected, latitude, longitude, altitude, dop, heading, numSatellites);
newStatus.notifyObservers(&status);
// Once we have sent a location once we only poll the GPS rarely, otherwise check back every 1s until we have something over
+3
View File
@@ -38,4 +38,7 @@ class UBloxGPS : public GPS, public concurrency::PeriodicTask
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
/// always returns 0 to indicate okay to sleep
int prepareSleep(void *unused);
/// Attempt to connect to our GPS, returns false if no gps is present
bool tryConnect();
};
+30 -18
View File
@@ -25,17 +25,17 @@
#include "MeshService.h"
#include "NEMAGPS.h"
#include "NodeDB.h"
#include "concurrency/Periodic.h"
#include "PowerFSM.h"
#include "UBloxGPS.h"
#include "concurrency/Periodic.h"
#include "configuration.h"
#include "error.h"
#include "power.h"
// #include "rom/rtc.h"
#include "DSRRouter.h"
#include "debug.h"
#include "main.h"
// #include "debug.h"
#include "graphics/Screen.h"
#include "main.h"
#include "sleep.h"
#include "timing.h"
#include <OneButton.h>
@@ -152,23 +152,29 @@ void userButtonPressedLong()
#ifndef NO_ESP32
void initWifi()
{
strcpy(radioConfig.preferences.wifi_ssid, "geeksville");
strcpy(radioConfig.preferences.wifi_password, "xxx");
#if 0
// strcpy(radioConfig.preferences.wifi_ssid, "xxx");
// strcpy(radioConfig.preferences.wifi_password, "xxx");
if (radioConfig.has_preferences) {
const char *wifiName = radioConfig.preferences.wifi_ssid;
if (*wifiName) {
const char *wifiPsw = radioConfig.preferences.wifi_password;
if (radioConfig.preferences.wifi_ap_mode) {
// DEBUG_MSG("STARTING WIFI AP: ssid=%s, ok=%d\n", wifiName, WiFi.softAP(wifiName, wifiPsw));
DEBUG_MSG("STARTING WIFI AP: ssid=%s, ok=%d\n", wifiName, WiFi.softAP(wifiName, wifiPsw));
} else {
// WiFi.mode(WIFI_MODE_STA);
WiFi.mode(WIFI_MODE_STA);
DEBUG_MSG("JOINING WIFI: ssid=%s\n", wifiName);
// WiFi.begin(wifiName, wifiPsw);
if (WiFi.begin(wifiName, wifiPsw) == WL_CONNECTED) {
DEBUG_MSG("MY IP ADDRESS: %s\n", WiFi.localIP().toString().c_str());
} else {
DEBUG_MSG("FAILED JOINING WIFI\n");
}
}
}
} else
} else
DEBUG_MSG("Not using WIFI\n");
#endif
}
#endif
@@ -259,12 +265,18 @@ void setup()
// Init GPS - first try ublox
gps = new UBloxGPS();
if (!gps->setup()) {
// Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all. Just
// assume NEMA at 9600 baud.
DEBUG_MSG("ERROR: No UBLOX GPS found, hoping that NEMA might work\n");
delete gps;
gps = new NEMAGPS();
gps->setup();
DEBUG_MSG("ERROR: No UBLOX GPS found\n");
if (GPS::_serial_gps) {
// Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all. Just
// assume NEMA at 9600 baud.
DEBUG_MSG("Hoping that NEMA might work\n");
delete gps;
// dumb NEMA access only work for serial GPSes)
gps = new NEMAGPS();
gps->setup();
}
}
#else
gps = new NEMAGPS();
@@ -290,15 +302,15 @@ void setup()
SPI.begin();
#else
// ESP32
SPI.begin(SCK_GPIO, MISO_GPIO, MOSI_GPIO, NSS_GPIO);
SPI.begin(RF95_SCK, RF95_MISO, RF95_MOSI, RF95_NSS);
SPI.setFrequency(4000000);
#endif
// MUST BE AFTER service.init, so we have our radio config settings (from nodedb init)
RadioInterface *rIf =
#if defined(RF95_IRQ_GPIO)
#if defined(RF95_IRQ)
// new CustomRF95(); old Radiohead based driver
new RF95Interface(NSS_GPIO, RF95_IRQ_GPIO, RESET_GPIO, SPI);
new RF95Interface(RF95_NSS, RF95_IRQ, RF95_RESET, SPI);
#elif defined(SX1262_CS)
new SX1262Interface(SX1262_CS, SX1262_DIO1, SX1262_RESET, SX1262_BUSY, SPI);
#else
+2 -4
View File
@@ -5,14 +5,14 @@
#include "GPS.h"
//#include "MeshBluetoothService.h"
#include "../concurrency/Periodic.h"
#include "BluetoothCommon.h" // needed for updateBatteryLevel, FIXME, eventually when we pull mesh out into a lib we shouldn't be whacking bluetooth from here
#include "MeshService.h"
#include "NodeDB.h"
#include "../concurrency/Periodic.h"
#include "PowerFSM.h"
#include "main.h"
#include "mesh-pb-constants.h"
#include "power.h"
#include "BluetoothUtil.h" // needed for updateBatteryLevel, FIXME, eventually when we pull mesh out into a lib we shouldn't be whacking bluetooth from here
#include "timing.h"
/*
@@ -283,8 +283,6 @@ void MeshService::sendOurPosition(NodeNum dest, bool wantReplies)
sendToMesh(p);
}
int MeshService::onGPSChanged(void *unused)
{
// DEBUG_MSG("got gps notify\n");
+45 -1
View File
@@ -12,6 +12,18 @@ RF95Interface::RF95Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOL
// FIXME - we assume devices never get destroyed
}
/** Some boards require GPIO control of tx vs rx paths */
void RF95Interface::setTransmitEnable(bool txon)
{
#ifdef RF95_TXEN
digitalWrite(RF95_TXEN, txon ? 1 : 0);
#endif
#ifdef RF95_RXEN
digitalWrite(RF95_RXEN, txon ? 0 : 1);
#endif
}
/// Initialise the Driver transport hardware and software.
/// Make sure the Driver is properly configured before calling init().
/// \return true if initialisation succeeded.
@@ -24,8 +36,30 @@ bool RF95Interface::init()
power = MAX_POWER;
iface = lora = new RadioLibRF95(&module);
#ifdef RF95_TCXO
pinMode(RF95_TCXO, OUTPUT);
digitalWrite(RF95_TCXO, 1);
#endif
/*
#define RF95_TXEN (22) // If defined, this pin should be set high prior to transmit (controls an external analog switch)
#define RF95_RXEN (23) // If defined, this pin should be set high prior to receive (controls an external analog switch)
*/
#ifdef RF95_TXEN
pinMode(RF95_TXEN, OUTPUT);
digitalWrite(RF95_TXEN, 0);
#endif
#ifdef RF95_RXEN
pinMode(RF95_RXEN, OUTPUT);
digitalWrite(RF95_RXEN, 1);
#endif
setTransmitEnable(false);
int res = lora->begin(freq, bw, sf, cr, syncWord, power, currentLimit, preambleLength);
DEBUG_MSG("LORA init result %d\n", res);
DEBUG_MSG("RF95 init result %d\n", res);
if (res == ERR_NONE)
res = lora->setCRC(SX126X_LORA_CRC_ON);
@@ -98,8 +132,18 @@ void RF95Interface::setStandby()
completeSending(); // If we were sending, not anymore
}
/** We override to turn on transmitter power as needed.
*/
void RF95Interface::configHardwareForSend()
{
setTransmitEnable(true);
RadioLibInterface::configHardwareForSend();
}
void RF95Interface::startReceive()
{
setTransmitEnable(false);
setStandby();
int err = lora->startReceive();
assert(err == ERR_NONE);
+9
View File
@@ -52,4 +52,13 @@ class RF95Interface : public RadioLibInterface
virtual void addReceiveMetadata(MeshPacket *mp);
virtual void setStandby();
/**
* We override to turn on transmitter power as needed.
*/
virtual void configHardwareForSend();
private:
/** Some boards require GPIO control of tx vs rx paths */
void setTransmitEnable(bool txon);
};
+5
View File
@@ -161,6 +161,11 @@ class SimRadio : public RadioInterface
/// Make sure the Driver is properly configured before calling init().
/// \return true if initialisation succeeded.
virtual bool init() { return true; }
/// Apply any radio provisioning changes
/// Make sure the Driver is properly configured before calling init().
/// \return true if initialisation succeeded.
virtual bool reconfigure() { return true; }
};
/// Debug printing for packets
+9
View File
@@ -3,6 +3,10 @@
#define RF95_CHIP_VERSION 0x12
#define RF95_ALT_VERSION 0x11 // Supposedly some versions of the chip have id 0x11
// From datasheet but radiolib doesn't know anything about this
#define SX127X_REG_TCXO 0x4B
RadioLibRF95::RadioLibRF95(Module *mod) : SX1278(mod) {}
int16_t RadioLibRF95::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint8_t currentLimit,
@@ -18,6 +22,11 @@ int16_t RadioLibRF95::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_
state = config();
RADIOLIB_ASSERT(state);
#ifdef RF95_TCXO
state = _mod->SPIsetRegValue(SX127X_REG_TCXO, 0x10 | _mod->SPIgetRegValue(SX127X_REG_TCXO));
RADIOLIB_ASSERT(state);
#endif
// configure publicly accessible settings
state = setFrequency(freq);
RADIOLIB_ASSERT(state);
+3 -3
View File
@@ -24,8 +24,8 @@
(MAX_RX_TOPHONE + MAX_RX_FROMRADIO + 2 * MAX_TX_QUEUE + \
2) // max number of packets which can be in flight (either queued from reception or queued for sending)
static MemoryPool<MeshPacket> staticPool(MAX_PACKETS);
// static MemoryDynamic<MeshPacket> staticPool;
// static MemoryPool<MeshPacket> staticPool(MAX_PACKETS);
static MemoryDynamic<MeshPacket> staticPool;
Allocator<MeshPacket> &packetPool = staticPool;
@@ -36,7 +36,7 @@ Allocator<MeshPacket> &packetPool = staticPool;
*/
Router::Router() : fromRadioQueue(MAX_RX_FROMRADIO)
{
// This is called pre main(), don't touch anything here, the following code is not safe
// This is called pre main(), don't touch anything here, the following code is not safe
/* DEBUG_MSG("Size of NodeInfo %d\n", sizeof(NodeInfo));
DEBUG_MSG("Size of SubPacket %d\n", sizeof(SubPacket));
+2 -3
View File
@@ -33,7 +33,7 @@ bool SX1262Interface::init()
if (power > 22) // This chip has lower power limits than some
power = 22;
int res = lora.begin(freq, bw, sf, cr, syncWord, power, currentLimit, preambleLength, tcxoVoltage, useRegulatorLDO);
DEBUG_MSG("LORA init result %d\n", res);
DEBUG_MSG("SX1262 init result %d\n", res);
#ifdef SX1262_TXEN
// lora.begin sets Dio2 as RF switch control, which is not true if we are manually controlling RX and TX
@@ -119,8 +119,7 @@ void SX1262Interface::addReceiveMetadata(MeshPacket *mp)
mp->rx_snr = lora.getSNR();
}
/** start an immediate transmit
* We override to turn on transmitter power as needed.
/** We override to turn on transmitter power as needed.
*/
void SX1262Interface::configHardwareForSend()
{
+148 -135
View File
@@ -1,22 +1,50 @@
#include "NRF52Bluetooth.h"
#include "BluetoothCommon.h"
#include "configuration.h"
#include "main.h"
#include <bluefruit.h>
/* HRM Service Definitions
* Heart Rate Monitor Service: 0x180D
* Heart Rate Measurement Char: 0x2A37
* Body Sensor Location Char: 0x2A38
*/
BLEService hrms = BLEService(UUID16_SVC_HEART_RATE);
BLECharacteristic hrmc = BLECharacteristic(UUID16_CHR_HEART_RATE_MEASUREMENT);
BLECharacteristic bslc = BLECharacteristic(UUID16_CHR_BODY_SENSOR_LOCATION);
// NRF52 wants these constants as byte arrays
// Generated here https://yupana-engineering.com/online-uuid-to-c-array-converter - but in REVERSE BYTE ORDER
const uint8_t MESH_SERVICE_UUID_16[16u] = {0xfd, 0xea, 0x73, 0xe2, 0xca, 0x5d, 0xa8, 0x9f,
0x1f, 0x46, 0xa8, 0x15, 0x18, 0xb2, 0xa1, 0x6b};
const uint8_t TORADIO_UUID_16[16u] = {0xe7, 0x01, 0x44, 0x12, 0x66, 0x78, 0xdd, 0xa1,
0xad, 0x4d, 0x9e, 0x12, 0xd2, 0x76, 0x5c, 0xf7};
const uint8_t FROMRADIO_UUID_16[16u] = {0xd5, 0x54, 0xe4, 0xc5, 0x25, 0xc5, 0x31, 0xa5,
0x55, 0x4a, 0x02, 0xee, 0xc2, 0xbc, 0xa2, 0x8b};
const uint8_t FROMNUM_UUID_16[16u] = {0x53, 0x44, 0xe3, 0x47, 0x75, 0xaa, 0x70, 0xa6,
0x66, 0x4f, 0x00, 0xa8, 0x8c, 0xa1, 0x9d, 0xed};
BLEDis bledis; // DIS (Device Information Service) helper class instance
BLEBas blebas; // BAS (Battery Service) helper class instance
BLEDfu bledfu; // DFU software update helper service
static BLEService meshBleService = BLEService(BLEUuid(MESH_SERVICE_UUID_16));
static BLECharacteristic fromNum = BLECharacteristic(BLEUuid(FROMNUM_UUID_16));
static BLECharacteristic fromRadio = BLECharacteristic(BLEUuid(FROMRADIO_UUID_16));
static BLECharacteristic toRadio = BLECharacteristic(BLEUuid(TORADIO_UUID_16));
uint8_t bps = 0;
static BLEDis bledis; // DIS (Device Information Service) helper class instance
static BLEBas blebas; // BAS (Battery Service) helper class instance
static BLEDfu bledfu; // DFU software update helper service
// This scratch buffer is used for various bluetooth reads/writes - but it is safe because only one bt operation can be in
// proccess at once
// static uint8_t trBytes[_max(_max(_max(_max(ToRadio_size, RadioConfig_size), User_size), MyNodeInfo_size), FromRadio_size)];
static uint8_t fromRadioBytes[FromRadio_size];
static uint8_t toRadioBytes[ToRadio_size];
class BluetoothPhoneAPI : public PhoneAPI
{
/**
* Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies)
*/
virtual void onNowHasData(uint32_t fromRadioNum)
{
PhoneAPI::onNowHasData(fromRadioNum);
DEBUG_MSG("BLE notify fromNum\n");
fromNum.notify32(fromRadioNum);
}
};
static BluetoothPhoneAPI *bluetoothPhoneAPI;
void connect_callback(uint16_t conn_handle)
{
@@ -26,7 +54,7 @@ void connect_callback(uint16_t conn_handle)
char central_name[32] = {0};
connection->getPeerName(central_name, sizeof(central_name));
DEBUG_MSG("Connected to %s\n", central_name);
DEBUG_MSG("BLE Connected to %s\n", central_name);
}
/**
@@ -37,9 +65,8 @@ void connect_callback(uint16_t conn_handle)
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void)conn_handle;
(void)reason;
DEBUG_MSG("Disconnected, reason = 0x%x\n", reason);
DEBUG_MSG("BLE Disconnected, reason = 0x%x\n", reason);
}
void cccd_callback(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_value)
@@ -49,11 +76,11 @@ void cccd_callback(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_valu
// Check the characteristic this CCCD update is associated with in case
// this handler is used for multiple CCCD records.
if (chr->uuid == hrmc.uuid) {
if (chr->uuid == fromNum.uuid) {
if (chr->notifyEnabled(conn_hdl)) {
DEBUG_MSG("Heart Rate Measurement 'Notify' enabled\n");
DEBUG_MSG("fromNum 'Notify' enabled\n");
} else {
DEBUG_MSG("Heart Rate Measurement 'Notify' disabled\n");
DEBUG_MSG("fromNum 'Notify' disabled\n");
}
}
}
@@ -62,13 +89,15 @@ void startAdv(void)
{
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// Include HRM Service UUID
Bluefruit.Advertising.addService(hrms);
// IncludeService UUID
// Bluefruit.ScanResponse.addService(meshBleService);
Bluefruit.ScanResponse.addTxPower();
Bluefruit.ScanResponse.addName();
// Include Name
Bluefruit.Advertising.addName();
// Bluefruit.Advertising.addName();
Bluefruit.Advertising.addService(meshBleService);
/* Start Advertising
* - Enable auto advertising if disconnected
@@ -82,74 +111,105 @@ void startAdv(void)
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds. FIXME, we should stop advertising after X
}
void setupHRM(void)
// Just ack that the caller is allowed to read
static void authorizeRead(uint16_t conn_hdl)
{
// Configure the Heart Rate Monitor service
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.heart_rate.xml
// Supported Characteristics:
// Name UUID Requirement Properties
// ---------------------------- ------ ----------- ----------
// Heart Rate Measurement 0x2A37 Mandatory Notify
// Body Sensor Location 0x2A38 Optional Read
// Heart Rate Control Point 0x2A39 Conditional Write <-- Not used here
hrms.begin();
ble_gatts_rw_authorize_reply_params_t reply = {.type = BLE_GATTS_AUTHORIZE_TYPE_READ};
reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
sd_ble_gatts_rw_authorize_reply(conn_hdl, &reply);
}
/**
* client is starting read, pull the bytes from our API class
*/
void fromRadioAuthorizeCb(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt_read_t *request)
{
if (request->offset == 0) {
// If the read is long, we will get multiple authorize invocations - we only populate data on the first
size_t numBytes = bluetoothPhoneAPI->getFromRadio(fromRadioBytes);
// DEBUG_MSG("fromRadioAuthorizeCb numBytes=%u\n", numBytes);
// if (numBytes >= 2) DEBUG_MSG("fromRadio bytes %x %x\n", fromRadioBytes[0], fromRadioBytes[1]);
// Someone is going to read our value as soon as this callback returns. So fill it with the next message in the queue
// or make empty if the queue is empty
fromRadio.write(fromRadioBytes, numBytes);
} else {
// DEBUG_MSG("Ignoring successor read\n");
}
authorizeRead(conn_hdl);
}
void toRadioWriteCb(uint16_t conn_hdl, BLECharacteristic *chr, uint8_t *data, uint16_t len)
{
DEBUG_MSG("toRadioWriteCb data %p, len %u\n", data, len);
bluetoothPhoneAPI->handleToRadio(data, len);
}
/**
* client is starting read, pull the bytes from our API class
*/
void fromNumAuthorizeCb(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt_read_t *request)
{
DEBUG_MSG("fromNumAuthorizeCb\n");
authorizeRead(conn_hdl);
}
void setupMeshService(void)
{
bluetoothPhoneAPI = new BluetoothPhoneAPI();
bluetoothPhoneAPI->init();
meshBleService.begin();
// Note: You must call .begin() on the BLEService before calling .begin() on
// any characteristic(s) within that service definition.. Calling .begin() on
// a BLECharacteristic will cause it to be added to the last BLEService that
// was 'begin()'ed!
// Configure the Heart Rate Measurement characteristic
// See:
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
// Properties = Notify
// Min Len = 1
// Max Len = 8
// B0 = UINT8 - Flag (MANDATORY)
// b5:7 = Reserved
// b4 = RR-Internal (0 = Not present, 1 = Present)
// b3 = Energy expended status (0 = Not present, 1 = Present)
// b1:2 = Sensor contact status (0+1 = Not supported, 2 = Supported but contact not detected, 3 = Supported and
// detected) b0 = Value format (0 = UINT8, 1 = UINT16)
// B1 = UINT8 - 8-bit heart rate measurement value in BPM
// B2:3 = UINT16 - 16-bit heart rate measurement value in BPM
// B4:5 = UINT16 - Energy expended in joules
// B6:7 = UINT16 - RR Internal (1/1024 second resolution)
hrmc.setProperties(CHR_PROPS_NOTIFY);
hrmc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
hrmc.setFixedLen(2);
hrmc.setCccdWriteCallback(cccd_callback); // Optionally capture CCCD updates
hrmc.begin();
uint8_t hrmdata[2] = {0b00000110, 0x40}; // Set the characteristic to use 8-bit values, with the sensor connected and detected
hrmc.write(hrmdata, 2);
fromNum.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ);
fromNum.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); // FIXME, secure this!!!
fromNum.setFixedLen(
0); // Variable len (either 0 or 4) FIXME consider changing protocol so it is fixed 4 byte len, where 0 means empty
fromNum.setMaxLen(4);
fromNum.setCccdWriteCallback(cccd_callback); // Optionally capture CCCD updates
// We don't yet need to hook the fromNum auth callback
// fromNum.setReadAuthorizeCallback(fromNumAuthorizeCb);
fromNum.write32(0); // Provide default fromNum of 0
fromNum.begin();
// uint8_t hrmdata[2] = {0b00000110, 0x40}; // Set the characteristic to use 8-bit values, with the sensor connected and
// detected
// hrmc.write(hrmdata, 2);
// Configure the Body Sensor Location characteristic
// See:
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
// Properties = Read
// Min Len = 1
// Max Len = 1
// B0 = UINT8 - Body Sensor Location
// 0 = Other
// 1 = Chest
// 2 = Wrist
// 3 = Finger
// 4 = Hand
// 5 = Ear Lobe
// 6 = Foot
// 7:255 = Reserved
bslc.setProperties(CHR_PROPS_READ);
bslc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
bslc.setFixedLen(1);
bslc.begin();
bslc.write8(2); // Set the characteristic to 'Wrist' (2)
fromRadio.setProperties(CHR_PROPS_READ);
fromRadio.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); // FIXME secure this!
fromRadio.setMaxLen(sizeof(fromRadioBytes));
fromRadio.setReadAuthorizeCallback(
fromRadioAuthorizeCb,
false); // We don't call this callback via the adafruit queue, because we can safely run in the BLE context
fromRadio.setBuffer(fromRadioBytes, sizeof(fromRadioBytes)); // we preallocate our fromradio buffer so we won't waste space
// for two copies
fromRadio.begin();
toRadio.setProperties(CHR_PROPS_WRITE);
toRadio.setPermission(SECMODE_OPEN, SECMODE_OPEN); // FIXME secure this!
toRadio.setFixedLen(0);
toRadio.setMaxLen(512);
toRadio.setBuffer(toRadioBytes, sizeof(toRadioBytes));
toRadio.setWriteCallback(
toRadioWriteCb,
false); // We don't call this callback via the adafruit queue, because we can safely run in the BLE context
toRadio.begin();
}
// FIXME, turn off soft device access for debugging
static bool isSoftDeviceAllowed = false;
static bool isSoftDeviceAllowed = true;
void NRF52Bluetooth::setup()
{
@@ -158,7 +218,7 @@ void NRF52Bluetooth::setup()
Bluefruit.begin();
// Set the advertised device name (keep it short!)
Bluefruit.setName(getDeviceName()); // FIXME
Bluefruit.setName(getDeviceName());
// Set the connect/disconnect callback handlers
Bluefruit.Periph.setConnectCallback(connect_callback);
@@ -166,21 +226,22 @@ void NRF52Bluetooth::setup()
// Configure and Start the Device Information Service
DEBUG_MSG("Configuring the Device Information Service\n");
bledis.setManufacturer("meshtastic.org");
bledis.setModel("NRF52-meshtastic"); // FIXME
bledis.setManufacturer(HW_VENDOR);
bledis.setModel(optstr(HW_VERSION));
bledis.setFirmwareRev(optstr(APP_VERSION));
bledis.begin();
// Start the BLE Battery Service and set it to 100%
DEBUG_MSG("Configuring the Battery Service\n");
blebas.begin();
blebas.write(42); // FIXME, report real power levels
blebas.write(0); // Unknown battery level for now
bledfu.begin(); // Install the DFU helper
// Setup the Heart Rate Monitor service using
// BLEService and BLECharacteristic classes
DEBUG_MSG("Configuring the Heart Rate Monitor Service\n");
setupHRM();
DEBUG_MSG("Configuring the Mesh bluetooth service\n");
setupMeshService();
// Supposedly debugging works with soft device if you disable advertising
if (isSoftDeviceAllowed) {
@@ -192,56 +253,8 @@ void NRF52Bluetooth::setup()
}
}
/*
void loop()
/// Given a level between 0-100, update the BLE attribute
void updateBatteryLevel(uint8_t level)
{
digitalToggle(LED_RED);
if ( Bluefruit.connected() ) {
uint8_t hrmdata[2] = { 0b00000110, bps++ }; // Sensor connected, increment BPS value
// Note: We use .notify instead of .write!
// If it is connected but CCCD is not enabled
// The characteristic's value is still updated although notification is not sent
if ( hrmc.notify(hrmdata, sizeof(hrmdata)) ){
Serial.print("Heart Rate Measurement updated to: "); Serial.println(bps);
}else{
Serial.println("ERROR: Notify not set in the CCCD or not connected!");
}
}
// Only send update once per second
delay(1000);
}
*/
/*
examples of advanced characteristics. use setReadAuthorizeCallback to prepare data for reads by others
err_t BLEDfu::begin(void)
{
// Invoke base class begin()
VERIFY_STATUS( BLEService::begin() );
// No need to keep packet & revision characteristics
BLECharacteristic chr_packet(UUID128_CHR_DFU_PACKET);
chr_packet.setTempMemory();
chr_packet.setProperties(CHR_PROPS_WRITE_WO_RESP);
chr_packet.setMaxLen(20);
VERIFY_STATUS( chr_packet.begin() );
_chr_control.setProperties(CHR_PROPS_WRITE | CHR_PROPS_NOTIFY);
_chr_control.setMaxLen(23);
_chr_control.setWriteAuthorizeCallback(bledfu_control_wr_authorize_cb);
VERIFY_STATUS( _chr_control.begin() );
BLECharacteristic chr_revision(UUID128_CHR_DFU_REVISON);
chr_revision.setTempMemory();
chr_revision.setProperties(CHR_PROPS_READ);
chr_revision.setFixedLen(2);
VERIFY_STATUS( chr_revision.begin());
chr_revision.write16(DFU_REV_APPMODE);
return ERROR_NONE;
}
*/
blebas.write(level);
}
+31
View File
@@ -0,0 +1,31 @@
#include "rtos.h"
#include <assert.h>
#include <stdlib.h>
/**
* Custom new/delete to panic if out out memory
*/
void *operator new(size_t size)
{
auto p = rtos_malloc(size);
assert(p);
return p;
}
void *operator new[](size_t size)
{
auto p = rtos_malloc(size);
assert(p);
return p;
}
void operator delete(void *ptr)
{
rtos_free(ptr);
}
void operator delete[](void *ptr)
{
rtos_free(ptr);
}
+31 -27
View File
@@ -5,47 +5,49 @@
enum { r0, r1, r2, r3, r12, lr, pc, psr };
// we can't use the regular DEBUG_MSG for these crash dumps because it depends on threading still being running. Instead use the
// segger in memory tool
#define FAULT_MSG(...) SEGGER_MSG(__VA_ARGS__)
// Per http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0552a/Cihcfefj.html
static void printUsageErrorMsg(uint32_t cfsr)
{
DEBUG_MSG("Usage fault: ");
FAULT_MSG("Usage fault: ");
cfsr >>= SCB_CFSR_USGFAULTSR_Pos; // right shift to lsb
if ((cfsr & (1 << 9)) != 0)
DEBUG_MSG("Divide by zero\n");
FAULT_MSG("Divide by zero\n");
if ((cfsr & (1 << 8)) != 0)
DEBUG_MSG("Unaligned\n");
FAULT_MSG("Unaligned\n");
}
static void printBusErrorMsg(uint32_t cfsr)
{
DEBUG_MSG("Usage fault: ");
FAULT_MSG("Bus fault: ");
cfsr >>= SCB_CFSR_BUSFAULTSR_Pos; // right shift to lsb
if ((cfsr & (1 << 0)) != 0)
DEBUG_MSG("Instruction bus error\n");
FAULT_MSG("Instruction bus error\n");
if ((cfsr & (1 << 1)) != 0)
DEBUG_MSG("Precise data bus error\n");
FAULT_MSG("Precise data bus error\n");
if ((cfsr & (1 << 2)) != 0)
DEBUG_MSG("Imprecise data bus error\n");
FAULT_MSG("Imprecise data bus error\n");
}
static void printMemErrorMsg(uint32_t cfsr)
{
DEBUG_MSG("Usage fault: ");
FAULT_MSG("Memory fault: ");
cfsr >>= SCB_CFSR_MEMFAULTSR_Pos; // right shift to lsb
if ((cfsr & (1 << 0)) != 0)
DEBUG_MSG("Instruction access violation\n");
FAULT_MSG("Instruction access violation\n");
if ((cfsr & (1 << 1)) != 0)
DEBUG_MSG("Data access violation\n");
FAULT_MSG("Data access violation\n");
}
static void HardFault_Impl(uint32_t stack[])
extern "C" void HardFault_Impl(uint32_t stack[])
{
DEBUG_MSG("In Hard Fault Handler\n");
DEBUG_MSG("SCB->HFSR = 0x%08lx\n", SCB->HFSR);
FAULT_MSG("Hard Fault occurred! SCB->HFSR = 0x%08lx\n", SCB->HFSR);
if ((SCB->HFSR & SCB_HFSR_FORCED_Msk) != 0) {
DEBUG_MSG("Forced Hard Fault\n");
DEBUG_MSG("SCB->CFSR = 0x%08lx\n", SCB->CFSR);
FAULT_MSG("Forced Hard Fault: SCB->CFSR = 0x%08lx\n", SCB->CFSR);
if ((SCB->CFSR & SCB_CFSR_USGFAULTSR_Msk) != 0) {
printUsageErrorMsg(SCB->CFSR);
@@ -57,21 +59,23 @@ static void HardFault_Impl(uint32_t stack[])
printMemErrorMsg(SCB->CFSR);
}
DEBUG_MSG("r0 = 0x%08lx\n", stack[r0]);
DEBUG_MSG("r1 = 0x%08lx\n", stack[r1]);
DEBUG_MSG("r2 = 0x%08lx\n", stack[r2]);
DEBUG_MSG("r3 = 0x%08lx\n", stack[r3]);
DEBUG_MSG("r12 = 0x%08lx\n", stack[r12]);
DEBUG_MSG("lr = 0x%08lx\n", stack[lr]);
DEBUG_MSG("pc = 0x%08lx\n", stack[pc]);
DEBUG_MSG("psr = 0x%08lx\n", stack[psr]);
asm volatile("bkpt #01");
while (1)
;
FAULT_MSG("r0 = 0x%08lx\n", stack[r0]);
FAULT_MSG("r1 = 0x%08lx\n", stack[r1]);
FAULT_MSG("r2 = 0x%08lx\n", stack[r2]);
FAULT_MSG("r3 = 0x%08lx\n", stack[r3]);
FAULT_MSG("r12 = 0x%08lx\n", stack[r12]);
FAULT_MSG("lr = 0x%08lx\n", stack[lr]);
FAULT_MSG("pc = 0x%08lx\n", stack[pc]);
FAULT_MSG("psr = 0x%08lx\n", stack[psr]);
}
FAULT_MSG("Done with fault report - Waiting to reboot\n");
asm volatile("bkpt #01"); // Enter the debugger if one is connected
while (1)
;
}
void HardFault_Handler(void)
extern "C" void HardFault_Handler(void)
{
asm volatile(" mrs r0,msp\n"
" b HardFault_Impl \n");
+19 -1
View File
@@ -3,7 +3,11 @@
#include <assert.h>
#include <ble_gap.h>
#include <memory.h>
#include <nrf52840.h>
#include <stdio.h>
#ifdef NRF52840_XXAA
// #include <nrf52840.h>
#endif
// #define USE_SOFTDEVICE
@@ -49,6 +53,7 @@ void setBluetoothEnable(bool on)
if (on != bleOn) {
if (on) {
if (!nrf52Bluetooth) {
// DEBUG_MSG("DISABLING NRF52 BLUETOOTH WHILE DEBUGGING\n");
nrf52Bluetooth = new NRF52Bluetooth();
nrf52Bluetooth->setup();
}
@@ -59,6 +64,18 @@ void setBluetoothEnable(bool on)
}
}
/**
* Override printf to use the SEGGER output library
*/
int printf(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
auto res = SEGGER_RTT_vprintf(0, fmt, &args);
va_end(args);
return res;
}
void nrf52Setup()
{
@@ -80,4 +97,5 @@ void nrf52Setup()
// ble_controller_rand_vector_get_blocking(&r, sizeof(r));
// randomSeed(r);
DEBUG_MSG("FIXME, call randomSeed\n");
// ::printf("TESTING PRINTF\n");
}