commit
8963aeccb9
15
README.md
15
README.md
@ -14,7 +14,7 @@ Paxcounter is a proof-of-concept device for metering passenger flows in realtime
|
|||||||
|
|
||||||
Intention of this project is to do this without intrusion in privacy: You don't need to track people owned devices, if you just want to count them. Therefore, Paxcounter does not persistenly store MAC adresses and does no kind of fingerprinting the scanned devices.
|
Intention of this project is to do this without intrusion in privacy: You don't need to track people owned devices, if you just want to count them. Therefore, Paxcounter does not persistenly store MAC adresses and does no kind of fingerprinting the scanned devices.
|
||||||
|
|
||||||
Metered counts are transferred to a server via a LoRaWAN network, and/or a local SPI cable interface.
|
Data is transferred to a server via a LoRaWAN network, and/or a wired SPI slave interface.
|
||||||
|
|
||||||
You can build this project battery powered and reach a full day uptime with a single 18650 Li-Ion cell.
|
You can build this project battery powered and reach a full day uptime with a single 18650 Li-Ion cell.
|
||||||
|
|
||||||
@ -33,15 +33,16 @@ This can all be done with a single small and cheap ESP32 board for less than $20
|
|||||||
LoLin32lite + [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-Lora)
|
LoLin32lite + [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-Lora)
|
||||||
- Adafruit ESP32 Feather + LoRa Wing + OLED Wing, #IoT Octopus32 (Octopus + ESP32 Feather)
|
- Adafruit ESP32 Feather + LoRa Wing + OLED Wing, #IoT Octopus32 (Octopus + ESP32 Feather)
|
||||||
|
|
||||||
*SPI only*: (code yet to come)
|
*SPI only*:
|
||||||
|
|
||||||
- Pyom: WiPy
|
- Pyom: WiPy
|
||||||
- WeMos: LoLin32, LoLin32 Lite, WeMos D32
|
- WeMos: LoLin32, LoLin32 Lite, WeMos D32
|
||||||
|
- Generic ESP32
|
||||||
|
|
||||||
Depending on board hardware following features are supported:
|
Depending on board hardware following features are supported:
|
||||||
- LED
|
- LED (power/status)
|
||||||
- OLED Display
|
- OLED Display (detailed status)
|
||||||
- RGB LED
|
- RGB LED (colorized status)
|
||||||
- Button
|
- Button
|
||||||
- Silicon unique ID
|
- Silicon unique ID
|
||||||
- Battery voltage monitoring
|
- Battery voltage monitoring
|
||||||
@ -53,7 +54,7 @@ Hardware dependent settings (pinout etc.) are stored in board files in /hal dire
|
|||||||
<b>3D printable cases</b> can be found (and, if wanted so, ordered) on Thingiverse, see
|
<b>3D printable cases</b> can be found (and, if wanted so, ordered) on Thingiverse, see
|
||||||
<A HREF="https://www.thingiverse.com/thing:2670713">Heltec</A>, <A HREF="https://www.thingiverse.com/thing:2811127">TTGOv2</A>, <A HREF="https://www.thingiverse.com/thing:3005574">TTGOv2.1</A>, <A HREF="https://www.thingiverse.com/thing:3041339">T-BEAM</A> for example.<br>
|
<A HREF="https://www.thingiverse.com/thing:2670713">Heltec</A>, <A HREF="https://www.thingiverse.com/thing:2811127">TTGOv2</A>, <A HREF="https://www.thingiverse.com/thing:3005574">TTGOv2.1</A>, <A HREF="https://www.thingiverse.com/thing:3041339">T-BEAM</A> for example.<br>
|
||||||
|
|
||||||
<b>Power consumption</b> was metered at around 750 - 1000mW, depending on board and user settings in paxcounter.conf. If you are limited on battery, you may want to save around 30% power by disabling bluetooth (commenting out line *#define BLECOUNTER* in paxcounter.conf).
|
<b>Power consumption</b> was metered at around 450 - 1000mW, depending on board and user settings in paxcounter.conf. If you are limited on battery, you may want to save around 30% power by disabling bluetooth (commenting out line *#define BLECOUNTER* in paxcounter.conf).
|
||||||
|
|
||||||
# Preparing
|
# Preparing
|
||||||
|
|
||||||
@ -102,7 +103,7 @@ Note: If you use this software you do this at your own risk. That means that you
|
|||||||
|
|
||||||
# Privacy disclosure
|
# Privacy disclosure
|
||||||
|
|
||||||
Paxcounter generates identifiers for sniffed MAC adresses and collects them temporary in the device's RAM for a configurable scan cycle time (default 240 seconds). After each scan cycle the collected identifiers are cleared. Identifiers are generated by salting and hashing MAC adresses. The random salt value changes after each scan cycle. Identifiers and MAC adresses are never transferred to the LoRaWAN network. No persistent storing of MAC adresses, identifiers or timestamps and no other kind of analytics than counting are implemented in this code. Wireless networks are not touched by this code, but MAC adresses from wireless devices as well within as not within wireless networks, regardless if encrypted or unencrypted, are sniffed and processed by this code. If the bluetooth option in the code is enabled, bluetooth MACs are scanned and processed by the included BLE stack, then hashed and counted by this code.
|
Paxcounter generates identifiers for sniffed MAC adresses and collects them temporary in the device's RAM for a configurable scan cycle time (default 60 seconds). After each scan cycle the collected identifiers are cleared. Identifiers are generated by salting and hashing MAC adresses. The random salt value changes after each scan cycle. Identifiers and MAC adresses are never transferred to the LoRaWAN network. No persistent storing of MAC adresses, identifiers or timestamps and no other kind of analytics than counting are implemented in this code. Wireless networks are not touched by this code, but MAC adresses from wireless devices as well within as not within wireless networks, regardless if encrypted or unencrypted, are sniffed and processed by this code. If the bluetooth option in the code is enabled, bluetooth MACs are scanned and processed by the included BLE stack, then hashed and counted by this code.
|
||||||
|
|
||||||
# LED blink pattern
|
# LED blink pattern
|
||||||
|
|
||||||
|
@ -38,6 +38,14 @@ typedef struct {
|
|||||||
uint8_t Message[PAYLOAD_BUFFER_SIZE];
|
uint8_t Message[PAYLOAD_BUFFER_SIZE];
|
||||||
} MessageBuffer_t;
|
} MessageBuffer_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t latitude;
|
||||||
|
uint32_t longitude;
|
||||||
|
uint8_t satellites;
|
||||||
|
uint16_t hdop;
|
||||||
|
uint16_t altitude;
|
||||||
|
} gpsStatus_t;
|
||||||
|
|
||||||
// global variables
|
// global variables
|
||||||
extern configData_t cfg; // current device configuration
|
extern configData_t cfg; // current device configuration
|
||||||
extern char display_line6[], display_line7[]; // screen buffers
|
extern char display_line6[], display_line7[]; // screen buffers
|
||||||
@ -52,24 +60,17 @@ extern std::array<uint64_t, 0xff> beacons;
|
|||||||
|
|
||||||
extern TaskHandle_t irqHandlerTask, wifiSwitchTask;
|
extern TaskHandle_t irqHandlerTask, wifiSwitchTask;
|
||||||
|
|
||||||
|
#include "led.h"
|
||||||
|
#include "payload.h"
|
||||||
|
|
||||||
#ifdef HAS_GPS
|
#ifdef HAS_GPS
|
||||||
#include "gpsread.h"
|
#include "gpsread.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
|
||||||
#include "led.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "payload.h"
|
|
||||||
|
|
||||||
#ifdef HAS_LORA
|
#ifdef HAS_LORA
|
||||||
#include "lorawan.h"
|
#include "lorawan.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAS_SPI
|
|
||||||
#include "spisend.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAS_DISPLAY
|
#ifdef HAS_DISPLAY
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
#endif
|
#endif
|
||||||
@ -90,8 +91,4 @@ extern TaskHandle_t irqHandlerTask, wifiSwitchTask;
|
|||||||
#include "antenna.h"
|
#include "antenna.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void reset_counters(void);
|
|
||||||
void blink_LED(uint16_t set_color, uint16_t set_blinkduration);
|
|
||||||
uint64_t uptime();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -8,14 +8,6 @@
|
|||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint32_t latitude;
|
|
||||||
uint32_t longitude;
|
|
||||||
uint8_t satellites;
|
|
||||||
uint16_t hdop;
|
|
||||||
uint16_t altitude;
|
|
||||||
} gpsStatus_t;
|
|
||||||
|
|
||||||
extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe
|
extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe
|
||||||
extern gpsStatus_t
|
extern gpsStatus_t
|
||||||
gps_status; // Make struct for storing gps data globally available
|
gps_status; // Make struct for storing gps data globally available
|
||||||
|
@ -37,8 +37,6 @@ extern TaskHandle_t ledLoopTask;
|
|||||||
void rgb_set_color(uint16_t hue);
|
void rgb_set_color(uint16_t hue);
|
||||||
void blink_LED(uint16_t set_color, uint16_t set_blinkduration);
|
void blink_LED(uint16_t set_color, uint16_t set_blinkduration);
|
||||||
void ledLoop(void *parameter);
|
void ledLoop(void *parameter);
|
||||||
#if (HAS_LED != NOT_A_PIN)
|
|
||||||
void switch_LED(uint8_t state);
|
void switch_LED(uint8_t state);
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -26,5 +26,10 @@ void os_getDevEui(u1_t *buf);
|
|||||||
void showLoraKeys(void);
|
void showLoraKeys(void);
|
||||||
void switch_lora(uint8_t sf, uint8_t tx);
|
void switch_lora(uint8_t sf, uint8_t tx);
|
||||||
void lora_send(osjob_t *job);
|
void lora_send(osjob_t *job);
|
||||||
|
void lora_enqueuedata(uint8_t messageType, MessageBuffer_t *message);
|
||||||
|
void lora_queuereset(void);
|
||||||
|
void lora_housekeeping(void);
|
||||||
|
|
||||||
|
esp_err_t lora_stack_init();
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -13,5 +13,7 @@
|
|||||||
#include "ota.h"
|
#include "ota.h"
|
||||||
#include "irqhandler.h"
|
#include "irqhandler.h"
|
||||||
#include "led.h"
|
#include "led.h"
|
||||||
|
#include "spislave.h"
|
||||||
|
#include "lorawan.h"
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -4,14 +4,14 @@
|
|||||||
#ifdef USE_OTA
|
#ifdef USE_OTA
|
||||||
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "update.h"
|
|
||||||
#include "battery.h"
|
#include "battery.h"
|
||||||
|
#include "update.h"
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include <WiFiClientSecure.h>
|
#include <WiFiClientSecure.h>
|
||||||
#include <BintrayClient.h>
|
#include <BintrayClient.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
void do_ota_update();
|
int do_ota_update();
|
||||||
void start_ota_update();
|
void start_ota_update();
|
||||||
int version_compare(const String v1, const String v2);
|
int version_compare(const String v1, const String v2);
|
||||||
void display(const uint8_t row, const std::string status,
|
void display(const uint8_t row, const std::string status,
|
||||||
|
@ -38,12 +38,8 @@ public:
|
|||||||
void addStatus(uint16_t voltage, uint64_t uptime, float cputemp, uint32_t mem,
|
void addStatus(uint16_t voltage, uint64_t uptime, float cputemp, uint32_t mem,
|
||||||
uint8_t reset1, uint8_t reset2);
|
uint8_t reset1, uint8_t reset2);
|
||||||
void addAlarm(int8_t rssi, uint8_t message);
|
void addAlarm(int8_t rssi, uint8_t message);
|
||||||
#ifdef HAS_GPS
|
|
||||||
void addGPS(gpsStatus_t value);
|
void addGPS(gpsStatus_t value);
|
||||||
#endif
|
|
||||||
#ifdef HAS_BUTTON
|
|
||||||
void addButton(uint8_t value);
|
void addButton(uint8_t value);
|
||||||
#endif
|
|
||||||
|
|
||||||
#if PAYLOAD_ENCODER == 1 // format plain
|
#if PAYLOAD_ENCODER == 1 // format plain
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#define _RCOMMAND_H
|
#define _RCOMMAND_H
|
||||||
|
|
||||||
#include "senddata.h"
|
#include "senddata.h"
|
||||||
|
#include "cyclic.h"
|
||||||
#include "configmanager.h"
|
#include "configmanager.h"
|
||||||
#include "lorawan.h"
|
#include "lorawan.h"
|
||||||
#include "macsniff.h"
|
#include "macsniff.h"
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
#ifndef _SENDDATA_H
|
#ifndef _SENDDATA_H
|
||||||
#define _SENDDATA_H
|
#define _SENDDATA_H
|
||||||
|
|
||||||
|
#include "spislave.h"
|
||||||
|
#include "lorawan.h"
|
||||||
|
#include "cyclic.h"
|
||||||
|
|
||||||
void SendData(uint8_t port);
|
void SendData(uint8_t port);
|
||||||
void sendPayload(void);
|
void sendPayload(void);
|
||||||
void checkSendQueues(void);
|
void checkSendQueues(void);
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
#ifndef _SPISEND_H
|
|
||||||
#define _SPISEND_H
|
|
||||||
|
|
||||||
#include "globals.h"
|
|
||||||
#include "spi.h"
|
|
||||||
|
|
||||||
extern TaskHandle_t SpiTask;
|
|
||||||
extern QueueHandle_t SPISendQueue;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Process data in SPI send queue
|
|
||||||
*/
|
|
||||||
void spi_loop(void *pvParameters);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* initialize local SPI wire interface
|
|
||||||
*/
|
|
||||||
void hal_spi_init();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Perform SPI write transaction on local SPI wire interface
|
|
||||||
* - write the command byte 'cmd'
|
|
||||||
* - write 'len' bytes out of 'buf'
|
|
||||||
*/
|
|
||||||
void hal_spi_write(uint8_t cmd, const uint8_t* buf, int len);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Perform SPI read transaction on local SPI wire interface
|
|
||||||
* - read the command byte 'cmd'
|
|
||||||
* - read 'len' bytes into 'buf'
|
|
||||||
*/
|
|
||||||
void hal_spi_read(uint8_t cmd, uint8_t* buf, int len);
|
|
||||||
|
|
||||||
#endif
|
|
36
include/spislave.h
Normal file
36
include/spislave.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
//////////////////////// ESP32-Paxcounter \\\\\\\\\\\\\\\\\\\\\\\\\\
|
||||||
|
|
||||||
|
Copyright 2018 Christian Ambach <christian.ambach@deutschebahn.com>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
NOTICE:
|
||||||
|
Parts of the source files in this repository are made available under different
|
||||||
|
licenses. Refer to LICENSE.txt file in repository for more details.
|
||||||
|
|
||||||
|
*/
|
||||||
|
#ifndef _SPISLAVE_H
|
||||||
|
#define _SPISLAVE_H
|
||||||
|
|
||||||
|
#include "globals.h"
|
||||||
|
|
||||||
|
esp_err_t spi_init();
|
||||||
|
|
||||||
|
void spi_enqueuedata(uint8_t messageType, MessageBuffer_t *message);
|
||||||
|
void spi_queuereset();
|
||||||
|
|
||||||
|
void spi_housekeeping();
|
||||||
|
|
||||||
|
#endif // _SPISLAVE_H
|
@ -41,7 +41,7 @@ class UpdateClass {
|
|||||||
Call this to check the space needed for the update
|
Call this to check the space needed for the update
|
||||||
Will return false if there is not enough space
|
Will return false if there is not enough space
|
||||||
*/
|
*/
|
||||||
bool begin(size_t size=UPDATE_SIZE_UNKNOWN, int command = U_FLASH);
|
bool begin(size_t size=UPDATE_SIZE_UNKNOWN, int command = U_FLASH, int ledPin = -1, uint8_t ledOn = LOW);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Writes a buffer to the flash and increments the address
|
Writes a buffer to the flash and increments the address
|
||||||
@ -174,6 +174,9 @@ class UpdateClass {
|
|||||||
|
|
||||||
String _target_md5;
|
String _target_md5;
|
||||||
MD5Builder _md5;
|
MD5Builder _md5;
|
||||||
|
|
||||||
|
int _ledPin;
|
||||||
|
uint8_t _ledOn;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern UpdateClass Update;
|
extern UpdateClass Update;
|
||||||
|
@ -29,7 +29,7 @@ description = Paxcounter is a proof-of-concept ESP32 device for metering passeng
|
|||||||
|
|
||||||
[common]
|
[common]
|
||||||
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
|
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
|
||||||
release_version = 1.6.54
|
release_version = 1.6.6
|
||||||
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
|
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
|
||||||
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
|
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
|
||||||
debug_level = 0
|
debug_level = 0
|
||||||
@ -58,6 +58,7 @@ build_flags =
|
|||||||
-w
|
-w
|
||||||
'-DARDUINO_LMIC_PROJECT_CONFIG_H=../../../src/lmic_config.h'
|
'-DARDUINO_LMIC_PROJECT_CONFIG_H=../../../src/lmic_config.h'
|
||||||
'-DCORE_DEBUG_LEVEL=${common.debug_level}'
|
'-DCORE_DEBUG_LEVEL=${common.debug_level}'
|
||||||
|
'-DLOG_LOCAL_LEVEL=${common.debug_level}'
|
||||||
'-DBINTRAY_PACKAGE="${PIOENV}"'
|
'-DBINTRAY_PACKAGE="${PIOENV}"'
|
||||||
'-DPROGVERSION="${common.release_version}"'
|
'-DPROGVERSION="${common.release_version}"'
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
// Basic config
|
// Basic config
|
||||||
#include "cyclic.h"
|
#include "cyclic.h"
|
||||||
|
#include "rcommand.h"
|
||||||
|
#include "spislave.h"
|
||||||
|
|
||||||
// Local logging tag
|
// Local logging tag
|
||||||
static const char TAG[] = "main";
|
static const char TAG[] = "main";
|
||||||
@ -25,9 +27,9 @@ void doHousekeeping() {
|
|||||||
#ifdef HAS_GPS
|
#ifdef HAS_GPS
|
||||||
ESP_LOGD(TAG, "Gpsloop %d bytes left", uxTaskGetStackHighWaterMark(GpsTask));
|
ESP_LOGD(TAG, "Gpsloop %d bytes left", uxTaskGetStackHighWaterMark(GpsTask));
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAS_SPI
|
|
||||||
ESP_LOGD(TAG, "Spiloop %d bytes left", uxTaskGetStackHighWaterMark(SpiTask));
|
spi_housekeeping();
|
||||||
#endif
|
|
||||||
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
||||||
ESP_LOGD(TAG, "LEDloop %d bytes left", uxTaskGetStackHighWaterMark(ledLoopTask));
|
ESP_LOGD(TAG, "LEDloop %d bytes left", uxTaskGetStackHighWaterMark(ledLoopTask));
|
||||||
#endif
|
#endif
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
// Hardware related definitions for ebox ESP32-bit with external connected RFM95 LoRa
|
// Hardware related definitions for ebox ESP32-bit with external connected RFM95 LoRa
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1
|
#define CFG_sx1276_radio 1
|
||||||
|
|
||||||
#define HAS_LED (23) // blue LED on board
|
#define HAS_LED (23) // blue LED on board
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
// Hardware related definitions for ebox ESP32-bit with external connected RFM95 LoRa
|
// Hardware related definitions for ebox ESP32-bit with external connected RFM95 LoRa
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1
|
#define CFG_sx1276_radio 1
|
||||||
|
|
||||||
#define HAS_LED (22) // Green LED on board
|
#define HAS_LED (22) // Green LED on board
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
// Hardware related definitions for Pycom FiPy Board
|
// Hardware related definitions for Pycom FiPy Board
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
|
|
||||||
#define CFG_sx1272_radio 1
|
#define CFG_sx1272_radio 1
|
||||||
#define HAS_LED NOT_A_PIN // FiPy has no on board LED, so we use RGB LED
|
#define HAS_LED NOT_A_PIN // FiPy has no on board LED, so we use RGB LED
|
||||||
|
@ -7,6 +7,11 @@
|
|||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa or has no LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa or has no LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||||
|
// pin definitions for SPI slave interface
|
||||||
|
#define SPI_MOSI GPIO_NUM_23
|
||||||
|
#define SPI_MISO GPIO_NUM_19
|
||||||
|
#define SPI_SCLK GPIO_NUM_18
|
||||||
|
#define SPI_CS GPIO_NUM_5
|
||||||
|
|
||||||
#define CFG_sx1276_radio 1 // select LoRa chip
|
#define CFG_sx1276_radio 1 // select LoRa chip
|
||||||
//#define CFG_sx1272_radio 1 // select LoRa chip
|
//#define CFG_sx1272_radio 1 // select LoRa chip
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
// Hardware related definitions for Heltec LoRa-32 Board
|
// Hardware related definitions for Heltec LoRa-32 Board
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1
|
#define CFG_sx1276_radio 1
|
||||||
|
|
||||||
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
|
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
// Hardware related definitions for Heltec V2 LoRa-32 Board
|
// Hardware related definitions for Heltec V2 LoRa-32 Board
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1
|
#define CFG_sx1276_radio 1
|
||||||
|
|
||||||
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
|
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
|
||||||
|
@ -7,9 +7,7 @@
|
|||||||
|
|
||||||
#define CFG_sx1272_radio 1 // dummy
|
#define CFG_sx1272_radio 1 // dummy
|
||||||
|
|
||||||
#define HAS_LED 22 // on board LED on GPIO22
|
#define HAS_LED (5) // on board LED on GPIO5
|
||||||
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
|
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
|
||||||
|
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -18,7 +18,6 @@
|
|||||||
#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
|
#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1 // RFM95 module
|
#define CFG_sx1276_radio 1 // RFM95 module
|
||||||
|
|
||||||
// Pins for LORA chip SPI interface, reset line and interrupt lines
|
// Pins for LORA chip SPI interface, reset line and interrupt lines
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
|
#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1 // RFM95 module
|
#define CFG_sx1276_radio 1 // RFM95 module
|
||||||
|
|
||||||
// Pins for LORA chip SPI interface, reset line and interrupt lines
|
// Pins for LORA chip SPI interface, reset line and interrupt lines
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
// Hardware related definitions for Pycom LoPy Board (NOT LoPy4)
|
// Hardware related definitions for Pycom LoPy Board (NOT LoPy4)
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1272_radio 1
|
#define CFG_sx1272_radio 1
|
||||||
#define HAS_LED NOT_A_PIN // LoPy4 has no on board mono LED, we use on board RGB LED
|
#define HAS_LED NOT_A_PIN // LoPy4 has no on board mono LED, we use on board RGB LED
|
||||||
#define HAS_RGB_LED (0) // WS2812B RGB LED on GPIO0
|
#define HAS_RGB_LED (0) // WS2812B RGB LED on GPIO0
|
||||||
@ -26,9 +25,9 @@
|
|||||||
#define WIFI_ANTENNA 0 // 0 = internal, 1 = external
|
#define WIFI_ANTENNA 0 // 0 = internal, 1 = external
|
||||||
|
|
||||||
// uncomment this only if your LoPy runs on a PYTRACK BOARD
|
// uncomment this only if your LoPy runs on a PYTRACK BOARD
|
||||||
//#define HAS_GPS 1
|
#define HAS_GPS 1
|
||||||
//#define GPS_I2C GPIO_NUM_25, GPIO_NUM_26 // SDA (P22), SCL (P21)
|
#define GPS_I2C GPIO_NUM_25, GPIO_NUM_26 // SDA (P22), SCL (P21)
|
||||||
//#define GPS_ADDR 0x10
|
#define GPS_ADDR 0x10
|
||||||
|
|
||||||
// uncomment this only if your LoPy runs on a EXPANSION BOARD
|
// uncomment this only if your LoPy runs on a EXPANSION BOARD
|
||||||
//#define HAS_LED (12) // use if LoPy is on Expansion Board, this has a user LED
|
//#define HAS_LED (12) // use if LoPy is on Expansion Board, this has a user LED
|
||||||
|
@ -6,7 +6,14 @@
|
|||||||
// Hardware related definitions for Pycom LoPy4 Board
|
// Hardware related definitions for Pycom LoPy4 Board
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
|
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
||||||
|
// pin definitions for local wired SPI slave interface
|
||||||
|
#define SPI_MOSI GPIO_NUM_22
|
||||||
|
#define SPI_MISO GPIO_NUM_33
|
||||||
|
#define SPI_SCLK GPIO_NUM_26
|
||||||
|
#define SPI_CS GPIO_NUM_36
|
||||||
|
|
||||||
#define CFG_sx1276_radio 1
|
#define CFG_sx1276_radio 1
|
||||||
//#define HAS_LED NOT_A_PIN // LoPy4 has no on board mono LED, we use on board RGB LED
|
//#define HAS_LED NOT_A_PIN // LoPy4 has no on board mono LED, we use on board RGB LED
|
||||||
#define HAS_RGB_LED (0) // WS2812B RGB LED on GPIO0 (P2)
|
#define HAS_RGB_LED (0) // WS2812B RGB LED on GPIO0 (P2)
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
//#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
|
//#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1 // RFM95 module
|
#define CFG_sx1276_radio 1 // RFM95 module
|
||||||
|
|
||||||
// Pins for LORA chip SPI interface, reset line and interrupt lines
|
// Pins for LORA chip SPI interface, reset line and interrupt lines
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
// Hardware related definitions for TTGO T-Beam board
|
// Hardware related definitions for TTGO T-Beam board
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
||||||
|
|
||||||
#define BOARD_HAS_PSRAM // use extra 4MB external RAM
|
#define BOARD_HAS_PSRAM // use extra 4MB external RAM
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
// Hardware related definitions for TTGOv1 board
|
// Hardware related definitions for TTGOv1 board
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1
|
#define CFG_sx1276_radio 1
|
||||||
|
|
||||||
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
|
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
// Hardware related definitions for TTGO V2 Board
|
// Hardware related definitions for TTGO V2 Board
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
||||||
|
|
||||||
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
|
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
||||||
|
|
||||||
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
|
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
|
||||||
#define HAS_SPI 1 // comment out if device shall not send data via SPI
|
|
||||||
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
||||||
#define HAS_LED NOT_A_PIN // no usable LED on board
|
#define HAS_LED NOT_A_PIN // no usable LED on board
|
||||||
#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
|
#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
|
||||||
|
@ -87,9 +87,8 @@ void rgb_set_color(uint16_t hue) {}
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (HAS_LED != NOT_A_PIN)
|
|
||||||
|
|
||||||
void switch_LED(uint8_t state) {
|
void switch_LED(uint8_t state) {
|
||||||
|
#if (HAS_LED != NOT_A_PIN)
|
||||||
if (state == LED_ON) {
|
if (state == LED_ON) {
|
||||||
// switch LED on
|
// switch LED on
|
||||||
#ifdef LED_ACTIVE_LOW
|
#ifdef LED_ACTIVE_LOW
|
||||||
@ -105,9 +104,8 @@ void switch_LED(uint8_t state) {
|
|||||||
digitalWrite(HAS_LED, LOW);
|
digitalWrite(HAS_LED, LOW);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
#ifdef HAS_LORA
|
|
||||||
|
|
||||||
// Basic Config
|
// Basic Config
|
||||||
#include "lorawan.h"
|
#include "lorawan.h"
|
||||||
|
|
||||||
// Local logging Tag
|
// Local logging Tag
|
||||||
static const char TAG[] = "lora";
|
static const char TAG[] = "lora";
|
||||||
|
|
||||||
|
#ifdef HAS_LORA
|
||||||
|
|
||||||
osjob_t sendjob;
|
osjob_t sendjob;
|
||||||
QueueHandle_t LoraSendQueue;
|
QueueHandle_t LoraSendQueue;
|
||||||
|
|
||||||
@ -329,9 +329,13 @@ void lora_send(osjob_t *job) {
|
|||||||
} else {
|
} else {
|
||||||
if (xQueueReceive(LoraSendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) {
|
if (xQueueReceive(LoraSendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) {
|
||||||
// SendBuffer gets struct MessageBuffer with next payload from queue
|
// SendBuffer gets struct MessageBuffer with next payload from queue
|
||||||
LMIC_setTxData2(SendBuffer.MessagePort, SendBuffer.Message,
|
if (!LMIC_setTxData2(SendBuffer.MessagePort, SendBuffer.Message,
|
||||||
SendBuffer.MessageSize, (cfg.countermode & 0x02));
|
SendBuffer.MessageSize, (cfg.countermode & 0x02))) {
|
||||||
ESP_LOGI(TAG, "%d bytes sent to LoRa", SendBuffer.MessageSize);
|
ESP_LOGI(TAG, "%d byte(s) sent to LoRa", SendBuffer.MessageSize);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "could not send %d byte(s) to LoRa",
|
||||||
|
SendBuffer.MessageSize);
|
||||||
|
}
|
||||||
// sprintf(display_line7, "PACKET QUEUED");
|
// sprintf(display_line7, "PACKET QUEUED");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -342,3 +346,71 @@ void lora_send(osjob_t *job) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAS_LORA
|
#endif // HAS_LORA
|
||||||
|
|
||||||
|
esp_err_t lora_stack_init() {
|
||||||
|
#ifndef HAS_LORA
|
||||||
|
return ESP_OK; // continue main program
|
||||||
|
#else
|
||||||
|
LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
|
||||||
|
if (LoraSendQueue == 0) {
|
||||||
|
ESP_LOGE(TAG, "Could not create LORA send queue. Aborting.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "LORA send queue created, size %d Bytes",
|
||||||
|
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Starting LMIC...");
|
||||||
|
os_init(); // initialize lmic run-time environment on core 1
|
||||||
|
LMIC_reset(); // initialize lmic MAC
|
||||||
|
LMIC_setLinkCheckMode(0);
|
||||||
|
// This tells LMIC to make the receive windows bigger, in case your clock is
|
||||||
|
// faster or slower. This causes the transceiver to be earlier switched on,
|
||||||
|
// so consuming more power. You may sharpen (reduce) CLOCK_ERROR_PERCENTAGE
|
||||||
|
// in src/lmic_config.h if you are limited on battery.
|
||||||
|
LMIC_setClockError(MAX_CLOCK_ERROR * CLOCK_ERROR_PROCENTAGE / 100);
|
||||||
|
// Set the data rate to Spreading Factor 7. This is the fastest supported
|
||||||
|
// rate for 125 kHz channels, and it minimizes air time and battery power. Set
|
||||||
|
// the transmission power to 14 dBi (25 mW).
|
||||||
|
LMIC_setDrTxpow(DR_SF7, 14);
|
||||||
|
|
||||||
|
#if defined(CFG_US915) || defined(CFG_au921)
|
||||||
|
// in the US, with TTN, it saves join time if we start on subband 1 (channels
|
||||||
|
// 8-15). This will get overridden after the join by parameters from the
|
||||||
|
// network. If working with other networks or in other regions, this will need
|
||||||
|
// to be changed.
|
||||||
|
LMIC_selectSubBand(1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!LMIC_startJoining()) { // start joining
|
||||||
|
ESP_LOGI(TAG, "Already joined");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK; // continue main program
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void lora_enqueuedata(uint8_t messageType, MessageBuffer_t *message) {
|
||||||
|
// enqueue message in LORA send queue
|
||||||
|
#ifdef HAS_LORA
|
||||||
|
BaseType_t ret =
|
||||||
|
xQueueSendToBack(LoraSendQueue, (void *)message, (TickType_t)0);
|
||||||
|
if (ret == pdTRUE) {
|
||||||
|
ESP_LOGI(TAG, "%d bytes enqueued for LORA interface", message->MessageSize);
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "LORA sendqueue is full");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void lora_queuereset(void) {
|
||||||
|
#ifdef HAS_LORA
|
||||||
|
xQueueReset(LoraSendQueue);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void lora_housekeeping(void) {
|
||||||
|
#ifdef HAS_LORA
|
||||||
|
// ESP_LOGD(TAG, "loraloop %d bytes left",
|
||||||
|
// uxTaskGetStackHighWaterMark(LoraTask));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
52
src/main.cpp
52
src/main.cpp
@ -160,51 +160,14 @@ void setup() {
|
|||||||
// initialize LoRa
|
// initialize LoRa
|
||||||
#ifdef HAS_LORA
|
#ifdef HAS_LORA
|
||||||
strcat_P(features, " LORA");
|
strcat_P(features, " LORA");
|
||||||
LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
|
|
||||||
if (LoraSendQueue == 0) {
|
|
||||||
ESP_LOGE(TAG, "Could not create LORA send queue. Aborting.");
|
|
||||||
exit(0);
|
|
||||||
} else
|
|
||||||
ESP_LOGI(TAG, "LORA send queue created, size %d Bytes",
|
|
||||||
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Starting LMIC...");
|
|
||||||
os_init(); // initialize lmic run-time environment on core 1
|
|
||||||
LMIC_reset(); // initialize lmic MAC
|
|
||||||
LMIC_setLinkCheckMode(0);
|
|
||||||
// This tells LMIC to make the receive windows bigger, in case your clock is
|
|
||||||
// faster or slower. This causes the transceiver to be earlier switched on,
|
|
||||||
// so consuming more power. You may sharpen (reduce) CLOCK_ERROR_PERCENTAGE
|
|
||||||
// in src/lmic_config.h if you are limited on battery.
|
|
||||||
LMIC_setClockError(MAX_CLOCK_ERROR * CLOCK_ERROR_PROCENTAGE / 100);
|
|
||||||
// Set the data rate to Spreading Factor 7. This is the fastest supported
|
|
||||||
// rate for 125 kHz channels, and it minimizes air time and battery power. Set
|
|
||||||
// the transmission power to 14 dBi (25 mW).
|
|
||||||
LMIC_setDrTxpow(DR_SF7, 14);
|
|
||||||
|
|
||||||
#if defined(CFG_US915) || defined(CFG_au921)
|
|
||||||
// in the US, with TTN, it saves join time if we start on subband 1 (channels
|
|
||||||
// 8-15). This will get overridden after the join by parameters from the
|
|
||||||
// network. If working with other networks or in other regions, this will need
|
|
||||||
// to be changed.
|
|
||||||
LMIC_selectSubBand(1);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
LMIC_startJoining(); // start joining
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
assert(lora_stack_init() == ESP_OK);
|
||||||
|
|
||||||
// initialize SPI
|
// initialize SPI
|
||||||
#ifdef HAS_SPI
|
#ifdef HAS_SPI
|
||||||
strcat_P(features, " SPI");
|
strcat_P(features, " SPI");
|
||||||
SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
|
|
||||||
if (SPISendQueue == 0) {
|
|
||||||
ESP_LOGE(TAG, "Could not create SPI send queue. Aborting.");
|
|
||||||
exit(0);
|
|
||||||
} else
|
|
||||||
ESP_LOGI(TAG, "SPI send queue created, size %d Bytes",
|
|
||||||
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
|
|
||||||
#endif
|
#endif
|
||||||
|
assert(spi_init() == ESP_OK);
|
||||||
|
|
||||||
#ifdef VENDORFILTER
|
#ifdef VENDORFILTER
|
||||||
strcat_P(features, " OUIFLT");
|
strcat_P(features, " OUIFLT");
|
||||||
@ -312,17 +275,6 @@ void setup() {
|
|||||||
1); // CPU core
|
1); // CPU core
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAS_SPI
|
|
||||||
ESP_LOGI(TAG, "Starting SPIloop...");
|
|
||||||
xTaskCreatePinnedToCore(spi_loop, // task function
|
|
||||||
"spiloop", // name of task
|
|
||||||
2048, // stack size of task
|
|
||||||
(void *)1, // parameter of the task
|
|
||||||
2, // priority of the task
|
|
||||||
&SpiTask, // task handle
|
|
||||||
0); // CPU core
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// start state machine
|
// start state machine
|
||||||
ESP_LOGI(TAG, "Starting IRQ Handler...");
|
ESP_LOGI(TAG, "Starting IRQ Handler...");
|
||||||
xTaskCreatePinnedToCore(irqHandler, // task function
|
xTaskCreatePinnedToCore(irqHandler, // task function
|
||||||
|
201
src/ota.cpp
201
src/ota.cpp
@ -26,9 +26,6 @@ const BintrayClient bintray(BINTRAY_USER, BINTRAY_REPO, BINTRAY_PACKAGE);
|
|||||||
// Connection port (HTTPS)
|
// Connection port (HTTPS)
|
||||||
const int port = 443;
|
const int port = 443;
|
||||||
|
|
||||||
// Connection timeout
|
|
||||||
const uint32_t RESPONSE_TIMEOUT_MS = 5000;
|
|
||||||
|
|
||||||
// Variables to validate firmware content
|
// Variables to validate firmware content
|
||||||
int volatile contentLength = 0;
|
int volatile contentLength = 0;
|
||||||
bool volatile isValidContentType = false;
|
bool volatile isValidContentType = false;
|
||||||
@ -43,24 +40,17 @@ inline String getHeaderValue(String header, String headerName) {
|
|||||||
|
|
||||||
void start_ota_update() {
|
void start_ota_update() {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// check battery status if we can before doing ota
|
// check battery status if we can before doing ota
|
||||||
#ifdef HAS_BATTERY_PROBE
|
#ifdef HAS_BATTERY_PROBE
|
||||||
if (!batt_sufficient()) {
|
if (!batt_sufficient()) {
|
||||||
ESP_LOGW(TAG, "Battery voltage %dmV too low for OTA", batt_voltage);
|
ESP_LOGW(TAG, "Battery voltage %dmV too low for OTA", batt_voltage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// turn on LED
|
switch_LED(LED_ON);
|
||||||
#if (HAS_LED != NOT_A_PIN)
|
|
||||||
#ifdef LED_ACTIVE_LOW
|
|
||||||
digitalWrite(HAS_LED, LOW);
|
|
||||||
#else
|
|
||||||
digitalWrite(HAS_LED, HIGH);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAS_DISPLAY
|
#ifdef HAS_DISPLAY
|
||||||
u8x8.begin();
|
u8x8.begin();
|
||||||
@ -82,86 +72,94 @@ void start_ota_update() {
|
|||||||
ESP_LOGI(TAG, "Starting Wifi OTA update");
|
ESP_LOGI(TAG, "Starting Wifi OTA update");
|
||||||
display(1, "**", WIFI_SSID);
|
display(1, "**", WIFI_SSID);
|
||||||
|
|
||||||
|
WiFi.mode(WIFI_AP_STA);
|
||||||
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
||||||
|
|
||||||
int i = WIFI_MAX_TRY;
|
int i = WIFI_MAX_TRY, j = OTA_MAX_TRY;
|
||||||
|
int ret = 1; // 0 = finished, 1 = retry, -1 = abort
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Trying to connect to %s", WIFI_SSID);
|
||||||
|
|
||||||
while (i--) {
|
while (i--) {
|
||||||
ESP_LOGI(TAG, "Trying to connect to %s", WIFI_SSID);
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
if (WiFi.status() == WL_CONNECTED)
|
// we now have wifi connection and try to do an OTA over wifi update
|
||||||
break;
|
|
||||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i >= 0) {
|
|
||||||
ESP_LOGI(TAG, "Connected to %s", WIFI_SSID);
|
ESP_LOGI(TAG, "Connected to %s", WIFI_SSID);
|
||||||
display(1, "OK", "WiFi connected");
|
display(1, "OK", "WiFi connected");
|
||||||
do_ota_update(); // gets and flashes new firmware
|
// do a number of tries to update firmware limited by OTA_MAX_TRY
|
||||||
} else {
|
while ((j--) && (ret > 0)) {
|
||||||
ESP_LOGI(TAG, "Could not connect to %s, rebooting.", WIFI_SSID);
|
ESP_LOGI(TAG, "Starting OTA update, attempt %u of %u", OTA_MAX_TRY - j,
|
||||||
display(1, " E", "no WiFi connect");
|
OTA_MAX_TRY);
|
||||||
|
ret = do_ota_update();
|
||||||
|
}
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||||
|
WiFi.reconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// wifi did not connect
|
||||||
|
ESP_LOGI(TAG, "Could not connect to %s", WIFI_SSID);
|
||||||
|
display(1, " E", "no WiFi connect");
|
||||||
|
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
end:
|
||||||
|
switch_LED(LED_OFF);
|
||||||
|
ESP_LOGI(TAG, "Rebooting to %s firmware", (ret == 0) ? "new" : "current");
|
||||||
display(5, "**", ""); // mark line rebooting
|
display(5, "**", ""); // mark line rebooting
|
||||||
|
|
||||||
// turn off LED
|
|
||||||
#if (HAS_LED != NOT_A_PIN)
|
|
||||||
#ifdef LED_ACTIVE_LOW
|
|
||||||
digitalWrite(HAS_LED, HIGH);
|
|
||||||
#else
|
|
||||||
digitalWrite(HAS_LED, LOW);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||||
ESP.restart();
|
ESP.restart();
|
||||||
|
|
||||||
} // start_ota_update
|
} // start_ota_update
|
||||||
|
|
||||||
void do_ota_update() {
|
// Reads data vom wifi client and flashes it to ota partition
|
||||||
|
// returns: 0 = finished, 1 = retry, -1 = abort
|
||||||
|
int do_ota_update() {
|
||||||
|
|
||||||
char buf[17];
|
char buf[17];
|
||||||
|
bool redirect = true;
|
||||||
|
size_t written = 0;
|
||||||
|
|
||||||
// Fetch the latest firmware version
|
// Fetch the latest firmware version
|
||||||
ESP_LOGI(TAG, "Checking latest firmware version on server...");
|
ESP_LOGI(TAG, "Checking latest firmware version on server");
|
||||||
display(2, "**", "checking version");
|
display(2, "**", "checking version");
|
||||||
const String latest = bintray.getLatestVersion();
|
const String latest = bintray.getLatestVersion();
|
||||||
|
|
||||||
if (latest.length() == 0) {
|
if (latest.length() == 0) {
|
||||||
ESP_LOGI(
|
ESP_LOGI(TAG, "Could not fetch info on latest firmware");
|
||||||
TAG,
|
|
||||||
"Could not load info about the latest firmware. Rebooting to runmode.");
|
|
||||||
display(2, " E", "file not found");
|
display(2, " E", "file not found");
|
||||||
return;
|
return -1;
|
||||||
} else if (version_compare(latest, cfg.version) <= 0) {
|
} else if (version_compare(latest, cfg.version) <= 0) {
|
||||||
ESP_LOGI(TAG, "Current firmware is up to date. Rebooting to runmode.");
|
ESP_LOGI(TAG, "Current firmware is up to date");
|
||||||
display(2, "NO", "no update found");
|
display(2, "NO", "no update found");
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
ESP_LOGI(TAG, "New firmware version v%s available. Downloading...",
|
ESP_LOGI(TAG, "New firmware version v%s available", latest.c_str());
|
||||||
latest.c_str());
|
|
||||||
display(2, "OK", latest.c_str());
|
display(2, "OK", latest.c_str());
|
||||||
|
|
||||||
display(3, "**", "");
|
display(3, "**", "");
|
||||||
String firmwarePath = bintray.getBinaryPath(latest);
|
String firmwarePath = bintray.getBinaryPath(latest);
|
||||||
if (!firmwarePath.endsWith(".bin")) {
|
if (!firmwarePath.endsWith(".bin")) {
|
||||||
ESP_LOGI(TAG, "Unsupported binary format, OTA update cancelled.");
|
ESP_LOGI(TAG, "Unsupported binary format");
|
||||||
display(3, " E", "file type error");
|
display(3, " E", "file type error");
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
String currentHost = bintray.getStorageHost();
|
String currentHost = bintray.getStorageHost();
|
||||||
String prevHost = currentHost;
|
String prevHost = currentHost;
|
||||||
|
|
||||||
WiFiClientSecure client;
|
WiFiClientSecure client;
|
||||||
|
|
||||||
client.setCACert(bintray.getCertificate(currentHost));
|
client.setCACert(bintray.getCertificate(currentHost));
|
||||||
|
// client.setTimeout(RESPONSE_TIMEOUT_MS);
|
||||||
|
// --> causing error [E][WiFiClient.cpp:236] setSocketOption(): 1006 : 9
|
||||||
|
// so we unfortunately need patched update.cpp which sets the stream timeout
|
||||||
|
|
||||||
if (!client.connect(currentHost.c_str(), port)) {
|
if (!client.connect(currentHost.c_str(), port)) {
|
||||||
ESP_LOGI(TAG, "Cannot connect to %s", currentHost.c_str());
|
ESP_LOGI(TAG, "Cannot connect to %s", currentHost.c_str());
|
||||||
display(3, " E", "connection lost");
|
display(3, " E", "connection lost");
|
||||||
return;
|
goto abort;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool redirect = true;
|
|
||||||
while (redirect) {
|
while (redirect) {
|
||||||
if (currentHost != prevHost) {
|
if (currentHost != prevHost) {
|
||||||
client.stop();
|
client.stop();
|
||||||
@ -170,7 +168,7 @@ void do_ota_update() {
|
|||||||
ESP_LOGI(TAG, "Redirect detected, but cannot connect to %s",
|
ESP_LOGI(TAG, "Redirect detected, but cannot connect to %s",
|
||||||
currentHost.c_str());
|
currentHost.c_str());
|
||||||
display(3, " E", "server error");
|
display(3, " E", "server error");
|
||||||
return;
|
goto abort;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,11 +181,10 @@ void do_ota_update() {
|
|||||||
|
|
||||||
unsigned long timeout = millis();
|
unsigned long timeout = millis();
|
||||||
while (client.available() == 0) {
|
while (client.available() == 0) {
|
||||||
if (millis() - timeout > RESPONSE_TIMEOUT_MS) {
|
if ((millis() - timeout) > (RESPONSE_TIMEOUT_MS)) {
|
||||||
ESP_LOGI(TAG, "Client Timeout.");
|
ESP_LOGI(TAG, "Client timeout");
|
||||||
display(3, " E", "client timeout");
|
display(3, " E", "client timeout");
|
||||||
client.stop();
|
goto abort;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,13 +204,12 @@ void do_ota_update() {
|
|||||||
"firmware flashing");
|
"firmware flashing");
|
||||||
redirect = false;
|
redirect = false;
|
||||||
} else if (line.indexOf("302") > 0) {
|
} else if (line.indexOf("302") > 0) {
|
||||||
ESP_LOGI(TAG, "Got 302 status code from server. Redirecting to the "
|
ESP_LOGI(TAG, "Got 302 status code from server. Redirecting to "
|
||||||
"new address");
|
"new address");
|
||||||
redirect = true;
|
redirect = true;
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGI(TAG, "Could not get a valid firmware url.");
|
ESP_LOGI(TAG, "Could not get firmware download URL");
|
||||||
// Unexptected HTTP response. Retry or skip update?
|
goto retry;
|
||||||
redirect = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,76 +238,71 @@ void do_ota_update() {
|
|||||||
isValidContentType = true;
|
isValidContentType = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} // while (client.available())
|
||||||
}
|
} // while (redirect)
|
||||||
|
|
||||||
display(3, "OK", ""); // line download
|
display(3, "OK", ""); // line download
|
||||||
|
|
||||||
// check whether we have everything for OTA update
|
// check whether we have everything for OTA update
|
||||||
if (contentLength && isValidContentType) {
|
if (!(contentLength && isValidContentType)) {
|
||||||
|
ESP_LOGI(TAG, "Invalid OTA server response");
|
||||||
|
display(4, " E", "response error");
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
|
||||||
size_t written = 0;
|
#ifdef HAS_LED
|
||||||
|
#ifndef LED_ACTIVE_LOW
|
||||||
|
if (!Update.begin(contentLength, U_FLASH, HAS_LED, HIGH)) {
|
||||||
|
#else
|
||||||
|
if (!Update.begin(contentLength, U_FLASH, HAS_LED, LOW)) {
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
if (!Update.begin(contentLength)) {
|
||||||
|
#endif
|
||||||
|
ESP_LOGI(TAG, "Not enough space to start OTA update");
|
||||||
|
display(4, " E", "disk full");
|
||||||
|
goto abort;
|
||||||
|
}
|
||||||
|
|
||||||
if (Update.begin(contentLength)) {
|
|
||||||
#ifdef HAS_DISPLAY
|
#ifdef HAS_DISPLAY
|
||||||
// register callback function for showing progress while streaming data
|
// register callback function for showing progress while streaming data
|
||||||
Update.onProgress(&show_progress);
|
Update.onProgress(&show_progress);
|
||||||
#endif
|
#endif
|
||||||
int i = FLASH_MAX_TRY;
|
|
||||||
while ((i--) && (written != contentLength)) {
|
|
||||||
|
|
||||||
ESP_LOGI(TAG,
|
|
||||||
"Starting OTA update, attempt %u of %u. This will take some "
|
|
||||||
"time to complete...",
|
|
||||||
FLASH_MAX_TRY - i, FLASH_MAX_TRY);
|
|
||||||
display(4, "**", "writing...");
|
display(4, "**", "writing...");
|
||||||
|
written = Update.writeStream(client); // this is a blocking call
|
||||||
written = Update.writeStream(client);
|
|
||||||
|
|
||||||
if (written == contentLength) {
|
if (written == contentLength) {
|
||||||
ESP_LOGI(TAG, "Written %u bytes successfully", written);
|
ESP_LOGI(TAG, "Written %u bytes successfully", written);
|
||||||
snprintf(buf, 17, "%ukB Done!", (uint16_t)(written / 1024));
|
snprintf(buf, 17, "%ukB Done!", (uint16_t)(written / 1024));
|
||||||
display(4, "OK", buf);
|
display(4, "OK", buf);
|
||||||
break;
|
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGI(TAG,
|
ESP_LOGI(TAG, "Written only %u of %u bytes, OTA update attempt cancelled",
|
||||||
"Written only %u of %u bytes, OTA update attempt cancelled.",
|
|
||||||
written, contentLength);
|
written, contentLength);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (Update.end()) {
|
if (Update.end()) {
|
||||||
|
goto finished;
|
||||||
if (Update.isFinished()) {
|
|
||||||
ESP_LOGI(
|
|
||||||
TAG,
|
|
||||||
"OTA update completed. Rebooting to runmode with new version.");
|
|
||||||
client.stop();
|
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGI(TAG, "Something went wrong! OTA update hasn't been finished "
|
ESP_LOGI(TAG, "An error occurred. Error#: %d", Update.getError());
|
||||||
"properly.");
|
snprintf(buf, 17, "Error#: %d", Update.getError());
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ESP_LOGI(TAG, "An error occurred. Error #: %d", Update.getError());
|
|
||||||
snprintf(buf, 17, "Error #: %d", Update.getError());
|
|
||||||
display(4, " E", buf);
|
display(4, " E", buf);
|
||||||
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
finished:
|
||||||
ESP_LOGI(TAG, "There isn't enough space to start OTA update");
|
|
||||||
display(4, " E", "disk full");
|
|
||||||
client.flush();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ESP_LOGI(TAG,
|
|
||||||
"There was no valid content in the response from the OTA server!");
|
|
||||||
display(4, " E", "response error");
|
|
||||||
client.flush();
|
|
||||||
}
|
|
||||||
ESP_LOGI(TAG,
|
|
||||||
"OTA update failed. Rebooting to runmode with current version.");
|
|
||||||
client.stop();
|
client.stop();
|
||||||
|
ESP_LOGI(TAG, "OTA update finished");
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
abort:
|
||||||
|
client.stop();
|
||||||
|
ESP_LOGI(TAG, "OTA update failed");
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
retry:
|
||||||
|
return 1;
|
||||||
|
|
||||||
} // do_ota_update
|
} // do_ota_update
|
||||||
|
|
||||||
void display(const uint8_t row, const std::string status,
|
void display(const uint8_t row, const std::string status,
|
||||||
@ -329,7 +320,7 @@ void display(const uint8_t row, const std::string status,
|
|||||||
|
|
||||||
#ifdef HAS_DISPLAY
|
#ifdef HAS_DISPLAY
|
||||||
// callback function to show download progress while streaming data
|
// callback function to show download progress while streaming data
|
||||||
void show_progress (unsigned long current, unsigned long size) {
|
void show_progress(unsigned long current, unsigned long size) {
|
||||||
char buf[17];
|
char buf[17];
|
||||||
snprintf(buf, 17, "%-9lu (%3lu%%)", current, current * 100 / size);
|
snprintf(buf, 17, "%-9lu (%3lu%%)", current, current * 100 / size);
|
||||||
display(4, "**", buf);
|
display(4, "**", buf);
|
||||||
|
@ -67,9 +67,10 @@
|
|||||||
|
|
||||||
// OTA settings
|
// OTA settings
|
||||||
#define USE_OTA 1 // Comment out to disable OTA update
|
#define USE_OTA 1 // Comment out to disable OTA update
|
||||||
#define WIFI_MAX_TRY 20 // maximum number of wifi connect attempts for OTA update [default = 20]
|
#define WIFI_MAX_TRY 5 // maximum number of wifi connect attempts for OTA update [default = 20]
|
||||||
#define FLASH_MAX_TRY 3 // maximum number of attempts for writing update binary to flash [default = 3]
|
#define OTA_MAX_TRY 5 // maximum number of attempts for OTA download and write to flash [default = 3]
|
||||||
#define OTA_MIN_BATT 3700 // minimum battery level vor OTA [millivolt]
|
#define OTA_MIN_BATT 3700 // minimum battery level for OTA [millivolt]
|
||||||
|
#define RESPONSE_TIMEOUT_MS 60000 // firmware binary server connection timeout [milliseconds]
|
||||||
|
|
||||||
// LMIC settings
|
// LMIC settings
|
||||||
// moved to src/lmic_config.h
|
// moved to src/lmic_config.h
|
@ -75,8 +75,8 @@ void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float cputemp,
|
|||||||
buffer[cursor++] = (byte)(reset2);
|
buffer[cursor++] = (byte)(reset2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_GPS
|
|
||||||
void PayloadConvert::addGPS(gpsStatus_t value) {
|
void PayloadConvert::addGPS(gpsStatus_t value) {
|
||||||
|
#ifdef HAS_GPS
|
||||||
buffer[cursor++] = (byte)((value.latitude & 0xFF000000) >> 24);
|
buffer[cursor++] = (byte)((value.latitude & 0xFF000000) >> 24);
|
||||||
buffer[cursor++] = (byte)((value.latitude & 0x00FF0000) >> 16);
|
buffer[cursor++] = (byte)((value.latitude & 0x00FF0000) >> 16);
|
||||||
buffer[cursor++] = (byte)((value.latitude & 0x0000FF00) >> 8);
|
buffer[cursor++] = (byte)((value.latitude & 0x0000FF00) >> 8);
|
||||||
@ -90,14 +90,17 @@ void PayloadConvert::addGPS(gpsStatus_t value) {
|
|||||||
buffer[cursor++] = lowByte(value.hdop);
|
buffer[cursor++] = lowByte(value.hdop);
|
||||||
buffer[cursor++] = highByte(value.altitude);
|
buffer[cursor++] = highByte(value.altitude);
|
||||||
buffer[cursor++] = lowByte(value.altitude);
|
buffer[cursor++] = lowByte(value.altitude);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
|
void PayloadConvert::addButton(uint8_t value) {
|
||||||
#ifdef HAS_BUTTON
|
#ifdef HAS_BUTTON
|
||||||
void PayloadConvert::addButton(uint8_t value) { buffer[cursor++] = value; }
|
buffer[cursor++] = value;
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------------- packed format with LoRa serialization Encoder ---------- */
|
/* ---------------- packed format with LoRa serialization Encoder ----------
|
||||||
|
*/
|
||||||
// derived from
|
// derived from
|
||||||
// https://github.com/thesolarnomad/lora-serialization/blob/master/src/LoraEncoder.cpp
|
// https://github.com/thesolarnomad/lora-serialization/blob/master/src/LoraEncoder.cpp
|
||||||
|
|
||||||
@ -138,18 +141,20 @@ void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float cputemp,
|
|||||||
writeUint8(reset2);
|
writeUint8(reset2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_GPS
|
|
||||||
void PayloadConvert::addGPS(gpsStatus_t value) {
|
void PayloadConvert::addGPS(gpsStatus_t value) {
|
||||||
|
#ifdef HAS_GPS
|
||||||
writeLatLng(value.latitude, value.longitude);
|
writeLatLng(value.latitude, value.longitude);
|
||||||
writeUint8(value.satellites);
|
writeUint8(value.satellites);
|
||||||
writeUint16(value.hdop);
|
writeUint16(value.hdop);
|
||||||
writeUint16(value.altitude);
|
writeUint16(value.altitude);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
|
void PayloadConvert::addButton(uint8_t value) {
|
||||||
#ifdef HAS_BUTTON
|
#ifdef HAS_BUTTON
|
||||||
void PayloadConvert::addButton(uint8_t value) { writeUint8(value); }
|
writeUint8(value);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void PayloadConvert::intToBytes(uint8_t pos, int32_t i, uint8_t byteSize) {
|
void PayloadConvert::intToBytes(uint8_t pos, int32_t i, uint8_t byteSize) {
|
||||||
for (uint8_t x = 0; x < byteSize; x++) {
|
for (uint8_t x = 0; x < byteSize; x++) {
|
||||||
@ -162,7 +167,7 @@ void PayloadConvert::writeUptime(uint64_t uptime) {
|
|||||||
intToBytes(cursor, uptime, 8);
|
intToBytes(cursor, uptime, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PayloadConvert::writeVersion(char * version) {
|
void PayloadConvert::writeVersion(char *version) {
|
||||||
memcpy(buffer + cursor, version, 10);
|
memcpy(buffer + cursor, version, 10);
|
||||||
cursor += 10;
|
cursor += 10;
|
||||||
}
|
}
|
||||||
@ -273,8 +278,8 @@ void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float celsius,
|
|||||||
buffer[cursor++] = lowByte(temp);
|
buffer[cursor++] = lowByte(temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_GPS
|
|
||||||
void PayloadConvert::addGPS(gpsStatus_t value) {
|
void PayloadConvert::addGPS(gpsStatus_t value) {
|
||||||
|
#ifdef HAS_GPS
|
||||||
int32_t lat = value.latitude / 100;
|
int32_t lat = value.latitude / 100;
|
||||||
int32_t lon = value.longitude / 100;
|
int32_t lon = value.longitude / 100;
|
||||||
int32_t alt = value.altitude * 100;
|
int32_t alt = value.altitude * 100;
|
||||||
@ -291,18 +296,18 @@ void PayloadConvert::addGPS(gpsStatus_t value) {
|
|||||||
buffer[cursor++] = (byte)((alt & 0xFF0000) >> 16);
|
buffer[cursor++] = (byte)((alt & 0xFF0000) >> 16);
|
||||||
buffer[cursor++] = (byte)((alt & 0x00FF00) >> 8);
|
buffer[cursor++] = (byte)((alt & 0x00FF00) >> 8);
|
||||||
buffer[cursor++] = (byte)((alt & 0x0000FF));
|
buffer[cursor++] = (byte)((alt & 0x0000FF));
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HAS_BUTTON
|
|
||||||
void PayloadConvert::addButton(uint8_t value) {
|
void PayloadConvert::addButton(uint8_t value) {
|
||||||
|
#ifdef HAS_BUTTON
|
||||||
#if (PAYLOAD_ENCODER == 3)
|
#if (PAYLOAD_ENCODER == 3)
|
||||||
buffer[cursor++] = LPP_BUTTON_CHANNEL;
|
buffer[cursor++] = LPP_BUTTON_CHANNEL;
|
||||||
#endif
|
#endif
|
||||||
buffer[cursor++] = LPP_DIGITAL_INPUT;
|
buffer[cursor++] = LPP_DIGITAL_INPUT;
|
||||||
buffer[cursor++] = value;
|
buffer[cursor++] = value;
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#error "No valid payload converter defined"
|
#error "No valid payload converter defined"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Basic Config
|
// Basic Config
|
||||||
#include "globals.h"
|
#include "senddata.h"
|
||||||
|
|
||||||
// put data to send in RTos Queues used for transmit over channels Lora and SPI
|
// put data to send in RTos Queues used for transmit over channels Lora and SPI
|
||||||
void SendData(uint8_t port) {
|
void SendData(uint8_t port) {
|
||||||
@ -12,19 +12,9 @@ void SendData(uint8_t port) {
|
|||||||
: (PAYLOAD_ENCODER == 4 ? LPP2PORT : LPP1PORT);
|
: (PAYLOAD_ENCODER == 4 ? LPP2PORT : LPP1PORT);
|
||||||
memcpy(SendBuffer.Message, payload.getBuffer(), payload.getSize());
|
memcpy(SendBuffer.Message, payload.getBuffer(), payload.getSize());
|
||||||
|
|
||||||
// enqueue message in LoRa send queue
|
// enqueue message in device's send queues
|
||||||
#ifdef HAS_LORA
|
lora_enqueuedata(port, &SendBuffer);
|
||||||
if (xQueueSendToBack(LoraSendQueue, (void *)&SendBuffer, (TickType_t)0) ==
|
spi_enqueuedata(port, &SendBuffer);
|
||||||
pdTRUE)
|
|
||||||
ESP_LOGI(TAG, "%d bytes enqueued to send on LoRa", payload.getSize());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// enqueue message in SPI send queue
|
|
||||||
#ifdef HAS_SPI
|
|
||||||
if (xQueueSendToBack(SPISendQueue, (void *)&SendBuffer, (TickType_t)0) ==
|
|
||||||
pdTRUE)
|
|
||||||
ESP_LOGI(TAG, "%d bytes enqueued to send on SPI", payload.getSize());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// clear counter if not in cumulative counter mode
|
// clear counter if not in cumulative counter mode
|
||||||
if ((port == COUNTERPORT) && (cfg.countermode != 1)) {
|
if ((port == COUNTERPORT) && (cfg.countermode != 1)) {
|
||||||
@ -63,10 +53,6 @@ void sendPayload() {
|
|||||||
} // sendpayload()
|
} // sendpayload()
|
||||||
|
|
||||||
void flushQueues() {
|
void flushQueues() {
|
||||||
#ifdef HAS_LORA
|
lora_queuereset();
|
||||||
xQueueReset(LoraSendQueue);
|
spi_queuereset();
|
||||||
#endif
|
|
||||||
#ifdef HAS_SPI
|
|
||||||
xQueueReset(SPISendQueue);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
@ -1,72 +0,0 @@
|
|||||||
#ifdef HAS_SPI
|
|
||||||
|
|
||||||
#include "spisend.h"
|
|
||||||
|
|
||||||
// Local logging tag
|
|
||||||
static const char TAG[] = "main";
|
|
||||||
|
|
||||||
MessageBuffer_t SendBuffer;
|
|
||||||
|
|
||||||
QueueHandle_t SPISendQueue;
|
|
||||||
TaskHandle_t SpiTask;
|
|
||||||
|
|
||||||
// SPI feed Task
|
|
||||||
void spi_loop(void *pvParameters) {
|
|
||||||
|
|
||||||
uint8_t buf[32];
|
|
||||||
|
|
||||||
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
// check if data to send on SPI interface
|
|
||||||
if (xQueueReceive(SPISendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) {
|
|
||||||
hal_spi_write(SendBuffer.MessagePort, SendBuffer.Message,
|
|
||||||
SendBuffer.MessageSize);
|
|
||||||
ESP_LOGI(TAG, "%d bytes sent to SPI", SendBuffer.MessageSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if command is received on SPI command port, then call interpreter
|
|
||||||
hal_spi_read(RCMDPORT, buf, 32);
|
|
||||||
if (buf[0])
|
|
||||||
rcommand(buf, sizeof(buf));
|
|
||||||
|
|
||||||
vTaskDelay(2 / portTICK_PERIOD_MS); // yield to CPU
|
|
||||||
} // end of infinite loop
|
|
||||||
|
|
||||||
vTaskDelete(NULL); // shoud never be reached
|
|
||||||
|
|
||||||
} // spi_loop()
|
|
||||||
|
|
||||||
// SPI hardware abstraction layer
|
|
||||||
|
|
||||||
void hal_spi_init() { SPI.begin(SCK, MISO, MOSI, SS); }
|
|
||||||
|
|
||||||
void hal_spi_trx(uint8_t port, uint8_t *buf, int len, uint8_t is_read) {
|
|
||||||
|
|
||||||
SPISettings settings(1E6, MSBFIRST, SPI_MODE0);
|
|
||||||
SPI.beginTransaction(settings);
|
|
||||||
digitalWrite(SS, 0);
|
|
||||||
|
|
||||||
SPI.transfer(port);
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < len; i++) {
|
|
||||||
uint8_t *p = buf + i;
|
|
||||||
uint8_t data = is_read ? 0x00 : *p;
|
|
||||||
data = SPI.transfer(data);
|
|
||||||
if (is_read)
|
|
||||||
*p = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
digitalWrite(SS, 1);
|
|
||||||
SPI.endTransaction();
|
|
||||||
}
|
|
||||||
|
|
||||||
void hal_spi_write(uint8_t port, const uint8_t *buf, int len) {
|
|
||||||
hal_spi_trx(port, (uint8_t *)buf, len, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void hal_spi_read(uint8_t port, uint8_t *buf, int len) {
|
|
||||||
hal_spi_trx(port, buf, len, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // HAS_SPI
|
|
169
src/spislave.cpp
Normal file
169
src/spislave.cpp
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
//////////////////////// ESP32-Paxcounter \\\\\\\\\\\\\\\\\\\\\\\\\\
|
||||||
|
|
||||||
|
Copyright 2018 Christian Ambach <christian.ambach@deutschebahn.com>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
NOTICE:
|
||||||
|
Parts of the source files in this repository are made available under different
|
||||||
|
licenses. Refer to LICENSE.txt file in repository for more details.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "spislave.h"
|
||||||
|
|
||||||
|
#include <driver/spi_slave.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <rom/crc.h>
|
||||||
|
|
||||||
|
static const char TAG[] = __FILE__;
|
||||||
|
|
||||||
|
#define HEADER_SIZE 4
|
||||||
|
// SPI transaction size needs to be at least 8 bytes and dividable by 4, see
|
||||||
|
// https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/spi_slave.html
|
||||||
|
#define BUFFER_SIZE \
|
||||||
|
(MAX(8, HEADER_SIZE + PAYLOAD_BUFFER_SIZE) + \
|
||||||
|
(4 - MAX(8, HEADER_SIZE + PAYLOAD_BUFFER_SIZE) % 4))
|
||||||
|
DMA_ATTR uint8_t txbuf[BUFFER_SIZE];
|
||||||
|
DMA_ATTR uint8_t rxbuf[BUFFER_SIZE];
|
||||||
|
|
||||||
|
QueueHandle_t SPISendQueue;
|
||||||
|
|
||||||
|
TaskHandle_t spiTask;
|
||||||
|
|
||||||
|
void spi_slave_task(void *param) {
|
||||||
|
while (1) {
|
||||||
|
MessageBuffer_t msg;
|
||||||
|
size_t transaction_size;
|
||||||
|
|
||||||
|
// clear rx + tx buffers
|
||||||
|
memset(txbuf, 0, sizeof(txbuf));
|
||||||
|
memset(rxbuf, 0, sizeof(rxbuf));
|
||||||
|
|
||||||
|
// wait until data to send arrivey
|
||||||
|
if (xQueueReceive(SPISendQueue, &msg, portMAX_DELAY) != pdTRUE) {
|
||||||
|
ESP_LOGE(TAG, "Premature return from xQueueReceive() with no data!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill tx buffer with data to send from queue and calculate crc16 cheksum
|
||||||
|
uint8_t *messageType = txbuf + 2;
|
||||||
|
*messageType = msg.MessagePort;
|
||||||
|
uint8_t *messageSize = txbuf + 3;
|
||||||
|
*messageSize = msg.MessageSize;
|
||||||
|
memcpy(txbuf + HEADER_SIZE, &msg.Message, msg.MessageSize);
|
||||||
|
// calculate crc16 checksum, not used yet
|
||||||
|
// uint16_t *crc = (uint16_t *)txbuf;
|
||||||
|
//*crc = crc16_be(0, messageType, msg.MessageSize + HEADER_SIZE - 2);
|
||||||
|
|
||||||
|
// set length for spi slave driver
|
||||||
|
transaction_size = HEADER_SIZE + msg.MessageSize;
|
||||||
|
transaction_size += (4 - transaction_size % 4);
|
||||||
|
|
||||||
|
// prepare spi transaction
|
||||||
|
spi_slave_transaction_t spi_transaction = {0};
|
||||||
|
spi_transaction.length = transaction_size * 8;
|
||||||
|
spi_transaction.tx_buffer = txbuf;
|
||||||
|
spi_transaction.rx_buffer = rxbuf;
|
||||||
|
|
||||||
|
// wait until spi master clocks out the data, and read results in rx buffer
|
||||||
|
ESP_LOGI(TAG, "Prepared SPI transaction for %zu byte(s)", transaction_size);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(TAG, txbuf, transaction_size, ESP_LOG_DEBUG);
|
||||||
|
esp_err_t ret =
|
||||||
|
spi_slave_transmit(HSPI_HOST, &spi_transaction, portMAX_DELAY);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(TAG, rxbuf, transaction_size, ESP_LOG_DEBUG);
|
||||||
|
ESP_LOGI(TAG, "Transaction finished with size %zu bits",
|
||||||
|
spi_transaction.trans_len);
|
||||||
|
|
||||||
|
// check if command was received, then call interpreter with command payload
|
||||||
|
if ((spi_transaction.trans_len) && ((rxbuf[2]) == RCMDPORT)) {
|
||||||
|
rcommand(rxbuf + HEADER_SIZE, spi_transaction.trans_len - HEADER_SIZE);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t spi_init() {
|
||||||
|
#ifndef HAS_SPI
|
||||||
|
return ESP_OK;
|
||||||
|
#else
|
||||||
|
|
||||||
|
SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
|
||||||
|
if (SPISendQueue == 0) {
|
||||||
|
ESP_LOGE(TAG, "Could not create SPI send queue. Aborting.");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "SPI send queue created, size %d Bytes",
|
||||||
|
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
|
||||||
|
|
||||||
|
spi_bus_config_t spi_bus_cfg = {.mosi_io_num = SPI_MOSI,
|
||||||
|
.miso_io_num = SPI_MISO,
|
||||||
|
.sclk_io_num = SPI_SCLK,
|
||||||
|
.quadwp_io_num = -1,
|
||||||
|
.quadhd_io_num = -1,
|
||||||
|
.max_transfer_sz = 0,
|
||||||
|
.flags = 0};
|
||||||
|
|
||||||
|
spi_slave_interface_config_t spi_slv_cfg = {.spics_io_num = SPI_CS,
|
||||||
|
.flags = 0,
|
||||||
|
.queue_size = 1,
|
||||||
|
.mode = 0,
|
||||||
|
.post_setup_cb = NULL,
|
||||||
|
.post_trans_cb = NULL};
|
||||||
|
|
||||||
|
// Enable pull-ups on SPI lines so we don't detect rogue pulses when no master
|
||||||
|
// is connected
|
||||||
|
gpio_set_pull_mode(SPI_MOSI, GPIO_PULLUP_ONLY);
|
||||||
|
gpio_set_pull_mode(SPI_SCLK, GPIO_PULLUP_ONLY);
|
||||||
|
gpio_set_pull_mode(SPI_CS, GPIO_PULLUP_ONLY);
|
||||||
|
|
||||||
|
esp_err_t ret =
|
||||||
|
spi_slave_initialize(HSPI_HOST, &spi_bus_cfg, &spi_slv_cfg, 1);
|
||||||
|
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
ESP_LOGI(TAG, "Starting SPIloop...");
|
||||||
|
xTaskCreate(spi_slave_task, "spiloop", 4096, (void *)NULL, 2, &spiTask);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "SPI interface initialization failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void spi_enqueuedata(uint8_t messageType, MessageBuffer_t *message) {
|
||||||
|
// enqueue message in SPI send queue
|
||||||
|
#ifdef HAS_SPI
|
||||||
|
BaseType_t ret =
|
||||||
|
xQueueSendToBack(SPISendQueue, (void *)message, (TickType_t)0);
|
||||||
|
if (ret == pdTRUE) {
|
||||||
|
ESP_LOGI(TAG, "%d byte(s) enqueued for SPI interface", message->MessageSize);
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "SPI sendqueue is full");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void spi_queuereset(void) {
|
||||||
|
#ifdef HAS_SPI
|
||||||
|
xQueueReset(SPISendQueue);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void spi_housekeeping(void) {
|
||||||
|
#ifdef HAS_SPI
|
||||||
|
ESP_LOGD(TAG, "spiloop %d bytes left", uxTaskGetStackHighWaterMark(spiTask));
|
||||||
|
#endif
|
||||||
|
}
|
@ -1,11 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
this file copied from esp32-arduino library and patched, see PR
|
this file copied from esp32-arduino library and patched, see PR
|
||||||
https://github.com/espressif/arduino-esp32/pull/1886
|
https://github.com/espressif/arduino-esp32/pull/1979
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "update.h"
|
#include "Update.h"
|
||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
#include "esp_spi_flash.h"
|
#include "esp_spi_flash.h"
|
||||||
#include "esp_ota_ops.h"
|
#include "esp_ota_ops.h"
|
||||||
@ -95,6 +93,10 @@ void UpdateClass::_reset() {
|
|||||||
_progress = 0;
|
_progress = 0;
|
||||||
_size = 0;
|
_size = 0;
|
||||||
_command = U_FLASH;
|
_command = U_FLASH;
|
||||||
|
|
||||||
|
if(_ledPin != -1) {
|
||||||
|
digitalWrite(_ledPin, !_ledOn); // off
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UpdateClass::canRollBack(){
|
bool UpdateClass::canRollBack(){
|
||||||
@ -113,12 +115,15 @@ bool UpdateClass::rollBack(){
|
|||||||
return _partitionIsBootable(partition) && !esp_ota_set_boot_partition(partition);
|
return _partitionIsBootable(partition) && !esp_ota_set_boot_partition(partition);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UpdateClass::begin(size_t size, int command) {
|
bool UpdateClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) {
|
||||||
if(_size > 0){
|
if(_size > 0){
|
||||||
log_w("already running");
|
log_w("already running");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ledPin = ledPin;
|
||||||
|
_ledOn = !!ledOn; // 0(LOW) or 1(HIGH)
|
||||||
|
|
||||||
_reset();
|
_reset();
|
||||||
_error = 0;
|
_error = 0;
|
||||||
|
|
||||||
@ -310,7 +315,7 @@ size_t UpdateClass::write(uint8_t *data, size_t len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t UpdateClass::writeStream(Stream &data) {
|
size_t UpdateClass::writeStream(Stream &data) {
|
||||||
data.setTimeout(20000);
|
data.setTimeout(RESPONSE_TIMEOUT_MS);
|
||||||
size_t written = 0;
|
size_t written = 0;
|
||||||
size_t toRead = 0;
|
size_t toRead = 0;
|
||||||
if(hasError() || !isRunning())
|
if(hasError() || !isRunning())
|
||||||
@ -323,16 +328,32 @@ size_t UpdateClass::writeStream(Stream &data) {
|
|||||||
if (_progress_callback) {
|
if (_progress_callback) {
|
||||||
_progress_callback(0, _size);
|
_progress_callback(0, _size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(_ledPin != -1) {
|
||||||
|
pinMode(_ledPin, OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
while(remaining()) {
|
while(remaining()) {
|
||||||
toRead = data.readBytes(_buffer + _bufferLen, (SPI_FLASH_SEC_SIZE - _bufferLen));
|
if(_ledPin != -1) {
|
||||||
|
digitalWrite(_ledPin, _ledOn); // Switch LED on
|
||||||
|
}
|
||||||
|
size_t bytesToRead = SPI_FLASH_SEC_SIZE - _bufferLen;
|
||||||
|
if(bytesToRead > remaining()) {
|
||||||
|
bytesToRead = remaining();
|
||||||
|
}
|
||||||
|
|
||||||
|
toRead = data.readBytes(_buffer + _bufferLen, bytesToRead);
|
||||||
if(toRead == 0) { //Timeout
|
if(toRead == 0) { //Timeout
|
||||||
delay(100);
|
delay(100);
|
||||||
toRead = data.readBytes(_buffer + _bufferLen, (SPI_FLASH_SEC_SIZE - _bufferLen));
|
toRead = data.readBytes(_buffer + _bufferLen, bytesToRead);
|
||||||
if(toRead == 0) { //Timeout
|
if(toRead == 0) { //Timeout
|
||||||
_abort(UPDATE_ERROR_STREAM);
|
_abort(UPDATE_ERROR_STREAM);
|
||||||
return written;
|
return written;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(_ledPin != -1) {
|
||||||
|
digitalWrite(_ledPin, !_ledOn); // Switch LED off
|
||||||
|
}
|
||||||
_bufferLen += toRead;
|
_bufferLen += toRead;
|
||||||
if((_bufferLen == remaining() || _bufferLen == SPI_FLASH_SEC_SIZE) && !_writeBuffer())
|
if((_bufferLen == remaining() || _bufferLen == SPI_FLASH_SEC_SIZE) && !_writeBuffer())
|
||||||
return written;
|
return written;
|
||||||
|
Loading…
Reference in New Issue
Block a user