├── ADCDMA.cpp
├── ADCDMA.h
├── BQ27441_Definitions.h
├── ClosedCube_SHT31D.cpp
├── ClosedCube_SHT31D.h
├── LICENSE
├── MMA8452-xlero-OSBH.cpp
├── MMA8452-xlero-OSBH.h
├── README.md
├── SparkFunBQ27441.cpp
├── SparkFunBQ27441.h
├── binary.h
├── downsampler.cpp
├── downsampler.h
├── filter.cpp
├── filter.h
├── fw19_OS.bin
├── geolocate.cpp
├── google-maps-device-locator.cpp
├── google-maps-device-locator.h
├── main_beta_comp.cpp
├── params.h
├── particle.ignore
└── softap_bb.h
/ADCDMA.cpp:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | Photon analog input in DMA mode
3 |
4 | ADCDMA.h
5 | ADCDMA Library Header File
6 | Modified by Peter Naylor: Jan 5, 2018
7 |
8 | derived from: Rickkas7 @ Particle.io
9 | https://github.com/rickkas7/photonAudio
10 |
11 | This file prototypes the ADCDMA class, implemented in ADCDMA.cpp.
12 |
13 | Development environment specifics:
14 | ParticleCLI
15 | Particle Photon
16 |
17 | Distributed as-is; no warranty is given.
18 | ******************************************************************************/
19 |
20 | #include "ADCDMA.h"
21 |
22 |
23 | // Helpful post:
24 | // https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https%3a%2f%2fmy%2est%2ecom%2fpublic%2fSTe2ecommunities%2fmcu%2fLists%2fcortex%5fmx%5fstm32%2fstm32f207%20ADC%2bTIMER%2bDMA%20%20Poor%20Peripheral%20Library%20Examples&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B¤tviews=6249
25 |
26 | ADCDMA::ADCDMA(int pin, uint16_t *buf, size_t bufSize) : pin(pin), buf(buf), bufSize(bufSize) {
27 | }
28 |
29 | ADCDMA::~ADCDMA() {
30 |
31 | }
32 |
33 | void ADCDMA::start(size_t freqHZ) {
34 |
35 | // Using Dual ADC Regular Simultaneous DMA Mode 1
36 |
37 | // Using Timer3. To change timers, make sure you edit all of:
38 | // RCC_APB1Periph_TIM3, TIM3, ADC_ExternalTrigConv_T3_TRGO
39 |
40 | RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
41 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
42 | RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, ENABLE);
43 | RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
44 |
45 | // Set the pin as analog input
46 | // GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
47 | // GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
48 | HAL_Pin_Mode(pin, AN_INPUT);
49 |
50 | // Enable the DMA Stream IRQ Channel
51 | NVIC_InitTypeDef NVIC_InitStructure;
52 | NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
53 | NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
54 | NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
55 | NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
56 | NVIC_Init(&NVIC_InitStructure);
57 |
58 | // 60000000UL = 60 MHz Timer Clock = HCLK / 2
59 | // Even low audio rates like 8000 Hz will fit in a 16-bit counter with no prescaler (period = 7500)
60 | TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
61 | TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
62 | TIM_TimeBaseStructure.TIM_Period = (60000000UL / freqHZ) - 1;
63 | TIM_TimeBaseStructure.TIM_Prescaler = 0;
64 | TIM_TimeBaseStructure.TIM_ClockDivision = 0;
65 | TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
66 | TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
67 | TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update); // ADC_ExternalTrigConv_T3_TRGO
68 | TIM_Cmd(TIM3, ENABLE);
69 |
70 | ADC_CommonInitTypeDef ADC_CommonInitStructure;
71 | ADC_InitTypeDef ADC_InitStructure;
72 | DMA_InitTypeDef DMA_InitStructure;
73 |
74 | // DMA2 Stream0 channel0 configuration
75 | DMA_InitStructure.DMA_Channel = DMA_Channel_0;
76 | DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)buf;
77 | DMA_InitStructure.DMA_PeripheralBaseAddr = 0x40012308; // CDR_ADDRESS; Packed ADC1, ADC2;
78 | DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
79 | DMA_InitStructure.DMA_BufferSize = bufSize;
80 | DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
81 | DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
82 | DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
83 | DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
84 | DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
85 | DMA_InitStructure.DMA_Priority = DMA_Priority_High;
86 | DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
87 | DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
88 | DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
89 | DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
90 | DMA_Init(DMA2_Stream0, &DMA_InitStructure);
91 |
92 | // Don't enable DMA Stream Half / Transfer Complete interrupt
93 | // Since we want to write out of loop anyway, there's no real advantage to using the interrupt, and as
94 | // far as I can tell, you can't set the interrupt handler for DMA2_Stream0 without modifying
95 | // system firmware because there's no built-in handler for it.
96 | // DMA_ITConfig(DMA2_Stream0, DMA_IT_TC | DMA_IT_HT, ENABLE);
97 |
98 | DMA_Cmd(DMA2_Stream0, ENABLE);
99 |
100 | // ADC Common Init
101 | ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult;
102 | ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
103 | ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1;
104 | ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
105 | ADC_CommonInit(&ADC_CommonInitStructure);
106 |
107 | // ADC1 configuration
108 | ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
109 | ADC_InitStructure.ADC_ScanConvMode = DISABLE;
110 | ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
111 | ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
112 | ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO;
113 | ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Left;
114 | ADC_InitStructure.ADC_NbrOfConversion = 1;
115 | ADC_Init(ADC1, &ADC_InitStructure);
116 |
117 | // ADC2 configuration - same
118 | ADC_Init(ADC2, &ADC_InitStructure);
119 |
120 | //
121 | ADC_RegularChannelConfig(ADC1, PIN_MAP[pin].adc_channel, 1, ADC_SampleTime_15Cycles);
122 | ADC_RegularChannelConfig(ADC2, PIN_MAP[pin].adc_channel, 1, ADC_SampleTime_15Cycles);
123 |
124 | // Enable DMA request after last transfer (Multi-ADC mode)
125 | ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);
126 |
127 | // Enable ADCs
128 | ADC_Cmd(ADC1, ENABLE);
129 | ADC_Cmd(ADC2, ENABLE);
130 |
131 | ADC_SoftwareStartConv(ADC1);
132 | }
133 |
134 | void ADCDMA::stop() {
135 | // Stop the ADC
136 | ADC_Cmd(ADC1, DISABLE);
137 | ADC_Cmd(ADC2, DISABLE);
138 |
139 | DMA_Cmd(DMA2_Stream0, DISABLE);
140 |
141 | // Stop the timer
142 | TIM_Cmd(TIM3, DISABLE);
143 | }
144 |
145 |
146 | // End ADCDMA
147 |
--------------------------------------------------------------------------------
/ADCDMA.h:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | Photon analog input in DMA mode
3 |
4 | ADCDMA.h
5 | ADCDMA Library Header File
6 | Modified by Peter Naylor: Jan 5, 2018
7 |
8 | derived from: Rickkas7 @ Particle.io
9 | https://github.com/rickkas7/photonAudio
10 |
11 | This file prototypes the ADCDMA class, implemented in ADCDMA.cpp.
12 |
13 | Development environment specifics:
14 | ParticleCLI
15 | Particle Photon
16 |
17 | Distributed as-is; no warranty is given.
18 | ******************************************************************************/
19 |
20 | #ifndef ADCDMA_h
21 | #define ADCDMA_h
22 |
23 |
24 | #include "Particle.h"
25 | #include "adc_hal.h"
26 | #include "gpio_hal.h"
27 | #include "pinmap_hal.h"
28 | #include "pinmap_impl.h"
29 |
30 |
31 | // ADCDMA - Class to use Photon ADC in DMA Mode
32 | class ADCDMA {
33 | public:
34 | ADCDMA(int pin, uint16_t *buf, size_t bufSize);
35 | virtual ~ADCDMA();
36 |
37 | void start(size_t freqHZ);
38 | void stop();
39 |
40 | private:
41 | int pin;
42 | uint16_t *buf;
43 | size_t bufSize;
44 | };
45 |
46 | #endif
--------------------------------------------------------------------------------
/BQ27441_Definitions.h:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | BQ27441_Definitions.h
3 | BQ27441 LiPo Fuel Gauge Definitions
4 | Jim Lindblom @ SparkFun Electronics
5 | May 9, 2016
6 | https://github.com/sparkfun/SparkFun_BQ27441_Arduino_Library
7 |
8 | BQ27441 hardware constants, register addresses, and bit positions.
9 |
10 | Hardware Resources:
11 | - Arduino Development Board
12 | - SparkFun Battery Babysitter
13 |
14 | Development environment specifics:
15 | Arduino 1.6.7
16 | SparkFun Battery Babysitter v1.0
17 | Arduino Uno (any 'duino should do)
18 | ******************************************************************************/
19 | #define BQ72441_I2C_ADDRESS 0x55 // Default I2C address of the BQ27441-G1A
20 |
21 | ///////////////////////
22 | // General Constants //
23 | ///////////////////////
24 | #define BQ27441_UNSEAL_KEY 0x8000 // Secret code to unseal the BQ27441-G1A
25 | #define BQ27441_DEVICE_ID 0x0421 // Default device ID
26 |
27 | ///////////////////////
28 | // Standard Commands //
29 | ///////////////////////
30 | // The fuel gauge uses a series of 2-byte standard commands to enable system
31 | // reading and writing of battery information. Each command has an associated
32 | // sequential command-code pair.
33 | #define BQ27441_COMMAND_CONTROL 0x00 // Control()
34 | #define BQ27441_COMMAND_TEMP 0x02 // Temperature()
35 | #define BQ27441_COMMAND_VOLTAGE 0x04 // Voltage()
36 | #define BQ27441_COMMAND_FLAGS 0x06 // Flags()
37 | #define BQ27441_COMMAND_NOM_CAPACITY 0x08 // NominalAvailableCapacity()
38 | #define BQ27441_COMMAND_AVAIL_CAPACITY 0x0A // FullAvailableCapacity()
39 | #define BQ27441_COMMAND_REM_CAPACITY 0x0C // RemainingCapacity()
40 | #define BQ27441_COMMAND_FULL_CAPACITY 0x0E // FullChargeCapacity()
41 | #define BQ27441_COMMAND_AVG_CURRENT 0x10 // AverageCurrent()
42 | #define BQ27441_COMMAND_STDBY_CURRENT 0x12 // StandbyCurrent()
43 | #define BQ27441_COMMAND_MAX_CURRENT 0x14 // MaxLoadCurrent()
44 | #define BQ27441_COMMAND_AVG_POWER 0x18 // AveragePower()
45 | #define BQ27441_COMMAND_SOC 0x1C // StateOfCharge()
46 | #define BQ27441_COMMAND_INT_TEMP 0x1E // InternalTemperature()
47 | #define BQ27441_COMMAND_SOH 0x20 // StateOfHealth()
48 | #define BQ27441_COMMAND_REM_CAP_UNFL 0x28 // RemainingCapacityUnfiltered()
49 | #define BQ27441_COMMAND_REM_CAP_FIL 0x2A // RemainingCapacityFiltered()
50 | #define BQ27441_COMMAND_FULL_CAP_UNFL 0x2C // FullChargeCapacityUnfiltered()
51 | #define BQ27441_COMMAND_FULL_CAP_FIL 0x2E // FullChargeCapacityFiltered()
52 | #define BQ27441_COMMAND_SOC_UNFL 0x30 // StateOfChargeUnfiltered()
53 |
54 | //////////////////////////
55 | // Control Sub-commands //
56 | //////////////////////////
57 | // Issuing a Control() command requires a subsequent 2-byte subcommand. These
58 | // additional bytes specify the particular control function desired. The
59 | // Control() command allows the system to control specific features of the fuel
60 | // gauge during normal operation and additional features when the device is in
61 | // different access modes.
62 | #define BQ27441_CONTROL_STATUS 0x00
63 | #define BQ27441_CONTROL_DEVICE_TYPE 0x01
64 | #define BQ27441_CONTROL_FW_VERSION 0x02
65 | #define BQ27441_CONTROL_DM_CODE 0x04
66 | #define BQ27441_CONTROL_PREV_MACWRITE 0x07
67 | #define BQ27441_CONTROL_CHEM_ID 0x08
68 | #define BQ27441_CONTROL_BAT_INSERT 0x0C
69 | #define BQ27441_CONTROL_BAT_REMOVE 0x0D
70 | #define BQ27441_CONTROL_SET_HIBERNATE 0x11
71 | #define BQ27441_CONTROL_CLEAR_HIBERNATE 0x12
72 | #define BQ27441_CONTROL_SET_CFGUPDATE 0x13
73 | #define BQ27441_CONTROL_SHUTDOWN_ENABLE 0x1B
74 | #define BQ27441_CONTROL_SHUTDOWN 0x1C
75 | #define BQ27441_CONTROL_SEALED 0x20
76 | #define BQ27441_CONTROL_PULSE_SOC_INT 0x23
77 | #define BQ27441_CONTROL_RESET 0x41
78 | #define BQ27441_CONTROL_SOFT_RESET 0x42
79 | #define BQ27441_CONTROL_EXIT_CFGUPDATE 0x43
80 | #define BQ27441_CONTROL_EXIT_RESIM 0x44
81 |
82 | ///////////////////////////////////////////
83 | // Control Status Word - Bit Definitions //
84 | ///////////////////////////////////////////
85 | // Bit positions for the 16-bit data of CONTROL_STATUS.
86 | // CONTROL_STATUS instructs the fuel gauge to return status information to
87 | // Control() addresses 0x00 and 0x01. The read-only status word contains status
88 | // bits that are set or cleared either automatically as conditions warrant or
89 | // through using specified subcommands.
90 | #define BQ27441_STATUS_SHUTDOWNEN (1<<15)
91 | #define BQ27441_STATUS_WDRESET (1<<14)
92 | #define BQ27441_STATUS_SS (1<<13)
93 | #define BQ27441_STATUS_CALMODE (1<<12)
94 | #define BQ27441_STATUS_CCA (1<<11)
95 | #define BQ27441_STATUS_BCA (1<<10)
96 | #define BQ27441_STATUS_QMAX_UP (1<<9)
97 | #define BQ27441_STATUS_RES_UP (1<<8)
98 | #define BQ27441_STATUS_INITCOMP (1<<7)
99 | #define BQ27441_STATUS_HIBERNATE (1<<6)
100 | #define BQ27441_STATUS_SLEEP (1<<4)
101 | #define BQ27441_STATUS_LDMD (1<<3)
102 | #define BQ27441_STATUS_RUP_DIS (1<<2)
103 | #define BQ27441_STATUS_VOK (1<<1)
104 |
105 | ////////////////////////////////////
106 | // Flag Command - Bit Definitions //
107 | ////////////////////////////////////
108 | // Bit positions for the 16-bit data of Flags()
109 | // This read-word function returns the contents of the fuel gauging status
110 | // register, depicting the current operating status.
111 | #define BQ27441_FLAG_OT (1<<15)
112 | #define BQ27441_FLAG_UT (1<<14)
113 | #define BQ27441_FLAG_FC (1<<9)
114 | #define BQ27441_FLAG_CHG (1<<8)
115 | #define BQ27441_FLAG_OCVTAKEN (1<<7)
116 | #define BQ27441_FLAG_ITPOR (1<<5)
117 | #define BQ27441_FLAG_CFGUPMODE (1<<4)
118 | #define BQ27441_FLAG_BAT_DET (1<<3)
119 | #define BQ27441_FLAG_SOC1 (1<<2)
120 | #define BQ27441_FLAG_SOCF (1<<1)
121 | #define BQ27441_FLAG_DSG (1<<0)
122 |
123 | ////////////////////////////
124 | // Extended Data Commands //
125 | ////////////////////////////
126 | // Extended data commands offer additional functionality beyond the standard
127 | // set of commands. They are used in the same manner; however, unlike standard
128 | // commands, extended commands are not limited to 2-byte words.
129 | #define BQ27441_EXTENDED_OPCONFIG 0x3A // OpConfig()
130 | #define BQ27441_EXTENDED_CAPACITY 0x3C // DesignCapacity()
131 | #define BQ27441_EXTENDED_DATACLASS 0x3E // DataClass()
132 | #define BQ27441_EXTENDED_DATABLOCK 0x3F // DataBlock()
133 | #define BQ27441_EXTENDED_BLOCKDATA 0x40 // BlockData()
134 | #define BQ27441_EXTENDED_CHECKSUM 0x60 // BlockDataCheckSum()
135 | #define BQ27441_EXTENDED_CONTROL 0x61 // BlockDataControl()
136 |
137 | ////////////////////////////////////////
138 | // Configuration Class, Subclass ID's //
139 | ////////////////////////////////////////
140 | // To access a subclass of the extended data, set the DataClass() function
141 | // with one of these values.
142 | // Configuration Classes
143 | #define BQ27441_ID_SAFETY 2 // Safety
144 | #define BQ27441_ID_CHG_TERMINATION 36 // Charge Termination
145 | #define BQ27441_ID_CONFIG_DATA 48 // Data
146 | #define BQ27441_ID_DISCHARGE 49 // Discharge
147 | #define BQ27441_ID_REGISTERS 64 // Registers
148 | #define BQ27441_ID_POWER 68 // Power
149 | // Gas Gauging Classes
150 | #define BQ27441_ID_IT_CFG 80 // IT Cfg
151 | #define BQ27441_ID_CURRENT_THRESH 81 // Current Thresholds
152 | #define BQ27441_ID_STATE 82 // State
153 | // Ra Tables Classes
154 | #define BQ27441_ID_R_A_RAM 89 // R_a RAM
155 | // Calibration Classes
156 | #define BQ27441_ID_CALIB_DATA 104 // Data
157 | #define BQ27441_ID_CC_CAL 105 // CC Cal
158 | #define BQ27441_ID_CURRENT 107 // Current
159 | // Security Classes
160 | #define BQ27441_ID_CODES 112 // Codes
161 |
162 | /////////////////////////////////////////
163 | // OpConfig Register - Bit Definitions //
164 | /////////////////////////////////////////
165 | // Bit positions of the OpConfig Register
166 | #define BQ27441_OPCONFIG_BIE (1<<13)
167 | #define BQ27441_OPCONFIG_BI_PU_EN (1<<12)
168 | #define BQ27441_OPCONFIG_GPIOPOL (1<<11)
169 | #define BQ27441_OPCONFIG_SLEEP (1<<5)
170 | #define BQ27441_OPCONFIG_RMFCC (1<<4)
171 | #define BQ27441_OPCONFIG_BATLOWEN (1<<2)
172 | #define BQ27441_OPCONFIG_TEMPS (1<<0)
--------------------------------------------------------------------------------
/ClosedCube_SHT31D.cpp:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Particle Photon SHT-3X-DIS Library
4 |
5 | ported from
6 |
7 | Arduino Library for Sensirion SHT3X-DIS Digital Humidity & Temperature Sensors
8 | Written by AA
9 | ---
10 |
11 | The MIT License (MIT)
12 |
13 | Copyright (c) 2015-2016 ClosedCube Limited
14 |
15 | Permission is hereby granted, free of charge, to any person obtaining a copy
16 | of this software and associated documentation files (the "Software"), to deal
17 | in the Software without restriction, including without limitation the rights
18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19 | copies of the Software, and to permit persons to whom the Software is
20 | furnished to do so, subject to the following conditions:
21 |
22 | The above copyright notice and this permission notice shall be included in
23 | all copies or substantial portions of the Software.
24 |
25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31 | THE SOFTWARE.
32 |
33 | */
34 | #include
35 | #include "ClosedCube_SHT31D.h"
36 |
37 | using namespace SHT31D_CC;
38 |
39 | ClosedCube_SHT31D::ClosedCube_SHT31D()
40 | {
41 | }
42 |
43 | SHT31D_ErrorCode ClosedCube_SHT31D::begin(uint8_t address) {
44 | SHT31D_ErrorCode error = NO_ERROR;
45 | _address = address;
46 | Wire.begin();
47 |
48 | return error;
49 | }
50 |
51 | SHT31D ClosedCube_SHT31D::periodicFetchData()
52 | {
53 | SHT31D_ErrorCode error = writeCommand(CMD_FETCH_DATA);
54 | if (error == NO_ERROR)
55 | return readTemperatureAndHumidity();
56 | else
57 | returnError(error);
58 | }
59 |
60 | SHT31D_ErrorCode ClosedCube_SHT31D::periodicStop() {
61 | return writeCommand(CMD_STOP_PERIODIC);
62 | }
63 |
64 | SHT31D_ErrorCode ClosedCube_SHT31D::periodicStart(SHT31D_Repeatability repeatability, SHT31D_Frequency frequency)
65 | {
66 | SHT31D_ErrorCode error;
67 |
68 | switch (repeatability)
69 | {
70 | case REPEATABILITY_LOW:
71 | switch (frequency)
72 | {
73 | case FREQUENCY_HZ5:
74 | error = writeCommand(CMD_PERIODIC_HALF_L);
75 | break;
76 | case FREQUENCY_1HZ:
77 | error = writeCommand(CMD_PERIODIC_1_L);
78 | break;
79 | case FREQUENCY_2HZ:
80 | error = writeCommand(CMD_PERIODIC_2_L);
81 | break;
82 | case FREQUENCY_4HZ:
83 | error = writeCommand(CMD_PERIODIC_4_L);
84 | break;
85 | case FREQUENCY_10HZ:
86 | error = writeCommand(CMD_PERIODIC_10_L);
87 | break;
88 | default:
89 | error = PARAM_WRONG_FREQUENCY;
90 | break;
91 | }
92 | break;
93 | case REPEATABILITY_MEDIUM:
94 | switch (frequency)
95 | {
96 | case FREQUENCY_HZ5:
97 | error = writeCommand(CMD_PERIODIC_HALF_M);
98 | break;
99 | case FREQUENCY_1HZ:
100 | error = writeCommand(CMD_PERIODIC_1_M);
101 | break;
102 | case FREQUENCY_2HZ:
103 | error = writeCommand(CMD_PERIODIC_2_M);
104 | break;
105 | case FREQUENCY_4HZ:
106 | error = writeCommand(CMD_PERIODIC_4_M);
107 | break;
108 | case FREQUENCY_10HZ:
109 | error = writeCommand(CMD_PERIODIC_10_M);
110 | break;
111 | default:
112 | error = PARAM_WRONG_FREQUENCY;
113 | break;
114 | }
115 | break;
116 |
117 | case REPEATABILITY_HIGH:
118 | switch (frequency)
119 | {
120 | case FREQUENCY_HZ5:
121 | error = writeCommand(CMD_PERIODIC_HALF_H);
122 | break;
123 | case FREQUENCY_1HZ:
124 | error = writeCommand(CMD_PERIODIC_1_H);
125 | break;
126 | case FREQUENCY_2HZ:
127 | error = writeCommand(CMD_PERIODIC_2_H);
128 | break;
129 | case FREQUENCY_4HZ:
130 | error = writeCommand(CMD_PERIODIC_4_H);
131 | break;
132 | case FREQUENCY_10HZ:
133 | error = writeCommand(CMD_PERIODIC_10_H);
134 | break;
135 | default:
136 | error = PARAM_WRONG_FREQUENCY;
137 | break;
138 | }
139 | break;
140 | default:
141 | error = PARAM_WRONG_REPEATABILITY;
142 | break;
143 | }
144 |
145 | return error;
146 | }
147 |
148 | SHT31D ClosedCube_SHT31D::readTempAndHumidity(SHT31D_Repeatability repeatability, SHT31D_Mode mode, uint8_t timeout)
149 | {
150 | SHT31D result;
151 |
152 | switch (mode) {
153 | case MODE_CLOCK_STRETCH:
154 | result = readTempAndHumidityClockStretch(repeatability);
155 | break;
156 | case MODE_POLLING:
157 | result = readTempAndHumidityPolling(repeatability, timeout);
158 | break;
159 | default:
160 | result = returnError(PARAM_WRONG_MODE);
161 | break;
162 | }
163 |
164 | return result;
165 | }
166 |
167 |
168 | SHT31D ClosedCube_SHT31D::readTempAndHumidityClockStretch(SHT31D_Repeatability repeatability)
169 | {
170 | SHT31D_ErrorCode error = NO_ERROR;
171 | SHT31D_Commands command;
172 |
173 | switch (repeatability)
174 | {
175 | case REPEATABILITY_LOW:
176 | error = writeCommand(CMD_CLOCK_STRETCH_L);
177 | break;
178 | case REPEATABILITY_MEDIUM:
179 | error = writeCommand(CMD_CLOCK_STRETCH_M);
180 | break;
181 | case REPEATABILITY_HIGH:
182 | error = writeCommand(CMD_CLOCK_STRETCH_H);
183 | break;
184 | default:
185 | error = PARAM_WRONG_REPEATABILITY;
186 | break;
187 | }
188 |
189 | if (error == NO_ERROR) {
190 | return readTemperatureAndHumidity();
191 | }
192 | else {
193 | return returnError(error);
194 | }
195 |
196 | }
197 |
198 |
199 | SHT31D ClosedCube_SHT31D::readTempAndHumidityPolling(SHT31D_Repeatability repeatability, uint8_t timeout)
200 | {
201 | SHT31D_ErrorCode error = NO_ERROR;
202 | SHT31D_Commands command;
203 |
204 | switch (repeatability)
205 | {
206 | case REPEATABILITY_LOW:
207 | error = writeCommand(CMD_POLLING_L);
208 | break;
209 | case REPEATABILITY_MEDIUM:
210 | error = writeCommand(CMD_POLLING_M);
211 | break;
212 | case REPEATABILITY_HIGH:
213 | error = writeCommand(CMD_POLLING_H);
214 | break;
215 | default:
216 | error = PARAM_WRONG_REPEATABILITY;
217 | break;
218 | }
219 |
220 | if (error == NO_ERROR) {
221 | return readTemperatureAndHumidity();
222 | }
223 | else {
224 | return returnError(error);
225 | }
226 |
227 | }
228 |
229 | SHT31D ClosedCube_SHT31D::readAlertHighSet() {
230 | return readAlertData(CMD_READ_ALR_LIMIT_HS);
231 | }
232 |
233 | SHT31D ClosedCube_SHT31D::readAlertHighClear() {
234 | return readAlertData(CMD_READ_ALR_LIMIT_HC);
235 | }
236 |
237 | SHT31D ClosedCube_SHT31D::readAlertLowSet() {
238 | return readAlertData(CMD_READ_ALR_LIMIT_LS);
239 | }
240 |
241 | SHT31D ClosedCube_SHT31D::readAlertLowClear() {
242 | return readAlertData(CMD_READ_ALR_LIMIT_LC);
243 | }
244 |
245 |
246 | SHT31D_ErrorCode ClosedCube_SHT31D::writeAlertHigh(float temperatureSet, float temperatureClear, float humiditySet, float humidityClear) {
247 | SHT31D_ErrorCode error = writeAlertData(CMD_WRITE_ALR_LIMIT_HS, temperatureSet, humiditySet);
248 | if (error == NO_ERROR)
249 | error = writeAlertData(CMD_WRITE_ALR_LIMIT_HC, temperatureClear, humidityClear);
250 |
251 | return error;
252 | }
253 |
254 | SHT31D_ErrorCode ClosedCube_SHT31D::writeAlertLow(float temperatureClear, float temperatureSet, float humidityClear, float humiditySet) {
255 | SHT31D_ErrorCode error = writeAlertData(CMD_WRITE_ALR_LIMIT_LS, temperatureSet, humiditySet);
256 | if (error == NO_ERROR)
257 | writeAlertData(CMD_WRITE_ALR_LIMIT_LC, temperatureClear, humidityClear);
258 |
259 | return error;
260 | }
261 |
262 | SHT31D_ErrorCode ClosedCube_SHT31D::writeAlertData(SHT31D_Commands command, float temperature, float humidity)
263 | {
264 | SHT31D_ErrorCode error;
265 |
266 | if ((humidity < 0.0) || (humidity > 100.0) || (temperature < -40.0) || (temperature > 125.0))
267 | {
268 | error = PARAM_WRONG_ALERT;
269 | }
270 | else
271 | {
272 | uint16_t rawTemperature = calculateRaWTemperature(temperature);
273 | uint16_t rawHumidity = calculateRawHumidity(humidity);
274 | uint16_t data = (rawHumidity & 0xFE00) | ((rawTemperature >> 7) & 0x001FF);
275 |
276 | uint8_t buf[2];
277 | buf[0] = data >> 8;
278 | buf[1] = data & 0xFF;
279 |
280 | uint8_t checksum = calculateCrc(buf);
281 |
282 | Wire.beginTransmission(_address);
283 | Wire.write(command >> 8);
284 | Wire.write(command & 0xFF);
285 | Wire.write(buf[0]);
286 | Wire.write(buf[1]);
287 | Wire.write(checksum);
288 | return (SHT31D_ErrorCode)(-10 * Wire.endTransmission());
289 | }
290 |
291 | return error;
292 | }
293 |
294 |
295 | SHT31D_ErrorCode ClosedCube_SHT31D::writeCommand(SHT31D_Commands command)
296 | {
297 | Wire.beginTransmission(_address);
298 | Wire.write(command >> 8);
299 | Wire.write(command & 0xFF);
300 | return (SHT31D_ErrorCode)(-10 * Wire.endTransmission());
301 | }
302 |
303 | SHT31D_ErrorCode ClosedCube_SHT31D::softReset() {
304 | return writeCommand(CMD_SOFT_RESET);
305 | }
306 |
307 | SHT31D_ErrorCode ClosedCube_SHT31D::generalCallReset() {
308 | Wire.beginTransmission(0x0);
309 | Wire.write(0x06);
310 | return (SHT31D_ErrorCode)(-10 * Wire.endTransmission());
311 | }
312 |
313 | SHT31D_ErrorCode ClosedCube_SHT31D::heaterEnable() {
314 | return writeCommand(CMD_HEATER_ENABLE);
315 | }
316 |
317 | SHT31D_ErrorCode ClosedCube_SHT31D::heaterDisable() {
318 | return writeCommand(CMD_HEATER_DISABLE);
319 | }
320 |
321 | SHT31D_ErrorCode ClosedCube_SHT31D::artEnable() {
322 | return writeCommand(CMD_ART);
323 | }
324 |
325 |
326 | uint32_t ClosedCube_SHT31D::readSerialNumber()
327 | {
328 | uint32_t result = NO_ERROR;
329 | uint16_t buf[2];
330 |
331 | if (writeCommand(CMD_READ_SERIAL_NUMBER) == NO_ERROR) {
332 | if (read(buf, 2) == NO_ERROR) {
333 | result = (buf[0] << 16) | buf[1];
334 | }
335 | }
336 |
337 | return result;
338 | }
339 |
340 | SHT31D_RegisterStatus ClosedCube_SHT31D::readStatusRegister()
341 | {
342 | SHT31D_RegisterStatus result;
343 |
344 | SHT31D_ErrorCode error = writeCommand(CMD_READ_STATUS);
345 | if (error == NO_ERROR)
346 | error = read(&result.rawData, 1);
347 |
348 | return result;
349 | }
350 |
351 | SHT31D_ErrorCode ClosedCube_SHT31D::clearAll() {
352 | return writeCommand(CMD_CLEAR_STATUS);
353 | }
354 |
355 |
356 | SHT31D ClosedCube_SHT31D::readTemperatureAndHumidity()
357 | {
358 | SHT31D result;
359 |
360 | result.t = 0;
361 | result.rh = 0;
362 |
363 | SHT31D_ErrorCode error;
364 | uint16_t buf[2];
365 |
366 | if (error == NO_ERROR)
367 | error = read(buf, 2);
368 |
369 | if (error == NO_ERROR) {
370 | result.t = calculateTemperature(buf[0]);
371 | result.rh = calculateHumidity(buf[1]);
372 | }
373 | result.error = error;
374 |
375 | return result;
376 | }
377 |
378 | SHT31D ClosedCube_SHT31D::readAlertData(SHT31D_Commands command)
379 | {
380 | SHT31D result;
381 |
382 | result.t = 0;
383 | result.rh = 0;
384 |
385 | SHT31D_ErrorCode error;
386 | uint16_t buf[1];
387 |
388 | error = writeCommand(command);
389 |
390 | if (error == NO_ERROR)
391 | error = read(buf, 1);
392 |
393 | if (error == NO_ERROR) {
394 | result.rh = calculateHumidity(buf[0] << 7);
395 | result.t = calculateTemperature(buf[0] & 0xFE00);
396 | }
397 |
398 | result.error = error;
399 |
400 | return result;
401 | }
402 |
403 | SHT31D_ErrorCode ClosedCube_SHT31D::read(uint16_t* data, uint8_t numOfPair)
404 | {
405 | uint8_t buf[2];
406 | uint8_t checksum;
407 |
408 | const uint8_t numOfBytes = numOfPair * 3;
409 |
410 | Wire.beginTransmission(_address);
411 | Wire.requestFrom(_address, numOfBytes);
412 |
413 | int counter = 0;
414 | while (Wire.available() < numOfBytes)
415 | {
416 | counter++;
417 | delay(50);
418 | if (counter > 100)
419 | return TIMEOUT_ERROR;
420 | }
421 |
422 | for (counter = 0; counter < numOfPair; counter++) {
423 | Wire.readBytes((char *)buf, (uint8_t)2);
424 | checksum = Wire.read();
425 |
426 | if (checkCrc(buf, checksum) != 0)
427 | return CRC_ERROR;
428 |
429 | data[counter] = (buf[0] << 8) | buf[1];
430 | }
431 |
432 | Wire.endTransmission();
433 |
434 | return NO_ERROR;
435 | }
436 |
437 |
438 | uint8_t ClosedCube_SHT31D::checkCrc(uint8_t data[], uint8_t checksum)
439 | {
440 | return calculateCrc(data) != checksum;
441 | }
442 |
443 | float ClosedCube_SHT31D::calculateTemperature(uint16_t rawValue)
444 | {
445 | return 175.0f * (float)rawValue / 65535.0f - 45.0f;
446 | }
447 |
448 |
449 | float ClosedCube_SHT31D::calculateHumidity(uint16_t rawValue)
450 | {
451 | return 100.0f * rawValue / 65535.0f;
452 | }
453 |
454 | uint16_t ClosedCube_SHT31D::calculateRaWTemperature(float value)
455 | {
456 | return (value + 45.0f) / 175.0f * 65535.0f;
457 | }
458 |
459 | uint16_t ClosedCube_SHT31D::calculateRawHumidity(float value)
460 | {
461 | return value / 100.0f * 65535.0f;
462 | }
463 |
464 | uint8_t ClosedCube_SHT31D::calculateCrc(uint8_t data[])
465 | {
466 | uint8_t bit;
467 | uint8_t crc = 0xFF;
468 | uint8_t dataCounter = 0;
469 |
470 | for (; dataCounter < 2; dataCounter++)
471 | {
472 | crc ^= (data[dataCounter]);
473 | for (bit = 8; bit > 0; --bit)
474 | {
475 | if (crc & 0x80)
476 | crc = (crc << 1) ^ 0x131;
477 | else
478 | crc = (crc << 1);
479 | }
480 | }
481 |
482 | return crc;
483 | }
484 |
485 | SHT31D ClosedCube_SHT31D::returnError(SHT31D_ErrorCode error) {
486 | SHT31D result;
487 | result.t = 0;
488 | result.rh = 0;
489 | result.error = error;
490 | return result;
491 | }
492 |
--------------------------------------------------------------------------------
/ClosedCube_SHT31D.h:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Particle Photon SHT-3X-DIS Library
4 |
5 | ported from
6 |
7 | Arduino Library for Sensirion SHT3X-DIS Digital Humidity & Temperature Sensors
8 | Written by AA
9 | ---
10 |
11 | The MIT License (MIT)
12 |
13 | Copyright (c) 2015-2016 ClosedCube Limited
14 |
15 | Permission is hereby granted, free of charge, to any person obtaining a copy
16 | of this software and associated documentation files (the "Software"), to deal
17 | in the Software without restriction, including without limitation the rights
18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19 | copies of the Software, and to permit persons to whom the Software is
20 | furnished to do so, subject to the following conditions:
21 |
22 | The above copyright notice and this permission notice shall be included in
23 | all copies or substantial portions of the Software.
24 |
25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31 | THE SOFTWARE.
32 |
33 | */
34 |
35 | #ifndef CLOSEDCUBE_SHT31D
36 | #define CLOSEDCUBE_SHT31D
37 |
38 | #include "application.h"
39 |
40 | namespace SHT31D_CC {
41 |
42 | typedef enum {
43 | CMD_READ_SERIAL_NUMBER = 0x3780,
44 |
45 | CMD_READ_STATUS = 0xF32D,
46 | CMD_CLEAR_STATUS = 0x3041,
47 |
48 | CMD_HEATER_ENABLE = 0x306D,
49 | CMD_HEATER_DISABLE = 0x3066,
50 |
51 | CMD_SOFT_RESET = 0x30A2,
52 |
53 | CMD_CLOCK_STRETCH_H = 0x2C06,
54 | CMD_CLOCK_STRETCH_M = 0x2C0D,
55 | CMD_CLOCK_STRETCH_L = 0x2C10,
56 |
57 | CMD_POLLING_H = 0x2400,
58 | CMD_POLLING_M = 0x240B,
59 | CMD_POLLING_L = 0x2416,
60 |
61 | CMD_ART = 0x2B32,
62 |
63 | CMD_PERIODIC_HALF_H = 0x2032,
64 | CMD_PERIODIC_HALF_M = 0x2024,
65 | CMD_PERIODIC_HALF_L = 0x202F,
66 | CMD_PERIODIC_1_H = 0x2130,
67 | CMD_PERIODIC_1_M = 0x2126,
68 | CMD_PERIODIC_1_L = 0x212D,
69 | CMD_PERIODIC_2_H = 0x2236,
70 | CMD_PERIODIC_2_M = 0x2220,
71 | CMD_PERIODIC_2_L = 0x222B,
72 | CMD_PERIODIC_4_H = 0x2334,
73 | CMD_PERIODIC_4_M = 0x2322,
74 | CMD_PERIODIC_4_L = 0x2329,
75 | CMD_PERIODIC_10_H = 0x2737,
76 | CMD_PERIODIC_10_M = 0x2721,
77 | CMD_PERIODIC_10_L = 0x272A,
78 |
79 | CMD_FETCH_DATA = 0xE000,
80 | CMD_STOP_PERIODIC = 0x3093,
81 |
82 | CMD_READ_ALR_LIMIT_LS = 0xE102,
83 | CMD_READ_ALR_LIMIT_LC = 0xE109,
84 | CMD_READ_ALR_LIMIT_HS = 0xE11F,
85 | CMD_READ_ALR_LIMIT_HC = 0xE114,
86 |
87 | CMD_WRITE_ALR_LIMIT_HS = 0x611D,
88 | CMD_WRITE_ALR_LIMIT_HC = 0x6116,
89 | CMD_WRITE_ALR_LIMIT_LC = 0x610B,
90 | CMD_WRITE_ALR_LIMIT_LS = 0x6100,
91 |
92 | CMD_NO_SLEEP = 0x303E,
93 | } SHT31D_Commands;
94 |
95 |
96 | typedef enum {
97 | REPEATABILITY_HIGH,
98 | REPEATABILITY_MEDIUM,
99 | REPEATABILITY_LOW,
100 | } SHT31D_Repeatability;
101 |
102 | typedef enum {
103 | MODE_CLOCK_STRETCH,
104 | MODE_POLLING,
105 | } SHT31D_Mode;
106 |
107 | typedef enum {
108 | FREQUENCY_HZ5,
109 | FREQUENCY_1HZ,
110 | FREQUENCY_2HZ,
111 | FREQUENCY_4HZ,
112 | FREQUENCY_10HZ
113 | } SHT31D_Frequency;
114 |
115 | typedef enum {
116 | NO_ERROR = 0,
117 |
118 | CRC_ERROR = -101,
119 | TIMEOUT_ERROR = -102,
120 |
121 | PARAM_WRONG_MODE = -501,
122 | PARAM_WRONG_REPEATABILITY = -502,
123 | PARAM_WRONG_FREQUENCY = -503,
124 | PARAM_WRONG_ALERT = -504,
125 |
126 | // Wire I2C translated error codes
127 | WIRE_I2C_DATA_TOO_LOG = -10,
128 | WIRE_I2C_RECEIVED_NACK_ON_ADDRESS = -20,
129 | WIRE_I2C_RECEIVED_NACK_ON_DATA = -30,
130 | WIRE_I2C_UNKNOW_ERROR = -40
131 | } SHT31D_ErrorCode;
132 |
133 | typedef union {
134 | uint16_t rawData;
135 | struct {
136 | uint8_t WriteDataChecksumStatus : 1;
137 | uint8_t CommandStatus : 1;
138 | uint8_t Reserved0 : 2;
139 | uint8_t SystemResetDetected : 1;
140 | uint8_t Reserved1 : 5;
141 | uint8_t T_TrackingAlert : 1;
142 | uint8_t RH_TrackingAlert : 1;
143 | uint8_t Reserved2 : 1;
144 | uint8_t HeaterStatus : 1;
145 | uint8_t Reserved3 : 1;
146 | uint8_t AlertPending : 1;
147 | };
148 | } SHT31D_RegisterStatus;
149 |
150 | struct SHT31D {
151 | float t;
152 | float rh;
153 | SHT31D_ErrorCode error;
154 | };
155 |
156 | class ClosedCube_SHT31D {
157 | public:
158 | ClosedCube_SHT31D();
159 |
160 | SHT31D_ErrorCode begin(uint8_t address);
161 | SHT31D_ErrorCode clearAll();
162 | SHT31D_RegisterStatus readStatusRegister();
163 |
164 | SHT31D_ErrorCode heaterEnable();
165 | SHT31D_ErrorCode heaterDisable();
166 |
167 | SHT31D_ErrorCode softReset();
168 | SHT31D_ErrorCode generalCallReset();
169 |
170 | SHT31D_ErrorCode artEnable();
171 |
172 | uint32_t readSerialNumber();
173 |
174 | SHT31D readTempAndHumidity(SHT31D_Repeatability repeatability, SHT31D_Mode mode, uint8_t timeout);
175 | SHT31D readTempAndHumidityClockStretch(SHT31D_Repeatability repeatability);
176 | SHT31D readTempAndHumidityPolling(SHT31D_Repeatability repeatability, uint8_t timeout);
177 |
178 | SHT31D_ErrorCode periodicStart(SHT31D_Repeatability repeatability, SHT31D_Frequency frequency);
179 | SHT31D periodicFetchData();
180 | SHT31D_ErrorCode periodicStop();
181 |
182 | SHT31D_ErrorCode writeAlertHigh(float temperatureSet, float temperatureClear, float humiditySet, float humidityClear);
183 | SHT31D readAlertHighSet();
184 | SHT31D readAlertHighClear();
185 |
186 | SHT31D_ErrorCode writeAlertLow(float temperatureClear, float temperatureSet, float humidityClear, float humiditySet);
187 | SHT31D readAlertLowSet();
188 | SHT31D readAlertLowClear();
189 |
190 |
191 | private:
192 | uint8_t _address;
193 | SHT31D_RegisterStatus _status;
194 |
195 | SHT31D_ErrorCode writeCommand(SHT31D_Commands command);
196 | SHT31D_ErrorCode writeAlertData(SHT31D_Commands command, float temperature, float humidity);
197 |
198 | uint8_t checkCrc(uint8_t data[], uint8_t checksum);
199 | uint8_t calculateCrc(uint8_t data[]);
200 |
201 | float calculateHumidity(uint16_t rawValue);
202 | float calculateTemperature(uint16_t rawValue);
203 |
204 | uint16_t calculateRawHumidity(float value);
205 | uint16_t calculateRaWTemperature(float value);
206 |
207 | SHT31D readTemperatureAndHumidity();
208 | SHT31D readAlertData(SHT31D_Commands command);
209 | SHT31D_ErrorCode read(uint16_t* data, uint8_t numOfPair);
210 |
211 | SHT31D returnError(SHT31D_ErrorCode command);
212 | };
213 | }
214 |
215 | #endif
216 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 OSBeehives, Inc.
4 |
5 | Audio portions: Copyright (c) 2016 rickkas7
6 | MMA8452 portions: credit to 2014 Jim Lindblom @ SparkFun Electronics
7 | Battery Babysitter portions: Copyright (c) 2016 SparkFun Electronics
8 | ClosedCube_SHT31D portions: Copyright (c) 2015-2016 ClosedCube Limited
9 |
10 |
11 | Permission is hereby granted, free of charge, to any person obtaining a copy
12 | of this software and associated documentation files (the "Software"), to deal
13 | in the Software without restriction, including without limitation the rights
14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | copies of the Software, and to permit persons to whom the Software is
16 | furnished to do so, subject to the following conditions:
17 |
18 | The above copyright notice and this permission notice shall be included in all
19 | copies or substantial portions of the Software.
20 |
21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | SOFTWARE.
28 |
--------------------------------------------------------------------------------
/MMA8452-xlero-OSBH.cpp:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | MMA8452-Accelerometer-Library-Spark-Core.cpp
3 | MMA8452-Accelerometer-Library-Spark-Core Library Source File
4 | Jim Lindblom @ SparkFun Electronics
5 | Original Creation Date: June 3, 2014
6 | Modified by Peter Naylor: Jan 5, 2018
7 | Original Source:
8 | https://github.com/sparkfun/MMA8452_Accelerometer
9 |
10 | This file implements all functions of the MMA8452Q class. Functions here range
11 | from higher level stuff, like reading/writing MMA8452Q registers to low-level,
12 | hardware I2C reads and writes.
13 |
14 | This code is beerware; if you see me (or any other SparkFun employee) at the
15 | local, and you've found our code helpful, please buy us a round!
16 |
17 | Distributed as-is; no warranty is given.
18 | ******************************************************************************/
19 |
20 | #include "MMA8452-xlero-OSBH.h"
21 |
22 | // CONSTRUCTUR
23 | // This function, called when you initialize the class will simply write the
24 | // supplied address into a private variable for future use.
25 | // The variable addr should be either 0x1C or 0x1D, depending on which voltage
26 | // the SA0 pin is tied to (GND or 3.3V respectively).
27 | MMA8452Q::MMA8452Q(byte addr)
28 | {
29 | address = addr; // Store address into private variable
30 | }
31 |
32 | // INITIALIZATION
33 | // This function initializes the MMA8452Q. It sets up the scale (either 2, 4,
34 | // or 8g), output data rate, portrait/landscape detection and tap detection.
35 | // It also checks the WHO_AM_I register to make sure we can communicate with
36 | // the sensor. Returns a 0 if communication failed, 1 if successful.
37 | byte MMA8452Q::init(MMA8452Q_Scale fsr, MMA8452Q_ODR odr)
38 | {
39 | scale = fsr; // Haul fsr into our class variable, scale
40 |
41 | Wire.begin(); // Initialize I2C
42 |
43 | byte c = readRegister(WHO_AM_I); // Read WHO_AM_I register
44 |
45 | if (c != 0x2A) // WHO_AM_I should always be 0x2A
46 | {
47 | return 0;
48 | }
49 |
50 | standby(); // Must be in standby to change registers
51 |
52 | setScale(scale); // Set up accelerometer scale
53 | setODR(odr); // Set up output data rate
54 | writeRegister(CTRL_REG2, 0x11); // Set MODS and SMODS to low noise low power mode (8uA)
55 | // Multiply parameter by 0.0625g to calculate threshold.
56 | setupTap(0x10, 0x10, 0x10); // Enable x, y, z set to 0.5g
57 | setupINT(); // setup interrupt INT1 for PULSE
58 | active(); // Set to active to start reading
59 |
60 | return 1;
61 | }
62 |
63 | // READ ACCELERATION DATA
64 | // This function will read the acceleration values from the MMA8452Q. After
65 | // reading, it will update two triplets of variables:
66 | // * int's x, y, and z will store the signed 12-bit values read out
67 | // of the acceleromter.
68 | // * floats cx, cy, and cz will store the calculated acceleration from
69 | // those 12-bit values. These variables are in units of g's.
70 | void MMA8452Q::read()
71 | {
72 | byte rawData[6]; // x/y/z accel register data stored here
73 |
74 | readRegisters(OUT_X_MSB, rawData, 6); // Read the six raw data registers into data array
75 |
76 | x = (rawData[0]<<8 | rawData[1]) >> 4;
77 | y = (rawData[2]<<8 | rawData[3]) >> 4;
78 | z = (rawData[4]<<8 | rawData[5]) >> 4;
79 | cx = (float) x / (float)(1<<11) * (float)(scale);
80 | cy = (float) y / (float)(1<<11) * (float)(scale);
81 | cz = (float) z / (float)(1<<11) * (float)(scale);
82 | }
83 |
84 | // CHECK IF NEW DATA IS AVAILABLE
85 | // This function checks the status of the MMA8452Q to see if new data is availble.
86 | // returns 0 if no new data is present, or a 1 if new data is available.
87 | byte MMA8452Q::available()
88 | {
89 | return (readRegister(STATUS) & 0x08) >> 3;
90 | }
91 |
92 | // SET FULL-SCALE RANGE
93 | // This function sets the full-scale range of the x, y, and z axis accelerometers.
94 | // Possible values for the fsr variable are SCALE_2G, SCALE_4G, or SCALE_8G.
95 | void MMA8452Q::setScale(MMA8452Q_Scale fsr)
96 | {
97 | // Must be in standby mode to make changes!!!
98 | byte cfg = readRegister(XYZ_DATA_CFG);
99 | cfg &= 0xFC; // Mask out scale bits
100 | cfg |= (fsr >> 2); // Neat trick, see page 22. 00 = 2G, 01 = 4A, 10 = 8G
101 | writeRegister(XYZ_DATA_CFG, cfg);
102 | }
103 |
104 | // SET THE OUTPUT DATA RATE
105 | // This function sets the output data rate of the MMA8452Q.
106 | // Possible values for the odr parameter are: ODR_800, ODR_400, ODR_200,
107 | // ODR_100, ODR_50, ODR_12, ODR_6, or ODR_1
108 | void MMA8452Q::setODR(MMA8452Q_ODR odr)
109 | {
110 | // Must be in standby mode to make changes!!!
111 | byte ctrl = readRegister(CTRL_REG1);
112 | ctrl &= 0xCF; // Mask out data rate bits
113 | ctrl |= (odr << 3);
114 | writeRegister(CTRL_REG1, ctrl);
115 | }
116 |
117 | // SET UP TAP DETECTION
118 | // This function can set up tap detection on the x, y, and/or z axes.
119 | // The xThs, yThs, and zThs parameters serve two functions:
120 | // 1. Enable tap detection on an axis. If the 7th bit is SET (0x80)
121 | // tap detection on that axis will be DISABLED.
122 | // 2. Set tap g's threshold. The lower 7 bits will set the tap threshold
123 | // on that axis.
124 | void MMA8452Q::setupTap(byte xThs, byte yThs, byte zThs)
125 | {
126 | // Set up single and double tap - 5 steps:
127 | // for more info check out this app note:
128 | // http://cache.freescale.com/files/sensors/doc/app_note/AN4072.pdf
129 | // Set the threshold - minimum required acceleration to cause a tap.
130 | byte temp = 0;
131 | if (!(xThs & 0x80)) // If top bit ISN'T set
132 | {
133 | temp |= 0x3; // Enable taps on x
134 | writeRegister(PULSE_THSX, xThs); // x thresh
135 | }
136 | if (!(yThs & 0x80))
137 | {
138 | temp |= 0xC; // Enable taps on y
139 | writeRegister(PULSE_THSY, yThs); // y thresh
140 | }
141 | if (!(zThs & 0x80))
142 | {
143 | temp |= 0x30; // Enable taps on z
144 | writeRegister(PULSE_THSZ, zThs); // z thresh
145 | }
146 | // Set up single and/or double tap detection on each axis individually.
147 | writeRegister(PULSE_CFG, temp | 0xFF);
148 | // Set the time limit - the maximum time that a tap can be above the thresh
149 | writeRegister(PULSE_TMLT, 0x30); // 30ms time limit at 800Hz odr
150 | // Set the pulse latency - the minimum required time between pulses
151 | writeRegister(PULSE_LTCY, 0x40); // 200ms (at 800Hz odr) between taps min
152 | // Set the second pulse window - maximum allowed time between end of
153 | // latency and start of second pulse
154 | writeRegister(PULSE_WIND, 0xFF); // 5. 318ms (max value) between taps max
155 | }
156 |
157 | // READ TAP STATUS
158 | // This function returns any taps read by the MMA8452Q. If the function
159 | // returns no new taps were detected. Otherwise the function will return the
160 | // lower 7 bits of the PULSE_SRC register. Clears interrupt flag in INT_SOURCE.
161 | byte MMA8452Q::readTap()
162 | {
163 | byte tapStat = readRegister(PULSE_SRC);
164 | if (tapStat & 0x80) // Read EA bit to check if a interrupt was generated
165 | {
166 | return tapStat & 0x7F;
167 | }
168 | else
169 | return 0;
170 | }
171 |
172 | // SET UP INTERRUPT on INT1
173 | // This function sets up portrait and landscape detection.
174 | void MMA8452Q::setupINT()
175 | {
176 | // Must be in standby mode to make changes!!!
177 | // For more info check out this app note:
178 | // http://cache.freescale.com/files/sensors/doc/app_note/AN4068.pdf
179 | // 1. Enable SRC_PULSE
180 | writeRegister(CTRL_REG3, 0x02); // Set IPOL (enable), active HIGH for INT pin
181 | writeRegister(CTRL_REG4, 0x08); // Set SRC_PULSE (enable), cleared when PULSE_SRC is read
182 | writeRegister(CTRL_REG5, 0x08); // Set INT_CFG_PULSE (enable), interrupt source is directed to INT1 pin
183 | }
184 |
185 | // SET STANDBY MODE
186 | // Sets the MMA8452 to standby mode. It must be in standby to change most register settings
187 | void MMA8452Q::standby()
188 | {
189 | byte c = readRegister(CTRL_REG1);
190 | writeRegister(CTRL_REG1, c & ~(0x01)); //Clear the active bit to go into standby
191 | }
192 |
193 | // SET ACTIVE MODE
194 | // Sets the MMA8452 to active mode. Needs to be in this mode to output data
195 | void MMA8452Q::active()
196 | {
197 | byte c = readRegister(CTRL_REG1);
198 | writeRegister(CTRL_REG1, c | 0x01); //Set the active bit to begin detection
199 | }
200 |
201 | // WRITE A SINGLE REGISTER
202 | // Write a single byte of data to a register in the MMA8452Q.
203 | void MMA8452Q::writeRegister(MMA8452Q_Register reg, byte data)
204 | {
205 | writeRegisters(reg, &data, 1);
206 | }
207 |
208 | // WRITE MULTIPLE REGISTERS
209 | // Write an array of "len" bytes ("buffer"), starting at register "reg", and
210 | // auto-incrmenting to the next.
211 | void MMA8452Q::writeRegisters(MMA8452Q_Register reg, byte *buffer, byte len)
212 | {
213 | Wire.beginTransmission(address);
214 | Wire.write(reg);
215 | for (int x = 0; x < len; x++)
216 | Wire.write(buffer[x]);
217 | Wire.endTransmission(); //Stop transmitting
218 | }
219 |
220 | // READ A SINGLE REGISTER
221 | // Read a byte from the MMA8452Q register "reg".
222 | byte MMA8452Q::readRegister(MMA8452Q_Register reg)
223 | {
224 | Wire.beginTransmission(address);
225 | Wire.write(reg);
226 | Wire.endTransmission(false); //endTransmission but keep the connection active
227 |
228 | Wire.requestFrom(address, (byte) 1); //Ask for 1 byte, once done, bus is released by default
229 |
230 | while(!Wire.available()) ; //Wait for the data to come back
231 |
232 | return Wire.read(); //Return this one byte
233 | }
234 |
235 | // READ MULTIPLE REGISTERS
236 | // Read "len" bytes from the MMA8452Q, starting at register "reg". Bytes are stored
237 | // in "buffer" on exit.
238 | void MMA8452Q::readRegisters(MMA8452Q_Register reg, byte *buffer, byte len)
239 | {
240 | Wire.beginTransmission(address);
241 | Wire.write(reg);
242 | Wire.endTransmission(false); //endTransmission but keep the connection active
243 |
244 | Wire.requestFrom(address, len); //Ask for bytes, once done, bus is released by default
245 |
246 | while(Wire.available() < len); //Hang out until we get the # of bytes we expect
247 |
248 | for(int x = 0 ; x < len ; x++)
249 | buffer[x] = Wire.read();
250 | }
--------------------------------------------------------------------------------
/MMA8452-xlero-OSBH.h:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | MMA8452-Accelerometer-Library-Spark-Core.h
3 | MMA8452-Accelerometer-Library-Spark-Core Library Header File
4 | Jim Lindblom @ SparkFun Electronics
5 | Original Creation Date: June 3, 2014
6 | Modified by Peter Naylor: Jan 5, 2018
7 | Original Source:
8 | https://github.com/sparkfun/MMA8452_Accelerometer
9 |
10 | This file prototypes the MMA8452Q class, implemented in SFE_MMA8452Q.cpp. In
11 | addition, it defines every register in the MMA8452Q.
12 |
13 | Development environment specifics:
14 | IDE: Arduino 1.0.5
15 | Hardware Platform: Arduino Uno
16 |
17 | This code is beerware; if you see me (or any other SparkFun employee) at the
18 | local, and you've found our code helpful, please buy us a round!
19 |
20 | Distributed as-is; no warranty is given.
21 | ******************************************************************************/
22 |
23 | #ifndef MMA8452-xlero_OSBH_h
24 | #define MMA8452-xlero_OSBH_h
25 |
26 | #include "application.h" // Needed for Spark Core
27 |
28 |
29 | ///////////////////////////////////
30 | // MMA8452Q Register Definitions //
31 | ///////////////////////////////////
32 | enum MMA8452Q_Register {
33 | STATUS = 0x00,
34 | OUT_X_MSB = 0x01,
35 | OUT_X_LSB = 0x02,
36 | OUT_Y_MSB = 0x03,
37 | OUT_Y_LSB = 0x04,
38 | OUT_Z_MSB = 0x05,
39 | OUT_Z_LSB = 0x06,
40 | SYSMOD = 0x0B,
41 | INT_SOURCE = 0x0C,
42 | WHO_AM_I = 0x0D,
43 | XYZ_DATA_CFG = 0x0E,
44 | HP_FILTER_CUTOFF = 0x0F,
45 | PL_STATUS = 0x10,
46 | PL_CFG = 0x11,
47 | PL_COUNT = 0x12,
48 | PL_BF_ZCOMP = 0x13,
49 | P_L_THS_REG = 0x14,
50 | FF_MT_CFG = 0x15,
51 | FF_MT_SRC = 0x16,
52 | FF_MT_THS = 0x17,
53 | FF_MT_COUNT = 0x18,
54 | TRANSIENT_CFG = 0x1D,
55 | TRANSIENT_SRC = 0x1E,
56 | TRANSIENT_THS = 0x1F,
57 | TRANSIENT_COUNT = 0x20,
58 | PULSE_CFG = 0x21,
59 | PULSE_SRC = 0x22,
60 | PULSE_THSX = 0x23,
61 | PULSE_THSY = 0x24,
62 | PULSE_THSZ = 0x25,
63 | PULSE_TMLT = 0x26,
64 | PULSE_LTCY = 0x27,
65 | PULSE_WIND = 0x28,
66 | ASLP_COUNT = 0x29,
67 | CTRL_REG1 = 0x2A,
68 | CTRL_REG2 = 0x2B,
69 | CTRL_REG3 = 0x2C,
70 | CTRL_REG4 = 0x2D,
71 | CTRL_REG5 = 0x2E,
72 | OFF_X = 0x2F,
73 | OFF_Y = 0x30,
74 | OFF_Z = 0x31
75 | };
76 |
77 | ////////////////////////////////
78 | // MMA8452Q Misc Declarations //
79 | ////////////////////////////////
80 | enum MMA8452Q_Scale {SCALE_2G = 2, SCALE_4G = 4, SCALE_8G = 8}; // Possible full-scale settings
81 | enum MMA8452Q_ODR {ODR_800, ODR_400, ODR_200, ODR_100, ODR_50, ODR_12, ODR_6, ODR_1}; // possible data rates
82 | // Possible portrait/landscape settings
83 | #define PORTRAIT_U 0
84 | #define PORTRAIT_D 1
85 | #define LANDSCAPE_R 2
86 | #define LANDSCAPE_L 3
87 | #define LOCKOUT 0x40
88 |
89 | ////////////////////////////////
90 | // MMA8452Q Class Declaration //
91 | ////////////////////////////////
92 | class MMA8452Q
93 | {
94 | public:
95 | MMA8452Q(byte addr = 0x1C); // Constructor
96 |
97 | byte init(MMA8452Q_Scale fsr = SCALE_2G, MMA8452Q_ODR odr = ODR_800);
98 | void read();
99 | byte available();
100 | byte readTap();
101 |
102 | int x, y, z;
103 | float cx, cy, cz;
104 | private:
105 | byte address;
106 | MMA8452Q_Scale scale;
107 |
108 | void standby();
109 | void active();
110 | void setupINT();
111 | void setupTap(byte xThs, byte yThs, byte zThs);
112 | void setScale(MMA8452Q_Scale fsr);
113 | void setODR(MMA8452Q_ODR odr);
114 | void writeRegister(MMA8452Q_Register reg, byte data);
115 | void writeRegisters(MMA8452Q_Register reg, byte *buffer, byte len);
116 | byte readRegister(MMA8452Q_Register reg);
117 | void readRegisters(MMA8452Q_Register reg, byte *buffer, byte len);
118 | };
119 |
120 | #endif
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # BzBoard_BETA
2 | beta-release of development firmware for BuzzzBoard PCBA
3 |
4 |
5 | ## Use
6 |
7 | See license file.
8 |
9 |
10 | ## Change Log
11 |
12 | ### v2
13 | - updated audio server URL
14 | ### v1
15 | - Initial release
16 |
17 |
18 |
19 | ## Local Build
20 |
21 | - Save to new folder.
22 |
23 | - Build using Particle CLI command 'particle compile photon . --saveTo firmware.bin'
24 |
25 | - Upload 'firmware.bin' to Photon using USB cable and DFU mode:
26 |
27 | - hold SETUP button on RESET until blinking YELLOW
28 |
29 | - Send 'particle flash --usb firmware.bin'
30 |
31 |
--------------------------------------------------------------------------------
/SparkFunBQ27441.cpp:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | SparkFunBQ27441.cpp
3 | BQ27441 Arduino Library Main Source File
4 | Jim Lindblom @ SparkFun Electronics
5 | May 9, 2016
6 | https://github.com/sparkfun/SparkFun_BQ27441_Arduino_Library
7 |
8 | Implementation of all features of the BQ27441 LiPo Fuel Gauge.
9 |
10 | Particle Port: Peter Naylor
11 |
12 | Hardware Resources:
13 | - Arduino Development Board
14 | - SparkFun Battery Babysitter
15 |
16 | Development environment specifics:
17 | Arduino 1.6.7
18 | SparkFun Battery Babysitter v1.0
19 | Arduino Uno (any 'duino should do)
20 | ******************************************************************************/
21 |
22 | #include "application.h"
23 | #include "SparkFunBQ27441.h"
24 | #include "BQ27441_Definitions.h"
25 |
26 | /*****************************************************************************
27 | ************************** Initialization Functions *************************
28 | *****************************************************************************/
29 | // Initializes class variables
30 | BQ27441::BQ27441() : _deviceAddress(BQ72441_I2C_ADDRESS), _sealFlag(false), _userConfigControl(false)
31 | {
32 | }
33 |
34 | // Initializes I2C and verifies communication with the BQ27441.
35 | bool BQ27441::begin(void)
36 | {
37 | uint16_t deviceID = 0;
38 |
39 | Wire.begin(); // Initialize I2C master
40 |
41 | deviceID = deviceType(); // Read deviceType from BQ27441
42 |
43 | if (deviceID == BQ27441_DEVICE_ID)
44 | {
45 | return true; // If device ID is valid, return true
46 | }
47 |
48 | return false; // Otherwise return false
49 | }
50 |
51 | // Configures the design capacity of the connected battery.
52 | bool BQ27441::setCapacity(uint16_t capacity)
53 | {
54 | // Write to STATE subclass (82) of BQ27441 extended memory.
55 | // Offset 0x0A (10)
56 | // Design capacity is a 2-byte piece of data - MSB first
57 | uint8_t capMSB = capacity >> 8;
58 | uint8_t capLSB = capacity & 0x00FF;
59 | uint8_t capacityData[2] = {capMSB, capLSB};
60 | return writeExtendedData(BQ27441_ID_STATE, 10, capacityData, 2);
61 | }
62 |
63 | /*****************************************************************************
64 | ********************** Battery Characteristics Functions ********************
65 | *****************************************************************************/
66 |
67 | // Reads and returns the battery voltage
68 | uint16_t BQ27441::voltage(void)
69 | {
70 | return readWord(BQ27441_COMMAND_VOLTAGE);
71 | }
72 |
73 | // Reads and returns the specified current measurement
74 | int16_t BQ27441::current(current_measure type)
75 | {
76 | int16_t current = 0;
77 | switch (type)
78 | {
79 | case AVG:
80 | current = (int16_t) readWord(BQ27441_COMMAND_AVG_CURRENT);
81 | break;
82 | case STBY:
83 | current = (int16_t) readWord(BQ27441_COMMAND_STDBY_CURRENT);
84 | break;
85 | case MAX:
86 | current = (int16_t) readWord(BQ27441_COMMAND_MAX_CURRENT);
87 | break;
88 | }
89 |
90 | return current;
91 | }
92 |
93 | // Reads and returns the specified capacity measurement
94 | uint16_t BQ27441::capacity(capacity_measure type)
95 | {
96 | uint16_t capacity = 0;
97 | switch (type)
98 | {
99 | case REMAIN:
100 | return readWord(BQ27441_COMMAND_REM_CAPACITY);
101 | break;
102 | case FULL:
103 | return readWord(BQ27441_COMMAND_FULL_CAPACITY);
104 | break;
105 | case AVAIL:
106 | capacity = readWord(BQ27441_COMMAND_NOM_CAPACITY);
107 | break;
108 | case AVAIL_FULL:
109 | capacity = readWord(BQ27441_COMMAND_AVAIL_CAPACITY);
110 | break;
111 | case REMAIN_F:
112 | capacity = readWord(BQ27441_COMMAND_REM_CAP_FIL);
113 | break;
114 | case REMAIN_UF:
115 | capacity = readWord(BQ27441_COMMAND_REM_CAP_UNFL);
116 | break;
117 | case FULL_F:
118 | capacity = readWord(BQ27441_COMMAND_FULL_CAP_FIL);
119 | break;
120 | case FULL_UF:
121 | capacity = readWord(BQ27441_COMMAND_FULL_CAP_UNFL);
122 | break;
123 | case DESIGN:
124 | capacity = readWord(BQ27441_EXTENDED_CAPACITY);
125 | }
126 |
127 | return capacity;
128 | }
129 |
130 | // Reads and returns measured average power
131 | int16_t BQ27441::power(void)
132 | {
133 | return (int16_t) readWord(BQ27441_COMMAND_AVG_POWER);
134 | }
135 |
136 | // Reads and returns specified state of charge measurement
137 | uint16_t BQ27441::soc(soc_measure type)
138 | {
139 | uint16_t socRet = 0;
140 | switch (type)
141 | {
142 | case FILTERED:
143 | socRet = readWord(BQ27441_COMMAND_SOC);
144 | break;
145 | case UNFILTERED:
146 | socRet = readWord(BQ27441_COMMAND_SOC_UNFL);
147 | break;
148 | }
149 |
150 | return socRet;
151 | }
152 |
153 | // Reads and returns specified state of health measurement
154 | uint8_t BQ27441::soh(soh_measure type)
155 | {
156 | uint16_t sohRaw = readWord(BQ27441_COMMAND_SOH);
157 | uint8_t sohStatus = sohRaw >> 8;
158 | uint8_t sohPercent = sohRaw & 0x00FF;
159 |
160 | if (type == PERCENT)
161 | return sohPercent;
162 | else
163 | return sohStatus;
164 | }
165 |
166 | // Reads and returns specified temperature measurement
167 | uint16_t BQ27441::temperature(temp_measure type)
168 | {
169 | uint16_t temp = 0;
170 | switch (type)
171 | {
172 | case BATTERY:
173 | temp = readWord(BQ27441_COMMAND_TEMP);
174 | break;
175 | case INTERNAL_TEMP:
176 | temp = readWord(BQ27441_COMMAND_INT_TEMP);
177 | break;
178 | }
179 | return temp;
180 | }
181 |
182 | /*****************************************************************************
183 | ************************** GPOUT Control Functions **************************
184 | *****************************************************************************/
185 | // Get GPOUT polarity setting (active-high or active-low)
186 | bool BQ27441::GPOUTPolarity(void)
187 | {
188 | uint16_t opConfigRegister = opConfig();
189 |
190 | return (opConfigRegister & BQ27441_OPCONFIG_GPIOPOL);
191 | }
192 |
193 | // Set GPOUT polarity to active-high or active-low
194 | bool BQ27441::setGPOUTPolarity(bool activeHigh)
195 | {
196 | uint16_t oldOpConfig = opConfig();
197 |
198 | // Check to see if we need to update opConfig:
199 | if ((activeHigh && (oldOpConfig & BQ27441_OPCONFIG_GPIOPOL)) ||
200 | (!activeHigh && !(oldOpConfig & BQ27441_OPCONFIG_GPIOPOL)))
201 | return true;
202 |
203 | uint16_t newOpConfig = oldOpConfig;
204 | if (activeHigh)
205 | newOpConfig |= BQ27441_OPCONFIG_GPIOPOL;
206 | else
207 | newOpConfig &= ~(BQ27441_OPCONFIG_GPIOPOL);
208 |
209 | return writeOpConfig(newOpConfig);
210 | }
211 |
212 | // Get GPOUT function (BAT_LOW or SOC_INT)
213 | bool BQ27441::GPOUTFunction(void)
214 | {
215 | uint16_t opConfigRegister = opConfig();
216 |
217 | return (opConfigRegister & BQ27441_OPCONFIG_BATLOWEN);
218 | }
219 |
220 | // Set GPOUT function to BAT_LOW or SOC_INT
221 | bool BQ27441::setGPOUTFunction(gpout_function function)
222 | {
223 | uint16_t oldOpConfig = opConfig();
224 |
225 | // Check to see if we need to update opConfig:
226 | if ((function && (oldOpConfig & BQ27441_OPCONFIG_BATLOWEN)) ||
227 | (!function && !(oldOpConfig & BQ27441_OPCONFIG_BATLOWEN)))
228 | return true;
229 |
230 | // Modify BATLOWN_EN bit of opConfig:
231 | uint16_t newOpConfig = oldOpConfig;
232 | if (function)
233 | newOpConfig |= BQ27441_OPCONFIG_BATLOWEN;
234 | else
235 | newOpConfig &= ~(BQ27441_OPCONFIG_BATLOWEN);
236 |
237 | // Write new opConfig
238 | return writeOpConfig(newOpConfig);
239 | }
240 |
241 | // Get SOC1_Set Threshold - threshold to set the alert flag
242 | uint8_t BQ27441::SOC1SetThreshold(void)
243 | {
244 | return readExtendedData(BQ27441_ID_DISCHARGE, 0);
245 | }
246 |
247 | // Get SOC1_Clear Threshold - threshold to clear the alert flag
248 | uint8_t BQ27441::SOC1ClearThreshold(void)
249 | {
250 | return readExtendedData(BQ27441_ID_DISCHARGE, 1);
251 | }
252 |
253 | // Set the SOC1 set and clear thresholds to a percentage
254 | bool BQ27441::setSOC1Thresholds(uint8_t set, uint8_t clear)
255 | {
256 | uint8_t thresholds[2];
257 | thresholds[0] = constrain(set, 0, 100);
258 | thresholds[1] = constrain(clear, 0, 100);
259 | return writeExtendedData(BQ27441_ID_DISCHARGE, 0, thresholds, 2);
260 | }
261 |
262 | // Get SOCF_Set Threshold - threshold to set the alert flag
263 | uint8_t BQ27441::SOCFSetThreshold(void)
264 | {
265 | return readExtendedData(BQ27441_ID_DISCHARGE, 2);
266 | }
267 |
268 | // Get SOCF_Clear Threshold - threshold to clear the alert flag
269 | uint8_t BQ27441::SOCFClearThreshold(void)
270 | {
271 | return readExtendedData(BQ27441_ID_DISCHARGE, 3);
272 | }
273 |
274 | // Set the SOCF set and clear thresholds to a percentage
275 | bool BQ27441::setSOCFThresholds(uint8_t set, uint8_t clear)
276 | {
277 | uint8_t thresholds[2];
278 | thresholds[0] = constrain(set, 0, 100);
279 | thresholds[1] = constrain(clear, 0, 100);
280 | return writeExtendedData(BQ27441_ID_DISCHARGE, 2, thresholds, 2);
281 | }
282 |
283 | // Check if the SOC1 flag is set
284 | bool BQ27441::socFlag(void)
285 | {
286 | uint16_t flagState = flags();
287 |
288 | return flagState & BQ27441_FLAG_SOC1;
289 | }
290 |
291 | // Check if the SOCF flag is set
292 | bool BQ27441::socfFlag(void)
293 | {
294 | uint16_t flagState = flags();
295 |
296 | return flagState & BQ27441_FLAG_SOCF;
297 |
298 | }
299 |
300 | // Get the SOC_INT interval delta
301 | uint8_t BQ27441::sociDelta(void)
302 | {
303 | return readExtendedData(BQ27441_ID_STATE, 26);
304 | }
305 |
306 | // Set the SOC_INT interval delta to a value between 1 and 100
307 | bool BQ27441::setSOCIDelta(uint8_t delta)
308 | {
309 | uint8_t soci = constrain(delta, 0, 100);
310 | return writeExtendedData(BQ27441_ID_STATE, 26, &soci, 1);
311 | }
312 |
313 | // Pulse the GPOUT pin - must be in SOC_INT mode
314 | bool BQ27441::pulseGPOUT(void)
315 | {
316 | return executeControlWord(BQ27441_CONTROL_PULSE_SOC_INT);
317 | }
318 |
319 | /*****************************************************************************
320 | *************************** Control Sub-Commands ****************************
321 | *****************************************************************************/
322 |
323 | // Read the device type - should be 0x0421
324 | uint16_t BQ27441::deviceType(void)
325 | {
326 | return readControlWord(BQ27441_CONTROL_DEVICE_TYPE);
327 | }
328 |
329 | // Enter configuration mode - set userControl if calling from an Arduino sketch
330 | // and you want control over when to exitConfig
331 | bool BQ27441::enterConfig(bool userControl)
332 | {
333 | if (userControl) _userConfigControl = true;
334 |
335 | if (sealed())
336 | {
337 | _sealFlag = true;
338 | unseal(); // Must be unsealed before making changes
339 | }
340 |
341 | if (executeControlWord(BQ27441_CONTROL_SET_CFGUPDATE))
342 | {
343 | int16_t timeout = BQ72441_I2C_TIMEOUT;
344 | while ((timeout--) && (!(status() & BQ27441_FLAG_CFGUPMODE)))
345 | delay(1);
346 |
347 | if (timeout > 0)
348 | return true;
349 | }
350 |
351 | return false;
352 | }
353 |
354 | // Exit configuration mode with the option to perform a resimulation
355 | bool BQ27441::exitConfig(bool resim)
356 | {
357 | // There are two methods for exiting config mode:
358 | // 1. Execute the EXIT_CFGUPDATE command
359 | // 2. Execute the SOFT_RESET command
360 | // EXIT_CFGUPDATE exits config mode _without_ an OCV (open-circuit voltage)
361 | // measurement, and without resimulating to update unfiltered-SoC and SoC.
362 | // If a new OCV measurement or resimulation is desired, SOFT_RESET or
363 | // EXIT_RESIM should be used to exit config mode.
364 | if (resim)
365 | {
366 | if (softReset())
367 | {
368 | int16_t timeout = BQ72441_I2C_TIMEOUT;
369 | while ((timeout--) && ((flags() & BQ27441_FLAG_CFGUPMODE)))
370 | delay(1);
371 | if (timeout > 0)
372 | {
373 | if (_sealFlag) seal(); // Seal back up if we IC was sealed coming in
374 | return true;
375 | }
376 | }
377 | return false;
378 | }
379 | else
380 | {
381 | return executeControlWord(BQ27441_CONTROL_EXIT_CFGUPDATE);
382 | }
383 | }
384 |
385 | // Read the flags() command
386 | uint16_t BQ27441::flags(void)
387 | {
388 | return readWord(BQ27441_COMMAND_FLAGS);
389 | }
390 |
391 | // Read the CONTROL_STATUS subcommand of control()
392 | uint16_t BQ27441::status(void)
393 | {
394 | return readControlWord(BQ27441_CONTROL_STATUS);
395 | }
396 |
397 | /***************************** Private Functions *****************************/
398 |
399 | // Check if the BQ27441-G1A is sealed or not.
400 | bool BQ27441::sealed(void)
401 | {
402 | uint16_t stat = status();
403 | return stat & BQ27441_STATUS_SS;
404 | }
405 |
406 | // Seal the BQ27441-G1A
407 | bool BQ27441::seal(void)
408 | {
409 | return readControlWord(BQ27441_CONTROL_SEALED);
410 | }
411 |
412 | // UNseal the BQ27441-G1A
413 | bool BQ27441::unseal(void)
414 | {
415 | // To unseal the BQ27441, write the key to the control
416 | // command. Then immediately write the same key to control again.
417 | if (readControlWord(BQ27441_UNSEAL_KEY))
418 | {
419 | return readControlWord(BQ27441_UNSEAL_KEY);
420 | }
421 | return false;
422 | }
423 |
424 | // Read the 16-bit opConfig register from extended data
425 | uint16_t BQ27441::opConfig(void)
426 | {
427 | return readWord(BQ27441_EXTENDED_OPCONFIG);
428 | }
429 |
430 | // Write the 16-bit opConfig register in extended data
431 | bool BQ27441::writeOpConfig(uint16_t value)
432 | {
433 | uint8_t opConfigMSB = value >> 8;
434 | uint8_t opConfigLSB = value & 0x00FF;
435 | uint8_t opConfigData[2] = {opConfigMSB, opConfigLSB};
436 |
437 | // OpConfig register location: BQ27441_ID_REGISTERS id, offset 0
438 | return writeExtendedData(BQ27441_ID_REGISTERS, 0, opConfigData, 2);
439 | }
440 |
441 | // Issue a soft-reset to the BQ27441-G1A
442 | bool BQ27441::softReset(void)
443 | {
444 | return executeControlWord(BQ27441_CONTROL_SOFT_RESET);
445 | }
446 |
447 | // Read a 16-bit command word from the BQ27441-G1A
448 | uint16_t BQ27441::readWord(uint16_t subAddress)
449 | {
450 | uint8_t data[2];
451 | i2cReadBytes(subAddress, data, 2);
452 | return ((uint16_t) data[1] << 8) | data[0];
453 | }
454 |
455 | // Read a 16-bit subcommand() from the BQ27441-G1A's control()
456 | uint16_t BQ27441::readControlWord(uint16_t function)
457 | {
458 | uint8_t subCommandMSB = (function >> 8);
459 | uint8_t subCommandLSB = (function & 0x00FF);
460 | uint8_t command[2] = {subCommandLSB, subCommandMSB};
461 | uint8_t data[2] = {0, 0};
462 |
463 | i2cWriteBytes((uint8_t) 0, command, 2);
464 |
465 | if (i2cReadBytes((uint8_t) 0, data, 2))
466 | {
467 | return ((uint16_t)data[1] << 8) | data[0];
468 | }
469 |
470 | return false;
471 | }
472 |
473 | // Execute a subcommand() from the BQ27441-G1A's control()
474 | bool BQ27441::executeControlWord(uint16_t function)
475 | {
476 | uint8_t subCommandMSB = (function >> 8);
477 | uint8_t subCommandLSB = (function & 0x00FF);
478 | uint8_t command[2] = {subCommandLSB, subCommandMSB};
479 | uint8_t data[2] = {0, 0};
480 |
481 | if (i2cWriteBytes((uint8_t) 0, command, 2))
482 | return true;
483 |
484 | return false;
485 | }
486 |
487 | /*****************************************************************************
488 | ************************** Extended Data Commands ***************************
489 | *****************************************************************************/
490 |
491 | // Issue a BlockDataControl() command to enable BlockData access
492 | bool BQ27441::blockDataControl(void)
493 | {
494 | uint8_t enableByte = 0x00;
495 | return i2cWriteBytes(BQ27441_EXTENDED_CONTROL, &enableByte, 1);
496 | }
497 |
498 | // Issue a DataClass() command to set the data class to be accessed
499 | bool BQ27441::blockDataClass(uint8_t id)
500 | {
501 | return i2cWriteBytes(BQ27441_EXTENDED_DATACLASS, &id, 1);
502 | }
503 |
504 | // Issue a DataBlock() command to set the data block to be accessed
505 | bool BQ27441::blockDataOffset(uint8_t offset)
506 | {
507 | return i2cWriteBytes(BQ27441_EXTENDED_DATABLOCK, &offset, 1);
508 | }
509 |
510 | // Read the current checksum using BlockDataCheckSum()
511 | uint8_t BQ27441::blockDataChecksum(void)
512 | {
513 | uint8_t csum;
514 | i2cReadBytes(BQ27441_EXTENDED_CHECKSUM, &csum, 1);
515 | return csum;
516 | }
517 |
518 | // Use BlockData() to read a byte from the loaded extended data
519 | uint8_t BQ27441::readBlockData(uint8_t offset)
520 | {
521 | uint8_t ret;
522 | uint8_t address = offset + BQ27441_EXTENDED_BLOCKDATA;
523 | i2cReadBytes(address, &ret, 1);
524 | return ret;
525 | }
526 |
527 | // Use BlockData() to write a byte to an offset of the loaded data
528 | bool BQ27441::writeBlockData(uint8_t offset, uint8_t data)
529 | {
530 | uint8_t address = offset + BQ27441_EXTENDED_BLOCKDATA;
531 | return i2cWriteBytes(address, &data, 1);
532 | }
533 |
534 | // Read all 32 bytes of the loaded extended data and compute a
535 | // checksum based on the values.
536 | uint8_t BQ27441::computeBlockChecksum(void)
537 | {
538 | uint8_t data[32];
539 | i2cReadBytes(BQ27441_EXTENDED_BLOCKDATA, data, 32);
540 |
541 | uint8_t csum = 0;
542 | for (int i=0; i<32; i++)
543 | {
544 | csum += data[i];
545 | }
546 | csum = 255 - csum;
547 |
548 | return csum;
549 | }
550 |
551 | // Use the BlockDataCheckSum() command to write a checksum value
552 | bool BQ27441::writeBlockChecksum(uint8_t csum)
553 | {
554 | return i2cWriteBytes(BQ27441_EXTENDED_CHECKSUM, &csum, 1);
555 | }
556 |
557 | // Read a byte from extended data specifying a class ID and position offset
558 | uint8_t BQ27441::readExtendedData(uint8_t classID, uint8_t offset)
559 | {
560 | uint8_t retData = 0;
561 | if (!_userConfigControl) enterConfig(false);
562 |
563 | if (!blockDataControl()) // // enable block data memory control
564 | return false; // Return false if enable fails
565 | if (!blockDataClass(classID)) // Write class ID using DataBlockClass()
566 | return false;
567 |
568 | blockDataOffset(offset / 32); // Write 32-bit block offset (usually 0)
569 |
570 | computeBlockChecksum(); // Compute checksum going in
571 | uint8_t oldCsum = blockDataChecksum();
572 | /*for (int i=0; i<32; i++)
573 | Serial.print(String(readBlockData(i)) + " ");*/
574 | retData = readBlockData(offset % 32); // Read from offset (limit to 0-31)
575 |
576 | if (!_userConfigControl) exitConfig();
577 |
578 | return retData;
579 | }
580 |
581 | // Write a specified number of bytes to extended data specifying a
582 | // class ID, position offset.
583 | bool BQ27441::writeExtendedData(uint8_t classID, uint8_t offset, uint8_t * data, uint8_t len)
584 | {
585 | if (len > 32)
586 | return false;
587 |
588 | if (!_userConfigControl) enterConfig(false);
589 |
590 | if (!blockDataControl()) // // enable block data memory control
591 | return false; // Return false if enable fails
592 | if (!blockDataClass(classID)) // Write class ID using DataBlockClass()
593 | return false;
594 |
595 | blockDataOffset(offset / 32); // Write 32-bit block offset (usually 0)
596 | computeBlockChecksum(); // Compute checksum going in
597 | uint8_t oldCsum = blockDataChecksum();
598 |
599 | // Write data bytes:
600 | for (int i = 0; i < len; i++)
601 | {
602 | // Write to offset, mod 32 if offset is greater than 32
603 | // The blockDataOffset above sets the 32-bit block
604 | writeBlockData((offset % 32) + i, data[i]);
605 | }
606 |
607 | // Write new checksum using BlockDataChecksum (0x60)
608 | uint8_t newCsum = computeBlockChecksum(); // Compute the new checksum
609 | writeBlockChecksum(newCsum);
610 |
611 | if (!_userConfigControl) exitConfig();
612 |
613 | return true;
614 | }
615 |
616 | /*****************************************************************************
617 | ************************ I2C Read and Write Routines ************************
618 | *****************************************************************************/
619 |
620 | // Read a specified number of bytes over I2C at a given subAddress
621 | int16_t BQ27441::i2cReadBytes(uint8_t subAddress, uint8_t * dest, uint8_t count)
622 | {
623 | int16_t timeout = BQ72441_I2C_TIMEOUT;
624 | Wire.beginTransmission(_deviceAddress);
625 | Wire.write(subAddress);
626 | Wire.endTransmission(true);
627 |
628 | Wire.requestFrom(_deviceAddress, count);
629 | while ((Wire.available() < count) && timeout--)
630 | delay(1);
631 | if (timeout)
632 | {
633 | for (int i=0; i0 indicates charging.
114 | */
115 | int16_t current(current_measure type = AVG);
116 |
117 | /**
118 | Reads and returns the specified capacity measurement
119 |
120 | @param capacity_measure enum specifying capacity value to be read
121 | @return specified capacity measurement in mAh.
122 | */
123 | uint16_t capacity(capacity_measure type = REMAIN);
124 |
125 | /**
126 | Reads and returns measured average power
127 |
128 | @return average power in mAh. >0 indicates charging.
129 | */
130 | int16_t power(void);
131 |
132 | /**
133 | Reads and returns specified state of charge measurement
134 |
135 | @param soc_measure enum specifying filtered or unfiltered measurement
136 | @return specified state of charge measurement in %
137 | */
138 | uint16_t soc(soc_measure type = FILTERED);
139 |
140 | /**
141 | Reads and returns specified state of health measurement
142 |
143 | @param soh_measure enum specifying filtered or unfiltered measurement
144 | @return specified state of health measurement in %, or status bits
145 | */
146 | uint8_t soh(soh_measure type = PERCENT);
147 |
148 | /**
149 | Reads and returns specified temperature measurement
150 |
151 | @param temp_measure enum specifying internal or battery measurement
152 | @return specified temperature measurement in degrees C
153 | */
154 | uint16_t temperature(temp_measure type = BATTERY);
155 |
156 | ////////////////////////////
157 | // GPOUT Control Commands //
158 | ////////////////////////////
159 | /**
160 | Get GPOUT polarity setting (active-high or active-low)
161 |
162 | @return true if active-high, false if active-low
163 | */
164 | bool GPOUTPolarity(void);
165 |
166 | /**
167 | Set GPOUT polarity to active-high or active-low
168 |
169 | @param activeHigh is true if active-high, false if active-low
170 | @return true on success
171 | */
172 | bool setGPOUTPolarity(bool activeHigh);
173 |
174 | /**
175 | Get GPOUT function (BAT_LOW or SOC_INT)
176 |
177 | @return true if BAT_LOW or false if SOC_INT
178 | */
179 | bool GPOUTFunction(void);
180 |
181 | /**
182 | Set GPOUT function to BAT_LOW or SOC_INT
183 |
184 | @param function should be either BAT_LOW or SOC_INT
185 | @return true on success
186 | */
187 | bool setGPOUTFunction(gpout_function function);
188 |
189 | /**
190 | Get SOC1_Set Threshold - threshold to set the alert flag
191 |
192 | @return state of charge value between 0 and 100%
193 | */
194 | uint8_t SOC1SetThreshold(void);
195 |
196 | /**
197 | Get SOC1_Clear Threshold - threshold to clear the alert flag
198 |
199 | @return state of charge value between 0 and 100%
200 | */
201 | uint8_t SOC1ClearThreshold(void);
202 |
203 | /**
204 | Set the SOC1 set and clear thresholds to a percentage
205 |
206 | @param set and clear percentages between 0 and 100. clear > set.
207 | @return true on success
208 | */
209 | bool setSOC1Thresholds(uint8_t set, uint8_t clear);
210 |
211 | /**
212 | Get SOCF_Set Threshold - threshold to set the alert flag
213 |
214 | @return state of charge value between 0 and 100%
215 | */
216 | uint8_t SOCFSetThreshold(void);
217 |
218 | /**
219 | Get SOCF_Clear Threshold - threshold to clear the alert flag
220 |
221 | @return state of charge value between 0 and 100%
222 | */
223 | uint8_t SOCFClearThreshold(void);
224 |
225 | /**
226 | Set the SOCF set and clear thresholds to a percentage
227 |
228 | @param set and clear percentages between 0 and 100. clear > set.
229 | @return true on success
230 | */
231 | bool setSOCFThresholds(uint8_t set, uint8_t clear);
232 |
233 | /**
234 | Check if the SOC1 flag is set in flags()
235 |
236 | @return true if flag is set
237 | */
238 | bool socFlag(void);
239 |
240 | /**
241 | Check if the SOCF flag is set in flags()
242 |
243 | @return true if flag is set
244 | */
245 | bool socfFlag(void);
246 |
247 | /**
248 | Get the SOC_INT interval delta
249 |
250 | @return interval percentage value between 1 and 100
251 | */
252 | uint8_t sociDelta(void);
253 |
254 | /**
255 | Set the SOC_INT interval delta to a value between 1 and 100
256 |
257 | @param interval percentage value between 1 and 100
258 | @return true on success
259 | */
260 | bool setSOCIDelta(uint8_t delta);
261 |
262 | /**
263 | Pulse the GPOUT pin - must be in SOC_INT mode
264 |
265 | @return true on success
266 | */
267 | bool pulseGPOUT(void);
268 |
269 | //////////////////////////
270 | // Control Sub-commands //
271 | //////////////////////////
272 |
273 | /**
274 | Read the device type - should be 0x0421
275 |
276 | @return 16-bit value read from DEVICE_TYPE subcommand
277 | */
278 | uint16_t deviceType(void);
279 |
280 | /**
281 | Enter configuration mode - set userControl if calling from an Arduino
282 | sketch and you want control over when to exitConfig.
283 |
284 | @param userControl is true if the Arduino sketch is handling entering
285 | and exiting config mode (should be false in library calls).
286 | @return true on success
287 | */
288 | bool enterConfig(bool userControl = true);
289 |
290 | /**
291 | Exit configuration mode with the option to perform a resimulation
292 |
293 | @param resim is true if resimulation should be performed after exiting
294 | @return true on success
295 | */
296 | bool exitConfig(bool resim = true);
297 |
298 | /**
299 | Read the flags() command
300 |
301 | @return 16-bit representation of flags() command register
302 | */
303 | uint16_t flags(void);
304 |
305 | /**
306 | Read the CONTROL_STATUS subcommand of control()
307 |
308 | @return 16-bit representation of CONTROL_STATUS subcommand
309 | */
310 | uint16_t status(void);
311 |
312 | private:
313 | uint8_t _deviceAddress; // Stores the BQ27441-G1A's I2C address
314 | bool _sealFlag; // Global to identify that IC was previously sealed
315 | bool _userConfigControl; // Global to identify that user has control over
316 | // entering/exiting config
317 |
318 | /**
319 | Check if the BQ27441-G1A is sealed or not.
320 |
321 | @return true if the chip is sealed
322 | */
323 | bool sealed(void);
324 |
325 | /**
326 | Seal the BQ27441-G1A
327 |
328 | @return true on success
329 | */
330 | bool seal(void);
331 |
332 | /**
333 | UNseal the BQ27441-G1A
334 |
335 | @return true on success
336 | */
337 | bool unseal(void);
338 |
339 | /**
340 | Read the 16-bit opConfig register from extended data
341 |
342 | @return opConfig register contents
343 | */
344 | uint16_t opConfig(void);
345 |
346 | /**
347 | Write the 16-bit opConfig register in extended data
348 |
349 | @param New 16-bit value for opConfig
350 | @return true on success
351 | */
352 | bool writeOpConfig(uint16_t value);
353 |
354 | /**
355 | Issue a soft-reset to the BQ27441-G1A
356 |
357 | @return true on success
358 | */
359 | bool softReset(void);
360 |
361 | /**
362 | Read a 16-bit command word from the BQ27441-G1A
363 |
364 | @param subAddress is the command to be read from
365 | @return 16-bit value of the command's contents
366 | */
367 | uint16_t readWord(uint16_t subAddress);
368 |
369 | /**
370 | Read a 16-bit subcommand() from the BQ27441-G1A's control()
371 |
372 | @param function is the subcommand of control() to be read
373 | @return 16-bit value of the subcommand's contents
374 | */
375 | uint16_t readControlWord(uint16_t function);
376 |
377 | /**
378 | Execute a subcommand() from the BQ27441-G1A's control()
379 |
380 | @param function is the subcommand of control() to be executed
381 | @return true on success
382 | */
383 | bool executeControlWord(uint16_t function);
384 |
385 | ////////////////////////////
386 | // Extended Data Commands //
387 | ////////////////////////////
388 | /**
389 | Issue a BlockDataControl() command to enable BlockData access
390 |
391 | @return true on success
392 | */
393 | bool blockDataControl(void);
394 |
395 | /**
396 | Issue a DataClass() command to set the data class to be accessed
397 |
398 | @param id is the id number of the class
399 | @return true on success
400 | */
401 | bool blockDataClass(uint8_t id);
402 |
403 | /**
404 | Issue a DataBlock() command to set the data block to be accessed
405 |
406 | @param offset of the data block
407 | @return true on success
408 | */
409 | bool blockDataOffset(uint8_t offset);
410 |
411 | /**
412 | Read the current checksum using BlockDataCheckSum()
413 |
414 | @return true on success
415 | */
416 | uint8_t blockDataChecksum(void);
417 |
418 | /**
419 | Use BlockData() to read a byte from the loaded extended data
420 |
421 | @param offset of data block byte to be read
422 | @return true on success
423 | */
424 | uint8_t readBlockData(uint8_t offset);
425 |
426 | /**
427 | Use BlockData() to write a byte to an offset of the loaded data
428 |
429 | @param offset is the position of the byte to be written
430 | data is the value to be written
431 | @return true on success
432 | */
433 | bool writeBlockData(uint8_t offset, uint8_t data);
434 |
435 | /**
436 | Read all 32 bytes of the loaded extended data and compute a
437 | checksum based on the values.
438 |
439 | @return 8-bit checksum value calculated based on loaded data
440 | */
441 | uint8_t computeBlockChecksum(void);
442 |
443 | /**
444 | Use the BlockDataCheckSum() command to write a checksum value
445 |
446 | @param csum is the 8-bit checksum to be written
447 | @return true on success
448 | */
449 | bool writeBlockChecksum(uint8_t csum);
450 |
451 | /**
452 | Read a byte from extended data specifying a class ID and position offset
453 |
454 | @param classID is the id of the class to be read from
455 | offset is the byte position of the byte to be read
456 | @return 8-bit value of specified data
457 | */
458 | uint8_t readExtendedData(uint8_t classID, uint8_t offset);
459 |
460 | /**
461 | Write a specified number of bytes to extended data specifying a
462 | class ID, position offset.
463 |
464 | @param classID is the id of the class to be read from
465 | offset is the byte position of the byte to be read
466 | data is the data buffer to be written
467 | len is the number of bytes to be written
468 | @return true on success
469 | */
470 | bool writeExtendedData(uint8_t classID, uint8_t offset, uint8_t * data, uint8_t len);
471 |
472 | /////////////////////////////////
473 | // I2C Read and Write Routines //
474 | /////////////////////////////////
475 |
476 | /**
477 | Read a specified number of bytes over I2C at a given subAddress
478 |
479 | @param subAddress is the 8-bit address of the data to be read
480 | dest is the data buffer to be written to
481 | count is the number of bytes to be read
482 | @return true on success
483 | */
484 | int16_t i2cReadBytes(uint8_t subAddress, uint8_t * dest, uint8_t count);
485 |
486 | /**
487 | Write a specified number of bytes over I2C to a given subAddress
488 |
489 | @param subAddress is the 8-bit address of the data to be written to
490 | src is the data buffer to be written
491 | count is the number of bytes to be written
492 | @return true on success
493 | */
494 | uint16_t i2cWriteBytes(uint8_t subAddress, uint8_t * src, uint8_t count);
495 | };
496 |
497 | extern BQ27441 lipo; // Use lipo.[] to interact with the library in an Arduino sketch
498 | // Thanks for reading!
499 |
500 | #endif
501 |
--------------------------------------------------------------------------------
/binary.h:
--------------------------------------------------------------------------------
1 | //Original code from: http://code.google.com/p/arduino/source/browse/trunk/hardware/arduino/cores/arduino/binary.h
2 |
3 | #ifndef Binary_h
4 | #define Binary_h
5 |
6 | #define B0 0
7 | #define B00 0
8 | #define B000 0
9 | #define B0000 0
10 | #define B00000 0
11 | #define B000000 0
12 | #define B0000000 0
13 | #define B00000000 0
14 | #define B1 1
15 | #define B01 1
16 | #define B001 1
17 | #define B0001 1
18 | #define B00001 1
19 | #define B000001 1
20 | #define B0000001 1
21 | #define B00000001 1
22 | #define B10 2
23 | #define B010 2
24 | #define B0010 2
25 | #define B00010 2
26 | #define B000010 2
27 | #define B0000010 2
28 | #define B00000010 2
29 | #define B11 3
30 | #define B011 3
31 | #define B0011 3
32 | #define B00011 3
33 | #define B000011 3
34 | #define B0000011 3
35 | #define B00000011 3
36 | #define B100 4
37 | #define B0100 4
38 | #define B00100 4
39 | #define B000100 4
40 | #define B0000100 4
41 | #define B00000100 4
42 | #define B101 5
43 | #define B0101 5
44 | #define B00101 5
45 | #define B000101 5
46 | #define B0000101 5
47 | #define B00000101 5
48 | #define B110 6
49 | #define B0110 6
50 | #define B00110 6
51 | #define B000110 6
52 | #define B0000110 6
53 | #define B00000110 6
54 | #define B111 7
55 | #define B0111 7
56 | #define B00111 7
57 | #define B000111 7
58 | #define B0000111 7
59 | #define B00000111 7
60 | #define B1000 8
61 | #define B01000 8
62 | #define B001000 8
63 | #define B0001000 8
64 | #define B00001000 8
65 | #define B1001 9
66 | #define B01001 9
67 | #define B001001 9
68 | #define B0001001 9
69 | #define B00001001 9
70 | #define B1010 10
71 | #define B01010 10
72 | #define B001010 10
73 | #define B0001010 10
74 | #define B00001010 10
75 | #define B1011 11
76 | #define B01011 11
77 | #define B001011 11
78 | #define B0001011 11
79 | #define B00001011 11
80 | #define B1100 12
81 | #define B01100 12
82 | #define B001100 12
83 | #define B0001100 12
84 | #define B00001100 12
85 | #define B1101 13
86 | #define B01101 13
87 | #define B001101 13
88 | #define B0001101 13
89 | #define B00001101 13
90 | #define B1110 14
91 | #define B01110 14
92 | #define B001110 14
93 | #define B0001110 14
94 | #define B00001110 14
95 | #define B1111 15
96 | #define B01111 15
97 | #define B001111 15
98 | #define B0001111 15
99 | #define B00001111 15
100 | #define B10000 16
101 | #define B010000 16
102 | #define B0010000 16
103 | #define B00010000 16
104 | #define B10001 17
105 | #define B010001 17
106 | #define B0010001 17
107 | #define B00010001 17
108 | #define B10010 18
109 | #define B010010 18
110 | #define B0010010 18
111 | #define B00010010 18
112 | #define B10011 19
113 | #define B010011 19
114 | #define B0010011 19
115 | #define B00010011 19
116 | #define B10100 20
117 | #define B010100 20
118 | #define B0010100 20
119 | #define B00010100 20
120 | #define B10101 21
121 | #define B010101 21
122 | #define B0010101 21
123 | #define B00010101 21
124 | #define B10110 22
125 | #define B010110 22
126 | #define B0010110 22
127 | #define B00010110 22
128 | #define B10111 23
129 | #define B010111 23
130 | #define B0010111 23
131 | #define B00010111 23
132 | #define B11000 24
133 | #define B011000 24
134 | #define B0011000 24
135 | #define B00011000 24
136 | #define B11001 25
137 | #define B011001 25
138 | #define B0011001 25
139 | #define B00011001 25
140 | #define B11010 26
141 | #define B011010 26
142 | #define B0011010 26
143 | #define B00011010 26
144 | #define B11011 27
145 | #define B011011 27
146 | #define B0011011 27
147 | #define B00011011 27
148 | #define B11100 28
149 | #define B011100 28
150 | #define B0011100 28
151 | #define B00011100 28
152 | #define B11101 29
153 | #define B011101 29
154 | #define B0011101 29
155 | #define B00011101 29
156 | #define B11110 30
157 | #define B011110 30
158 | #define B0011110 30
159 | #define B00011110 30
160 | #define B11111 31
161 | #define B011111 31
162 | #define B0011111 31
163 | #define B00011111 31
164 | #define B100000 32
165 | #define B0100000 32
166 | #define B00100000 32
167 | #define B100001 33
168 | #define B0100001 33
169 | #define B00100001 33
170 | #define B100010 34
171 | #define B0100010 34
172 | #define B00100010 34
173 | #define B100011 35
174 | #define B0100011 35
175 | #define B00100011 35
176 | #define B100100 36
177 | #define B0100100 36
178 | #define B00100100 36
179 | #define B100101 37
180 | #define B0100101 37
181 | #define B00100101 37
182 | #define B100110 38
183 | #define B0100110 38
184 | #define B00100110 38
185 | #define B100111 39
186 | #define B0100111 39
187 | #define B00100111 39
188 | #define B101000 40
189 | #define B0101000 40
190 | #define B00101000 40
191 | #define B101001 41
192 | #define B0101001 41
193 | #define B00101001 41
194 | #define B101010 42
195 | #define B0101010 42
196 | #define B00101010 42
197 | #define B101011 43
198 | #define B0101011 43
199 | #define B00101011 43
200 | #define B101100 44
201 | #define B0101100 44
202 | #define B00101100 44
203 | #define B101101 45
204 | #define B0101101 45
205 | #define B00101101 45
206 | #define B101110 46
207 | #define B0101110 46
208 | #define B00101110 46
209 | #define B101111 47
210 | #define B0101111 47
211 | #define B00101111 47
212 | #define B110000 48
213 | #define B0110000 48
214 | #define B00110000 48
215 | #define B110001 49
216 | #define B0110001 49
217 | #define B00110001 49
218 | #define B110010 50
219 | #define B0110010 50
220 | #define B00110010 50
221 | #define B110011 51
222 | #define B0110011 51
223 | #define B00110011 51
224 | #define B110100 52
225 | #define B0110100 52
226 | #define B00110100 52
227 | #define B110101 53
228 | #define B0110101 53
229 | #define B00110101 53
230 | #define B110110 54
231 | #define B0110110 54
232 | #define B00110110 54
233 | #define B110111 55
234 | #define B0110111 55
235 | #define B00110111 55
236 | #define B111000 56
237 | #define B0111000 56
238 | #define B00111000 56
239 | #define B111001 57
240 | #define B0111001 57
241 | #define B00111001 57
242 | #define B111010 58
243 | #define B0111010 58
244 | #define B00111010 58
245 | #define B111011 59
246 | #define B0111011 59
247 | #define B00111011 59
248 | #define B111100 60
249 | #define B0111100 60
250 | #define B00111100 60
251 | #define B111101 61
252 | #define B0111101 61
253 | #define B00111101 61
254 | #define B111110 62
255 | #define B0111110 62
256 | #define B00111110 62
257 | #define B111111 63
258 | #define B0111111 63
259 | #define B00111111 63
260 | #define B1000000 64
261 | #define B01000000 64
262 | #define B1000001 65
263 | #define B01000001 65
264 | #define B1000010 66
265 | #define B01000010 66
266 | #define B1000011 67
267 | #define B01000011 67
268 | #define B1000100 68
269 | #define B01000100 68
270 | #define B1000101 69
271 | #define B01000101 69
272 | #define B1000110 70
273 | #define B01000110 70
274 | #define B1000111 71
275 | #define B01000111 71
276 | #define B1001000 72
277 | #define B01001000 72
278 | #define B1001001 73
279 | #define B01001001 73
280 | #define B1001010 74
281 | #define B01001010 74
282 | #define B1001011 75
283 | #define B01001011 75
284 | #define B1001100 76
285 | #define B01001100 76
286 | #define B1001101 77
287 | #define B01001101 77
288 | #define B1001110 78
289 | #define B01001110 78
290 | #define B1001111 79
291 | #define B01001111 79
292 | #define B1010000 80
293 | #define B01010000 80
294 | #define B1010001 81
295 | #define B01010001 81
296 | #define B1010010 82
297 | #define B01010010 82
298 | #define B1010011 83
299 | #define B01010011 83
300 | #define B1010100 84
301 | #define B01010100 84
302 | #define B1010101 85
303 | #define B01010101 85
304 | #define B1010110 86
305 | #define B01010110 86
306 | #define B1010111 87
307 | #define B01010111 87
308 | #define B1011000 88
309 | #define B01011000 88
310 | #define B1011001 89
311 | #define B01011001 89
312 | #define B1011010 90
313 | #define B01011010 90
314 | #define B1011011 91
315 | #define B01011011 91
316 | #define B1011100 92
317 | #define B01011100 92
318 | #define B1011101 93
319 | #define B01011101 93
320 | #define B1011110 94
321 | #define B01011110 94
322 | #define B1011111 95
323 | #define B01011111 95
324 | #define B1100000 96
325 | #define B01100000 96
326 | #define B1100001 97
327 | #define B01100001 97
328 | #define B1100010 98
329 | #define B01100010 98
330 | #define B1100011 99
331 | #define B01100011 99
332 | #define B1100100 100
333 | #define B01100100 100
334 | #define B1100101 101
335 | #define B01100101 101
336 | #define B1100110 102
337 | #define B01100110 102
338 | #define B1100111 103
339 | #define B01100111 103
340 | #define B1101000 104
341 | #define B01101000 104
342 | #define B1101001 105
343 | #define B01101001 105
344 | #define B1101010 106
345 | #define B01101010 106
346 | #define B1101011 107
347 | #define B01101011 107
348 | #define B1101100 108
349 | #define B01101100 108
350 | #define B1101101 109
351 | #define B01101101 109
352 | #define B1101110 110
353 | #define B01101110 110
354 | #define B1101111 111
355 | #define B01101111 111
356 | #define B1110000 112
357 | #define B01110000 112
358 | #define B1110001 113
359 | #define B01110001 113
360 | #define B1110010 114
361 | #define B01110010 114
362 | #define B1110011 115
363 | #define B01110011 115
364 | #define B1110100 116
365 | #define B01110100 116
366 | #define B1110101 117
367 | #define B01110101 117
368 | #define B1110110 118
369 | #define B01110110 118
370 | #define B1110111 119
371 | #define B01110111 119
372 | #define B1111000 120
373 | #define B01111000 120
374 | #define B1111001 121
375 | #define B01111001 121
376 | #define B1111010 122
377 | #define B01111010 122
378 | #define B1111011 123
379 | #define B01111011 123
380 | #define B1111100 124
381 | #define B01111100 124
382 | #define B1111101 125
383 | #define B01111101 125
384 | #define B1111110 126
385 | #define B01111110 126
386 | #define B1111111 127
387 | #define B01111111 127
388 | #define B10000000 128
389 | #define B10000001 129
390 | #define B10000010 130
391 | #define B10000011 131
392 | #define B10000100 132
393 | #define B10000101 133
394 | #define B10000110 134
395 | #define B10000111 135
396 | #define B10001000 136
397 | #define B10001001 137
398 | #define B10001010 138
399 | #define B10001011 139
400 | #define B10001100 140
401 | #define B10001101 141
402 | #define B10001110 142
403 | #define B10001111 143
404 | #define B10010000 144
405 | #define B10010001 145
406 | #define B10010010 146
407 | #define B10010011 147
408 | #define B10010100 148
409 | #define B10010101 149
410 | #define B10010110 150
411 | #define B10010111 151
412 | #define B10011000 152
413 | #define B10011001 153
414 | #define B10011010 154
415 | #define B10011011 155
416 | #define B10011100 156
417 | #define B10011101 157
418 | #define B10011110 158
419 | #define B10011111 159
420 | #define B10100000 160
421 | #define B10100001 161
422 | #define B10100010 162
423 | #define B10100011 163
424 | #define B10100100 164
425 | #define B10100101 165
426 | #define B10100110 166
427 | #define B10100111 167
428 | #define B10101000 168
429 | #define B10101001 169
430 | #define B10101010 170
431 | #define B10101011 171
432 | #define B10101100 172
433 | #define B10101101 173
434 | #define B10101110 174
435 | #define B10101111 175
436 | #define B10110000 176
437 | #define B10110001 177
438 | #define B10110010 178
439 | #define B10110011 179
440 | #define B10110100 180
441 | #define B10110101 181
442 | #define B10110110 182
443 | #define B10110111 183
444 | #define B10111000 184
445 | #define B10111001 185
446 | #define B10111010 186
447 | #define B10111011 187
448 | #define B10111100 188
449 | #define B10111101 189
450 | #define B10111110 190
451 | #define B10111111 191
452 | #define B11000000 192
453 | #define B11000001 193
454 | #define B11000010 194
455 | #define B11000011 195
456 | #define B11000100 196
457 | #define B11000101 197
458 | #define B11000110 198
459 | #define B11000111 199
460 | #define B11001000 200
461 | #define B11001001 201
462 | #define B11001010 202
463 | #define B11001011 203
464 | #define B11001100 204
465 | #define B11001101 205
466 | #define B11001110 206
467 | #define B11001111 207
468 | #define B11010000 208
469 | #define B11010001 209
470 | #define B11010010 210
471 | #define B11010011 211
472 | #define B11010100 212
473 | #define B11010101 213
474 | #define B11010110 214
475 | #define B11010111 215
476 | #define B11011000 216
477 | #define B11011001 217
478 | #define B11011010 218
479 | #define B11011011 219
480 | #define B11011100 220
481 | #define B11011101 221
482 | #define B11011110 222
483 | #define B11011111 223
484 | #define B11100000 224
485 | #define B11100001 225
486 | #define B11100010 226
487 | #define B11100011 227
488 | #define B11100100 228
489 | #define B11100101 229
490 | #define B11100110 230
491 | #define B11100111 231
492 | #define B11101000 232
493 | #define B11101001 233
494 | #define B11101010 234
495 | #define B11101011 235
496 | #define B11101100 236
497 | #define B11101101 237
498 | #define B11101110 238
499 | #define B11101111 239
500 | #define B11110000 240
501 | #define B11110001 241
502 | #define B11110010 242
503 | #define B11110011 243
504 | #define B11110100 244
505 | #define B11110101 245
506 | #define B11110110 246
507 | #define B11110111 247
508 | #define B11111000 248
509 | #define B11111001 249
510 | #define B11111010 250
511 | #define B11111011 251
512 | #define B11111100 252
513 | #define B11111101 253
514 | #define B11111110 254
515 | #define B11111111 255
516 |
517 | #endif
--------------------------------------------------------------------------------
/downsampler.cpp:
--------------------------------------------------------------------------------
1 | #include "downsampler.h"
2 | #include
3 |
4 | //Constructor
5 | Downsampler::Downsampler(int ratio)
6 | {
7 | //Float vectors for storing coefficients
8 | vector> coeffs;
9 | vector sos(6);
10 |
11 | if (ratio==5)
12 | {
13 | sos={(float)0.24523728, 0.24523728, 0.000000e+00, 1.000000e+00, -0.50952545, 0.000000e+00};
14 | }
15 | else if(ratio==10){
16 | sos={(float)0.02008337, 0.04016673, 0.02008337, 1.0, -1.56101808,0.64135154};
17 | }
18 | else if(ratio==20){
19 | sos={(float)0.00554272, 0.01108543, 0.00554272, 1.0, -1.77863178,0.80080265};
20 | }
21 | else if(ratio==50){
22 | sos={(float)9.44691844e-04, 1.88938369e-03, 9.44691844e-04, 1.00000000e+00, -1.91119707e+00, 9.14975835e-01};
23 | }
24 | else if(ratio==100){
25 | sos={(float)2.41359049e-04, 4.82718098e-04, 2.41359049e-04,1.00000000e+00, -1.95557824e+00, 9.56543677e-01};
26 | }
27 |
28 | coeffs.push_back(sos);
29 | f= new Filter(coeffs, 1.0);;
30 | this->ratio=ratio;
31 | this->ready=false;
32 | this->lastSample=0;
33 | this->sampleCount=ratio-2;
34 | }
35 |
36 | //Update feature extractor with new sample
37 |
38 | void Downsampler::update (float value)
39 | {
40 | lastSample=f->filter(value);
41 |
42 | sampleCount+=1;
43 | //If sample count has reached window length, normalize, set flag as ready and reset sample count
44 | if(sampleCount>=ratio)
45 | {
46 | ready=true;
47 | sampleCount=0;
48 | }
49 |
50 | }
51 |
52 | //Return ready state
53 | bool Downsampler::isReady()
54 | {
55 | return ready;
56 | }
57 |
58 |
59 | //Return sample
60 | float Downsampler::getSample()
61 | {
62 | ready=false;
63 | return lastSample;
64 | }
65 |
66 |
--------------------------------------------------------------------------------
/downsampler.h:
--------------------------------------------------------------------------------
1 | //#include
2 | #include
3 | #include "filter.h"
4 | using namespace std;
5 |
6 | //feature Extractor class. Computes energy for each stored filter
7 |
8 | class Downsampler {
9 |
10 | //Filter
11 | Filter* f;
12 |
13 | //downsampling ratio
14 | int ratio;
15 |
16 | //updated sample count
17 | int sampleCount;
18 |
19 | //ready flag
20 | bool ready;
21 |
22 | //last sample
23 | float lastSample;
24 |
25 | public:
26 | Downsampler (int ratio);
27 | void update(float value);
28 | bool isReady();
29 | float getSample();
30 |
31 | };
32 |
33 |
--------------------------------------------------------------------------------
/filter.cpp:
--------------------------------------------------------------------------------
1 | #include "filter.h"
2 |
3 |
4 | //Constructor
5 | Filter::Filter (vector > coeffs, float g){
6 | this->g=g;
7 | this->coeffs=coeffs;
8 | this->size=coeffs.size();
9 | this->z.resize(size);
10 |
11 | //Set z to 0
12 | for (int i=0; iz[i].begin(), this->z[i].end(), 0);
16 | }
17 | }
18 |
19 |
20 | //Filter function based on sequential 2nd order section filtering (Direct Form II)
21 | //coeffs structure:
22 | //b01 b11 b21 1 a11 a21
23 | //b02 b12 b22 1 a12 a22
24 | //...
25 | float Filter::filter (float x)
26 | {
27 | float y=g*x;
28 |
29 | float v=0;
30 |
31 | //for each 2nd order section
32 | for (int f=0; f
2 | #include
3 | using namespace std;
4 |
5 | #ifndef _FILTER_
6 | #define _FILTER_
7 |
8 |
9 | //Simple bandpass filter
10 |
11 | class Filter
12 | {
13 |
14 | //Number of second order sections
15 | int size;
16 |
17 |
18 | //Filter coefficients, organized in second order sections:
19 | //b01 b11 b21 1 a11 a21
20 | //b02 b12 b22 1 a12 a22
21 | vector > coeffs;
22 |
23 | //Filter gain
24 | float g;
25 |
26 | //Buffers
27 | vector > z;
28 |
29 | public:
30 | Filter (vector > coeffs, float g);
31 | float filter (float);
32 |
33 | };
34 |
35 | #endif
36 |
--------------------------------------------------------------------------------
/fw19_OS.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opensourcebeehives/BuzzBoard_firmware/bd5be0be936579e5d149e35586315f8e1a2da116/fw19_OS.bin
--------------------------------------------------------------------------------
/geolocate.cpp:
--------------------------------------------------------------------------------
1 | #include "Particle.h"
2 | #include "google-maps-device-locator.h"
3 |
4 | void locationCallback(float lat, float lon, float accuracy);
5 |
6 | SerialLogHandler logHandler;
7 | GoogleMapsDeviceLocator locator;
8 |
9 | SYSTEM_THREAD(ENABLED);
10 |
11 |
12 | void setup() {
13 | Serial.begin(9600);
14 | // locator.withLocatePeriodic(60).withEventName("geoLocate").withSubscribe(locationCallback);
15 | locator.withEventName("geoLocate").withSubscribe(locationCallback);
16 | }
17 |
18 |
19 | void loop() {
20 | locator.loop();
21 |
22 | delay(3000);
23 | locator.publishLocation();
24 | delay(1000);
25 | }
26 |
27 | void locationCallback(float lat, float lon, float accuracy) {
28 | Serial.printlnf("lat=%f lon=%f accuracy=%f", lat, lon, accuracy);
29 | }
30 |
--------------------------------------------------------------------------------
/google-maps-device-locator.cpp:
--------------------------------------------------------------------------------
1 | #include "Particle.h"
2 | #include "google-maps-device-locator.h"
3 |
4 | #if Wiring_Cellular
5 | # include "CellularHelper.h"
6 | #endif
7 |
8 | static char requestBuf[256];
9 | static char *requestCur;
10 | static int numAdded = 0;
11 |
12 | GoogleMapsDeviceLocator::GoogleMapsDeviceLocator() : locatorMode(LOCATOR_MODE_MANUAL), periodMs(10000), eventName("deviceLocator"),
13 | stateTime(0), state(CONNECT_WAIT_STATE), callback(NULL), waitAfterConnect(8000) {
14 |
15 | }
16 |
17 | GoogleMapsDeviceLocator::~GoogleMapsDeviceLocator() {
18 |
19 | }
20 |
21 | GoogleMapsDeviceLocator &GoogleMapsDeviceLocator::withLocateOnce() {
22 | locatorMode = LOCATOR_MODE_ONCE;
23 | return *this;
24 | }
25 |
26 | GoogleMapsDeviceLocator &GoogleMapsDeviceLocator::withLocatePeriodic(unsigned long secondsPeriodic) {
27 | locatorMode = LOCATOR_MODE_PERIODIC;
28 | if (secondsPeriodic < 5) {
29 | secondsPeriodic = 5;
30 | }
31 | periodMs = secondsPeriodic * 1000;
32 | return *this;
33 | }
34 |
35 | GoogleMapsDeviceLocator &GoogleMapsDeviceLocator::withEventName(const char *name) {
36 | this->eventName = name;
37 | return *this;
38 | }
39 |
40 | GoogleMapsDeviceLocator &GoogleMapsDeviceLocator::withSubscribe(GoogleMapsDeviceLocatorSubscriptionCallback callback) {
41 | this->callback = callback;
42 |
43 | snprintf(requestBuf, sizeof(requestBuf), "hook-response/%s/%s", eventName.c_str(), System.deviceID().c_str());
44 |
45 | Particle.subscribe(requestBuf, &GoogleMapsDeviceLocator::subscriptionHandler, this, MY_DEVICES);
46 |
47 | return *this;
48 | }
49 |
50 | void GoogleMapsDeviceLocator::loop() {
51 | switch(state) {
52 | case CONNECT_WAIT_STATE:
53 | if (Particle.connected()) {
54 | state = CONNECTED_WAIT_STATE;
55 | stateTime = millis();
56 | }
57 | break;
58 |
59 | case CONNECTED_WAIT_STATE:
60 | if (millis() - stateTime >= waitAfterConnect) {
61 | // Wait several seconds after connecting before doing the location
62 | if (locatorMode == LOCATOR_MODE_ONCE) {
63 | publishLocation();
64 |
65 | state = IDLE_STATE;
66 | }
67 | else
68 | if (locatorMode == LOCATOR_MODE_MANUAL) {
69 | state = IDLE_STATE;
70 | }
71 | else {
72 | state = CONNECTED_STATE;
73 | stateTime = millis() - periodMs;
74 | }
75 | }
76 | break;
77 |
78 | case CONNECTED_STATE:
79 | if (Particle.connected()) {
80 | if (millis() - stateTime >= periodMs) {
81 | stateTime = millis();
82 | publishLocation();
83 | }
84 | }
85 | else {
86 | // We have disconnected, rec
87 | state = CONNECT_WAIT_STATE;
88 | }
89 | break;
90 |
91 |
92 | case IDLE_STATE:
93 | // Just hang out here forever (entered only on LOCATOR_MODE_ONCE)
94 | break;
95 | }
96 |
97 | }
98 |
99 | const char *GoogleMapsDeviceLocator::scan() {
100 | #if Wiring_WiFi
101 | return wifiScan();
102 | #endif
103 | #if Wiring_Cellular
104 | return cellularScan();
105 | #endif
106 | }
107 |
108 |
109 | void GoogleMapsDeviceLocator::publishLocation() {
110 |
111 | Serial.println("publishLocation");
112 |
113 | const char *scanData = scan();
114 |
115 | Serial.printlnf("scanData=%s", scanData);
116 |
117 | if (scanData[0]) {
118 |
119 | if (Particle.connected()) {
120 | Particle.publish(eventName, scanData, PRIVATE);
121 | }
122 | }
123 | }
124 |
125 | void GoogleMapsDeviceLocator::subscriptionHandler(const char *event, const char *data) {
126 | // event: hook-response/deviceLocator//0
127 |
128 | if (callback) {
129 | // float lat, float lon, float accuracy
130 | char *mutableCopy = strdup(data);
131 | char *part, *end;
132 | float lat, lon, accuracy;
133 |
134 | part = strtok_r(mutableCopy, ",", &end);
135 | if (part) {
136 | lat = atof(part);
137 | part = strtok_r(NULL, ",", &end);
138 | if (part) {
139 | lon = atof(part);
140 | part = strtok_r(NULL, ",", &end);
141 | if (part) {
142 | accuracy = atof(part);
143 |
144 | (*callback)(lat, lon, accuracy);
145 | }
146 | }
147 | }
148 |
149 | free(mutableCopy);
150 | }
151 | }
152 |
153 |
154 |
155 | #if Wiring_WiFi
156 |
157 | static void wifiScanCallback(WiFiAccessPoint* wap, void* data) {
158 | // The - 3 factor here to leave room for the closing JSON array ] object }} and the trailing null
159 | size_t spaceLeft = &requestBuf[sizeof(requestBuf) - 3] - requestCur;
160 | if (spaceLeft < 30) {
161 | return;
162 | }
163 |
164 | int sizeNeeded = snprintf(requestCur, spaceLeft,
165 | "%s{\"m\":\"%02x:%02x:%02x:%02x:%02x:%02x\",\"s\":%d,\"c\":%d}",
166 | (requestCur[-1] == '[' ? "" : ","),
167 | wap->bssid[0], wap->bssid[1], wap->bssid[2], wap->bssid[3], wap->bssid[4], wap->bssid[5],
168 | wap->rssi, wap->channel);
169 | if (sizeNeeded > 0 && sizeNeeded < (int)spaceLeft) {
170 | // There is enough space to store the whole entry, so save it
171 | requestCur += sizeNeeded;
172 | numAdded++;
173 | }
174 | }
175 |
176 |
177 | const char *GoogleMapsDeviceLocator::wifiScan() {
178 |
179 | requestCur = requestBuf;
180 | numAdded = 0;
181 |
182 | requestCur += sprintf(requestCur, "{\"w\":{\"a\":");
183 | *requestCur++ = '[';
184 |
185 | WiFi.scan(wifiScanCallback);
186 |
187 | *requestCur++ = ']';
188 | *requestCur++ = '}';
189 | *requestCur++ = '}';
190 | *requestCur++ = 0;
191 |
192 | if (numAdded == 0) {
193 | requestBuf[0] = 0;
194 | }
195 |
196 | return requestBuf;
197 | }
198 |
199 | #endif /* Wiring_WiFi */
200 |
201 |
202 | #if Wiring_Cellular
203 |
204 | static void cellularAddTower(const CellularHelperEnvironmentCellData *cellData) {
205 | // The - 4 factor here to leave room for the closing JSON array ], object }}, and the trailing null
206 | size_t spaceLeft = &requestBuf[sizeof(requestBuf) - 4] - requestCur;
207 |
208 | int sizeNeeded = snprintf(requestCur, spaceLeft,
209 | "%s{\"i\":%d,\"l\":%u,\"c\":%d,\"n\":%d}",
210 | (requestCur[-1] == '[' ? "" : ","),
211 | cellData->ci, cellData->lac, cellData->mcc, cellData->mnc);
212 |
213 | if (sizeNeeded > 0 && sizeNeeded < (int)spaceLeft && cellData->lac != 0 && cellData->lac != 65535 && cellData->mcc != 65535 && cellData->mnc != 65535) {
214 | // There is enough space to store the whole entry, so save it
215 | requestCur += sizeNeeded;
216 | numAdded++;
217 | }
218 |
219 | }
220 |
221 | const char *GoogleMapsDeviceLocator::cellularScan() {
222 |
223 | // First try to get info on neighboring cells. This doesn't work for me using the U260
224 | CellularHelperEnvironmentResponseStatic<4> envResp;
225 |
226 | CellularHelper.getEnvironment(5, envResp);
227 |
228 | if (envResp.resp != RESP_OK) {
229 | // We couldn't get neighboring cells, so try just the receiving cell
230 | CellularHelper.getEnvironment(3, envResp);
231 | }
232 | // envResp.serialDebug();
233 |
234 |
235 | requestCur = requestBuf;
236 | numAdded = 0;
237 |
238 | // We know these things fit, so just using sprintf instead of snprintf here
239 | requestCur += sprintf(requestCur, "{\"c\":{\"o\":\"%s\",",
240 | CellularHelper.getOperatorName().c_str());
241 |
242 | requestCur += sprintf(requestCur, "\"a\":[");
243 |
244 | cellularAddTower(&envResp.service);
245 |
246 | for(size_t ii = 0; ii < envResp.getNumNeighbors(); ii++) {
247 | cellularAddTower(&envResp.neighbors[ii]);
248 | }
249 |
250 | *requestCur++ = ']';
251 | *requestCur++ = '}';
252 | *requestCur++ = '}';
253 | *requestCur++ = 0;
254 |
255 | if (numAdded == 0) {
256 | requestBuf[0] = 0;
257 | }
258 |
259 | return requestBuf;
260 | }
261 |
262 |
263 | #endif /* Wiring_Cellular */
264 |
265 |
266 |
267 |
268 |
269 |
--------------------------------------------------------------------------------
/google-maps-device-locator.h:
--------------------------------------------------------------------------------
1 | #ifndef __GOOGLEMAPSDEVICELOCATOR_H
2 | #define __GOOGLEMAPSDEVICELOCATOR_H
3 |
4 |
5 | #include "Particle.h"
6 |
7 | /**
8 | * This is the callback function prototype for the callback if you use the
9 | * withSubscribe() method to be notified of your own location.
10 | *
11 | * lat is the latitude in degrees
12 | * lon is the longitude in degrees
13 | * accuracy is the accuracy radius in meters
14 | */
15 | typedef void (*GoogleMapsDeviceLocatorSubscriptionCallback)(float lat, float lon, float accuracy);
16 |
17 | class GoogleMapsDeviceLocator {
18 | public:
19 | GoogleMapsDeviceLocator();
20 | virtual ~GoogleMapsDeviceLocator();
21 |
22 | /**
23 | * If you use withLocateOnce() then the location will be updated once after you've
24 | * connected to the cloud.
25 | */
26 | GoogleMapsDeviceLocator &withLocateOnce();
27 |
28 | /**
29 | * If you use withLocatePeriod() then the location will be updated this often when
30 | * connected to the cloud.
31 | *
32 | * If you use neither withLocateOnce() nor withLocatePeriodic() you can check manually
33 | * using publishLocation() instead
34 | */
35 | GoogleMapsDeviceLocator &withLocatePeriodic(unsigned long secondsPeriodic);
36 |
37 | /**
38 | * The default event name is "deviceLocator". Use this method to change it. Note that this
39 | * name is also configured in your Google location integration and must be changed in both
40 | * locations or location detection will not work
41 | */
42 | GoogleMapsDeviceLocator &withEventName(const char *name);
43 |
44 | /**
45 | * Use this method to register a callback to be notified when your location has been found.
46 | *
47 | * The prototype for the function is:
48 | *
49 | * void locationCallback(float lat, float lon, float accuracy)
50 | */
51 | GoogleMapsDeviceLocator &withSubscribe(GoogleMapsDeviceLocatorSubscriptionCallback callback);
52 |
53 | /**
54 | * You should call this from loop() to give the code time to process things in the background.
55 | * This is really only needed if you use withLocateOnce() or withLocatePeriodic() but it doesn't
56 | * hurt to call it always from loop. It executes quickly.
57 | */
58 | void loop();
59 |
60 | /**
61 | * You can use this to manually publish your location. It finds the Wi-Fi or cellular location
62 | * using scan() and then publishes it as an event
63 | */
64 | void publishLocation();
65 |
66 | /**
67 | * Queries the location information (Wi-Fi or cellular) and returns a JSON block of data to
68 | * put in the event data. This returns a static buffer pointer and is not reentrant.
69 | */
70 | const char *scan();
71 |
72 | protected:
73 | void subscriptionHandler(const char *event, const char *data);
74 |
75 | #if Wiring_WiFi
76 | const char *wifiScan();
77 | #endif
78 |
79 | #if Wiring_Cellular
80 | const char *cellularScan();
81 | #endif
82 |
83 | static const int CONNECT_WAIT_STATE = 0;
84 | static const int CONNECTED_WAIT_STATE = 2;
85 | static const int CONNECTED_STATE = 3;
86 | static const int IDLE_STATE = 4;
87 |
88 | static const int LOCATOR_MODE_MANUAL = 0;
89 | static const int LOCATOR_MODE_ONCE = 1;
90 | static const int LOCATOR_MODE_PERIODIC = 2;
91 |
92 | int locatorMode;
93 | unsigned long periodMs;
94 | String eventName;
95 | unsigned long stateTime;
96 | int state;
97 | GoogleMapsDeviceLocatorSubscriptionCallback callback;
98 | unsigned long waitAfterConnect;
99 | };
100 |
101 | #endif /* __GOOGLEMAPSDEVICELOCATOR_H */
102 |
103 |
--------------------------------------------------------------------------------
/main_beta_comp.cpp:
--------------------------------------------------------------------------------
1 | // BzBoard Firmware Application - June 2018
2 | // OSBeehives, Inc. Development
3 | // Created by: Peter Naylor, Javier Abors
4 | //
5 | //
6 | // LOTS of help and reference from Particle.io community:
7 | // ricckas7, ScruffR, peekay123, kennethlimcp, etc.
8 | //
9 | //
10 | // Libraries can be found here:
11 | // https://github.com/rickkas7/photonAudio
12 | // https://github.com/sparkfun/Battery_Babysitter
13 | //
14 | // TODO:
15 | // - Deep sleep
16 | // - Memory card backup for audio samples
17 | // - Sleep mode for peripheralz
18 |
19 |
20 | #include "Particle.h"
21 | #include "softap_bb.h"
22 | #include "google-maps-device-locator.h"
23 | #include "ClosedCube_SHT31D.h"
24 | #include "SparkFunBQ27441.h"
25 | #include "MMA8452-xlero-OSBH.h"
26 | #include "adc_hal.h"
27 | #include "gpio_hal.h"
28 | #include "pinmap_hal.h"
29 | #include "pinmap_impl.h"
30 | #include
31 | #include "params.h"
32 | #include "downsampler.h"
33 |
34 | using namespace std;
35 |
36 | const int FW_VERSION = 21;
37 |
38 |
39 | // System threading is required for this project
40 | SYSTEM_THREAD(ENABLED);
41 | SYSTEM_MODE(SEMI_AUTOMATIC);
42 |
43 | // Class instances
44 | MMA8452Q xlero;
45 | SHT31D_CC::ClosedCube_SHT31D sht31d;
46 | TCPClient client;
47 | Downsampler d(10);
48 | GoogleMapsDeviceLocator locator;
49 |
50 | const unsigned int BATTERY_CAPACITY = 850; // e.g. 850mAh battery
51 |
52 | // This is the pin connected to the INT1 pin on the MMA8452 chip
53 | const int ACCEL_INT_PIN = D2;
54 |
55 | // Various timing constants
56 | const unsigned long MAX_RECORDING_LENGTH_MS = 10000; // audio sample time
57 | const unsigned long MAX_TIME_TO_PUBLISH_MS = 30000; // Only stay awake for 20 seconds trying to connect to the cloud and publish
58 | const unsigned long TIME_AFTER_PUBLISH_MS = 1000; // After publish, wait 4 seconds for data to go out
59 | const unsigned long TIME_AFTER_BOOT_MS = 1000; // At boot, wait 1 seconds before going to publish
60 | const unsigned long TIME_PUBLISH_BATTERY_SEC = 20 * 60; // every 10 minutes, send an update
61 | const unsigned long TIME_PUBLISH_BATTERY_SEC_LP = 60 * 60; // states)
326 | {
327 | //Count all states
328 | std::array count={0,0,0,0,0,0,0,0,0,0,0};
329 | for( int i = 0; i < states.size(); i++ )
330 | {
331 | count[states[i]]++;
332 | }
333 |
334 | //Determine most repeated state
335 | int indexWinner = 1;
336 | for( int i = 1; i < count.size(); i++ )
337 | {
338 | if( count[i] > count[indexWinner] )
339 | {
340 | indexWinner = i;
341 | }
342 | }
343 | return indexWinner;
344 | }
345 |
346 | void printResult(String text, SHT31D_CC::SHT31D result) {
347 | if (result.error == SHT31D_CC::NO_ERROR) {
348 | Serial.print(text);
349 | Serial.print(": T=");
350 | Serial.print(result.t);
351 | Serial.print("C, RH=");
352 | Serial.print(result.rh);
353 | Serial.println("%");
354 | tempC_in = result.t;
355 | RH_in = result.rh;
356 | }
357 | else {
358 | Serial.print(text);
359 | Serial.print(": [ERROR] Code #");
360 | Serial.println(result.error);
361 | }
362 | }
363 |
364 |
365 | void update_params(void){
366 | Serial.println("update params...");
367 | printResult("SHT31", sht31d.readTempAndHumidity(SHT31D_CC::REPEATABILITY_LOW, SHT31D_CC::MODE_CLOCK_STRETCH, 50));
368 |
369 | // BQ27441 Battery Manager calls
370 | soc = lipo.soc(); // Read state-of-charge (in %)
371 | volts = lipo.voltage(); // Read voltage (in mV)
372 | current = lipo.current(AVG); // Read average current (in mA)
373 | power = lipo.power(); // Read power consumption (in mW)
374 | soh = lipo.soh(); // Read state-of-health (in %)
375 |
376 | }
377 |
378 | void free_mem(void){
379 | freemem = System.freeMemory();
380 | Serial.print(" ----- free memory: ");
381 | Serial.println(freemem);
382 | }
383 |
384 | void buff_in_mem(){
385 | //uint16_t samplez[MAX_BUF][SAMPLE_BUF_SIZE/4]; // MAX_BUF number of samples
386 | samplez = (uint8_t **) malloc (sizeof(uint8_t *)*MAX_BUF);
387 | Serial.printlnf("size: %i", sizeof(uint8_t *)*MAX_BUF);
388 | for (int r = 0; r < MAX_BUF; r++) {
389 | samplez[r] = (uint8_t *) malloc(sizeof(uint8_t)*SAMPLE_BUF_SIZE/2);
390 | Serial.printlnf("size %i: %i", r, sizeof(uint8_t)*SAMPLE_BUF_SIZE/2);
391 | }
392 | }
393 |
394 | void buff_out_mem(){
395 | for (int r = 0; r < MAX_BUF; r++) {
396 | free(samplez[r]);
397 | Serial.printlnf("row: %i", r);
398 | free_mem();
399 | }
400 | free(samplez);
401 | }
402 |
403 | void otaHandler(){
404 | Serial.println("ota update");
405 | stateTime = millis();
406 | state = SETUP_LOOP;
407 | ota_active = true;
408 | Particle.process();
409 | }
410 |
411 | void button_click_handler(system_event_t event, int param)
412 | {
413 | times = times + system_button_clicks(param);
414 | if(times%2){
415 | Serial.println("===test mode===");
416 | state = RSSI_TEST_LOOP_PRE;
417 | }
418 | else{
419 | Serial.println("===run mode===");
420 | state = BOOT_WAIT_STATE;
421 | }
422 | }
423 |
424 | void rssi_report(){
425 | rssi = WiFi.RSSI();
426 | wifi_rat = (float)(wifi_goo + 1) / (wifi_timeout + wifi_goo + 1);
427 | aud_rat = (float)(good_count + 1) / (total_count + 1);
428 | Serial.printlnf("good: %i total: %i", good_count, total_count);
429 | Serial.printlnf("RSSI: %i, NG/OK: %i/%i, wifi_rat: %.2f, A_NG/OK: %i/%i, aud_rat: %.2f", rssi, wifi_timeout, wifi_goo, wifi_rat, (total_count - good_count), total_count, aud_rat );
430 |
431 | }
432 |
433 | void batt_report(){
434 | Serial.print("State of Charge/Health: ");
435 | Serial.print(soc);
436 | Serial.print(", ");
437 | Serial.println(soh);
438 | int16_t l = -600, h = -400;
439 | for(int j = 0; j <= 5 ; j++){
440 | if((power >= l && power < h) || j ==5){
441 | chg = j;
442 | break;
443 | }
444 | l = l + 200;
445 | h = l + 200;
446 | }
447 | Serial.printlnf("chg: %i, mA: %i ,mW: %i", chg, current, power);
448 | }
449 |
450 | bool WiFi_Listen_Check(){ // checks for WiFi listening mode on Photon
451 | if(WiFi.listening() && !setup_once){
452 | state = SETUP_LOOP;
453 | stateTime = millis();
454 | Serial.println("SETUP LOOP");
455 | setup_once = true;
456 | // buff_out_mem();
457 | return true;
458 | }
459 | else return false;
460 | }
461 |
462 | int8_t ALaw_Encode(int16_t number)
463 | {
464 | const uint16_t ALAW_MAX = 0xFFF;
465 | uint16_t mask = 0x800;
466 | uint8_t sign = 0;
467 | uint8_t position = 11;
468 | uint8_t lsb = 0;
469 | if (number < 0)
470 | {
471 | number = -number;
472 | sign = 0x80;
473 | }
474 | if (number > ALAW_MAX)
475 | {
476 | number = ALAW_MAX;
477 | }
478 | for (; ((number & mask) != mask && position >= 5); mask >>= 1, position--);
479 | lsb = (number >> ((position == 4) ? (1) : (position - 4))) & 0x0f;
480 | return (sign | ((position - 4) << 4) | lsb) ^ 0x55;
481 | }
482 |
483 | ApplicationWatchdog wd(30000, System.reset, 256); // declare watchdog
484 |
485 |
486 | void setup() {
487 |
488 | Serial.begin(57600);
489 | WiFi.useDynamicIP();
490 | delay(1000);
491 |
492 | Serial.printlnf("System firmware version: %s", System.version().c_str());
493 | Serial.printlnf("BuzzBoard FW version: %i", FW_VERSION);
494 | Serial.println("Setup");
495 | free_mem();
496 |
497 | String myID;
498 | myID = System.deviceID();
499 | myID.toCharArray(myIDtest, sizeof(myIDtest));
500 |
501 | Serial.printlnf("DeviceID: %s", myIDtest);
502 |
503 | WiFi.selectAntenna(ANT_EXTERNAL);
504 |
505 | sht31d.begin(0x44);
506 | Serial.print("SHT31D Serial #");
507 | Serial.println(sht31d.readSerialNumber());
508 |
509 | // Initialization for the battery monitor
510 | if (!lipo.begin())
511 | {
512 | Serial.println("Error: Unable to communicate with BQ27441.");
513 | Serial.println("Check wiring and try again.");
514 | Serial.println("(Battery must be plugged into Battery Babysitter!)");
515 | }
516 |
517 | Serial.println("Connected to BQ27441!");
518 | lipo.setCapacity(BATTERY_CAPACITY); // Configure BQ27441 to assume a 1000 mAh battery
519 |
520 | // Initialization for accelerometer
521 | Serial.println("MMA xlerometer init");
522 | xlero.init(SCALE_2G, ODR_12); // 2g scale, sampled at 12 Hz
523 |
524 | pinMode(D7, OUTPUT);
525 | free_mem();
526 | System.on(firmware_update_pending, otaHandler);
527 | System.on(button_click, button_click_handler);
528 |
529 | Serial.println("Setup COMPLETE");
530 | }
531 |
532 |
533 | void loop() {
534 |
535 | uint16_t *sendBuf = NULL;
536 |
537 | if(millis() >= debug_timer){
538 | if(state != 5){
539 | Serial.printlnf("State: %d", state);
540 | }
541 | wd.checkin(); // reset watchdog timer
542 | Particle.process();
543 | WiFi_Listen_Check();
544 | free_mem();
545 | locator.loop();
546 | debug_timer = millis() + 1000; /// try this
547 | if(xlero.readTap() != 0){
548 | Serial.println("xlrometer disturbance~!");
549 | }
550 | }
551 |
552 |
553 | switch(state) {
554 |
555 | case BOOT_WAIT_STATE:
556 | if (millis() - stateTime >= TIME_AFTER_BOOT_MS) {
557 | // To publish the battery stats after boot, set state to PUBLISH_STATE
558 | // To go to sleep immediately, set state to SLEEP_STATE
559 | Particle.connect();
560 | p_once = false;
561 | state = PUBLISH_STATE;
562 | }
563 | break;
564 |
565 | case LP_CHECK: // Li-Ion battery check
566 | delay(250);
567 |
568 | // check to see if reason for waking was xlrometer disturbance, or timer
569 | if(xlero.readTap() != 0){
570 | Serial.println("xlrometer disturbance~!");
571 | accel_awake = true;
572 | }
573 | else{
574 | accel_awake = false;
575 | }
576 |
577 | update_params();
578 | if(soc < LP_SHUTDOWN_PERCENTAGE || soc > LP_FULL_CHARGE){
579 | Serial.println("Low Battery, system will shutdown until charged");
580 | batt_report();
581 | // WiFi.off();
582 | state = SLEEP_STATE; // go to sleep
583 | }
584 | if(soc >= LP_SHUTDOWN_PERCENTAGE && soc <= LP_FULL_CHARGE){
585 | state = BOOT_WAIT_STATE; // continue normal operation
586 | WiFi.useDynamicIP();
587 | WiFi.on();
588 | }
589 | if(soc < LP_SLOWDOWN){ // set low-power update time
590 | publish_time = TIME_PUBLISH_BATTERY_SEC_LP;
591 | }
592 | if(soc >= LP_SLOWDOWN && soc <= LP_FULL_CHARGE){ // set normal update time
593 | publish_time = TIME_PUBLISH_BATTERY_SEC;
594 | }
595 |
596 | break;
597 |
598 | case PUBLISH_STATE:
599 |
600 | if (!p_once) {
601 | // BME280 reads
602 | update_params();
603 | // free_mem();
604 | rssi_report();
605 | batt_report();
606 |
607 | // Debug printouts
608 | Serial.println("SHT30: ");
609 | Serial.print("temp (C): ");
610 | Serial.print(tempC_in);
611 | Serial.print("RH (%): ");
612 | Serial.println(RH_in);
613 |
614 | p_once = true;
615 | }
616 |
617 | if(WiFi_Listen_Check()) break;
618 |
619 | if (Particle.connected()) {
620 |
621 | locator.withEventName("geoLocate").withSubscribe(locationCallback);
622 | locator.publishLocation();
623 | // delay(500);
624 |
625 | rssi_report();
626 |
627 | if(soc >= LP_SHUTDOWN_PERCENTAGE && soc < LP_SLOWDOWN){
628 | Particle.publish("Low Battery Warning");
629 | Serial.println("Low Battery Warning");
630 | }
631 |
632 | Serial.println("publish");
633 | if(accel_awake){
634 | Particle.publish("disturbance");
635 | }
636 |
637 | update_params();
638 | batt_report();
639 |
640 | char data[512];
641 | snprintf(data, sizeof(data), "{\"T_in\": %.01f, \"RH_i\": %d, \"soc\": %d, \"soh\": %d, \"chg\": %d, \"I\": %d, \"P\": %d, \"RSSI\": %i, \"w_r\": %.2f, \"a_r\": %.2f, \"acc\": %f, \"lon\": %f, \"lat\": %f}", tempC_in, RH_in, soc, soh, chg, current, power, rssi, wifi_rat, aud_rat, accuracy, longitude, latitude);
642 | Particle.publish("measurements", data, PRIVATE);
643 |
644 | // Wait for the publish to go out
645 | stateTime = millis();
646 | wifi_goo++;
647 | state = PUBLISH_WAIT_STATE;
648 | }
649 | else {
650 | // Haven't come online yet
651 | if (millis() - stateTime >= MAX_TIME_TO_PUBLISH_MS) {
652 | // Took too long to publish, just go to sleep
653 | state = SLEEP_STATE; // {{PETER}} SLEEP_STATE
654 | wifi_timeout++;
655 | }
656 | }
657 | break;
658 |
659 | case PUBLISH_WAIT_STATE:
660 | if (millis() - stateTime >= TIME_AFTER_PUBLISH_MS) {
661 | state = AWS_CONNECT; //AWS_CONNECT
662 | }
663 | break;
664 |
665 | case AWS_CONNECT:
666 | // Ready to connect to the server via TCP
667 | if (client.connect(serverAddr, serverPort)) {
668 | // Connected
669 |
670 | Serial.println("stream starting");
671 | // Serial.println("packet #,ok?,good,re-try,failed");
672 | free_mem();
673 |
674 | char influxDbMessage[256];
675 | snprintf(influxDbMessage, sizeof(influxDbMessage), "%s%s%s%s%d%s%s", postHeader1test, myIDtest, postHeader2test, postHeader4test, FW_VERSION, postHeader5test, postHeaderAudiotest);
676 |
677 | int count = client.write(influxDbMessage);
678 |
679 | if(count == strlen(influxDbMessage)){
680 | Serial.println(influxDbMessage);
681 | }
682 | free_mem();
683 |
684 | buff_in_mem(); // aalocate audio buffer in memory
685 | free_mem();
686 |
687 | Serial.printlnf("sizeof samplez: %i, %i", sizeof(samplez), sizeof(samplez[0]));
688 |
689 | adcDMA.start(SAMPLE_RATE);
690 | recordingStart = millis();
691 | digitalWrite(D7, HIGH);
692 | stateTime = millis();
693 | state = AWS_STREAM;
694 | }
695 | else {
696 | Serial.println("failed to connect to server");
697 | delay(500);
698 | state = SLEEP_STATE; //{{PETER}} SLEEP_STATE, AWS_CONNECT
699 | }
700 | break;
701 |
702 | case HEADER_WAIT_STATE:
703 | if (millis() - stateTime >= TIME_AFTER_PUBLISH_MS) {
704 | adcDMA.start(SAMPLE_RATE);
705 | recordingStart = millis();
706 | digitalWrite(D7, HIGH);
707 | state = AWS_STREAM; //
708 | }
709 | break;
710 |
711 |
712 | case AWS_STREAM:
713 | bool success;
714 |
715 | if (DMA_GetFlagStatus(DMA2_Stream0, DMA_FLAG_HTIF0)) { // first half
716 | DMA_ClearFlag(DMA2_Stream0, DMA_FLAG_HTIF0);
717 | sendBuf = samples;
718 | }
719 | if (DMA_GetFlagStatus(DMA2_Stream0, DMA_FLAG_TCIF0)) { // second half
720 | DMA_ClearFlag(DMA2_Stream0, DMA_FLAG_TCIF0);
721 | sendBuf = &samples[SAMPLE_BUF_SIZE / 2];
722 | }
723 |
724 | if (sendBuf != NULL) { // process 32kHz samples pass to downsampler
725 | // There is a sample buffer to send
726 | // Average the pairs of samples and format them
727 | for(size_t ii = 0, jj = 0; ii < SAMPLE_BUF_SIZE / 2; ii += 2, jj++) {
728 | uint32_t sum = (uint32_t)sendBuf[ii] + (uint32_t)sendBuf[ii + 1];
729 | sendBuf[jj] = (uint16_t)(sum / 2);
730 |
731 | x = (((float)sendBuf[jj] - 32768)/65536);
732 | d.update(x);
733 |
734 | if(d.isReady()){ // downsample ready
735 | x = d.getSample();
736 | re_sample_i16 = (int16_t)(x * 65536); // convert to uint16 for audio stream
737 | re_sample_i8 = ALaw_Encode(re_sample_i16);
738 | samplez[aud_select][sample_num] = re_sample_i8; //fill send-stream buffer
739 | sample_num++;
740 |
741 | if(sample_num >= (SAMPLE_BUF_SIZE / 2)){ // send-stream sample ready /// /2
742 | total_count++; // total number of TCP buffers filled
743 | sample_num = 0;
744 | aud_select++;
745 | retry_timer = 0;
746 | bo_once = false;
747 | if(aud_select > MAX_BUF - 1) aud_select = 0;
748 | }
749 |
750 | }
751 | }
752 | }
753 |
754 |
755 | if(aud_select != send_select && millis() >= retry_timer){ // send sample packet
756 |
757 | Serial.println("-----");
758 | Serial.printlnf("aud_/send_select: %i / %i, millis(%lu)", aud_select, send_select, millis());
759 | //Send buffer to server
760 | int count = client.write((uint8_t *)samplez[send_select], SAMPLE_BUF_SIZE / 2);
761 |
762 | if (count == SAMPLE_BUF_SIZE / 2) {
763 | // Success
764 | good_count++;
765 | success = true;
766 |
767 | send_select++;
768 | if(send_select > MAX_BUF - 1) send_select = 0;
769 |
770 | Serial.printlnf("success! %d, %d", good_count, count);
771 | }
772 |
773 | else
774 | if (count == -16) {
775 | // TCP Buffer full
776 | error_count++;
777 | success = false;
778 | retry_timer = millis() + RETRY_TIME;
779 | Serial.printlnf("TCP buffer full");
780 | }
781 | else {
782 | // Error
783 | Serial.printlnf("error writing %d", count);
784 | pass_count = 0;
785 |
786 | adcDMA.stop();
787 | digitalWrite(D7, LOW);
788 | client.stop();
789 |
790 | accel_awake = false;
791 | stateTime = millis();
792 |
793 | state = AWS_FINISH;
794 | }
795 | }
796 | if((aud_select == send_select) && !success && !bo_once){ // error, aud_buffer has looped around and reached send_buffer
797 | Serial.printlnf("BUFFER OVERWRITE: aud: %i, send: %i", aud_select, send_select);
798 | bo_once = true;
799 | overflow_count++;
800 | }
801 |
802 | if (millis() - recordingStart >= MAX_RECORDING_LENGTH_MS) { // recording time reached
803 | state = AWS_FINISH;
804 | }
805 | break;
806 |
807 | case AWS_FINISH:
808 | adcDMA.stop();
809 | buff_out_mem();
810 | digitalWrite(D7, LOW);
811 | Serial.println("aws stopping");
812 | Serial.println("testnum entries ; good/fails/overflows (x16) // total packets: ");
813 | Serial.printlnf("%lu ; %d/%d/%d // %d", pass_count, good_count, error_count, overflow_count, total_count);
814 |
815 | client.stop();
816 |
817 | //If feature extractor is ready
818 | if (client.connect(serverAddr, serverPort)) {
819 | // Connected
820 |
821 | Serial.println("OBC send");
822 |
823 | char influxDbMessage[256];
824 | snprintf(influxDbMessage, sizeof(influxDbMessage), "%s%s%s%s%s%s%s\r\n\r\n", postHeader1test, myIDtest, postHeader2test, postHeader3test, postHeader5test, postHeaderStatetest, hive_state_msg_test);
825 |
826 | int count = client.write(influxDbMessage);
827 | if(count == strlen(influxDbMessage)){
828 | Serial.println(influxDbMessage);
829 | }
830 | client.stop();
831 | }
832 | else {
833 | Serial.println("failed to connect to server");
834 | state = SLEEP_STATE;
835 | }
836 |
837 | //print On-Board-Classification State
838 | state = SLEEP_STATE;
839 | break;
840 |
841 | case SLEEP_STATE: // Sleep
842 |
843 | Serial.print("Go to sleep...");
844 |
845 | pass_count = 0;
846 | aud_select = 0;
847 | send_select = 0;
848 |
849 | if (WiFi_Listen_Check()) break;
850 |
851 | state = GO_TO_SLEEP;
852 | break;
853 |
854 |
855 | case GO_TO_SLEEP:
856 |
857 | Serial.println(" zzz");
858 | xlero.readTap();
859 | Particle.disconnect();
860 | WiFi.off();
861 | System.sleep(ACCEL_INT_PIN, RISING, publish_time); // slow to sleep after waking, only when no wifi connection
862 | state = LP_CHECK;
863 | stateTime = millis();
864 | break;
865 |
866 | case SETUP_LOOP:
867 | if (millis() - stateTime >= 300000) {
868 | state = GO_TO_SLEEP; //AWS_CONNECT
869 | }
870 | if(!WiFi.listening() && !ota_active){
871 | stateTime = millis();
872 | state = BOOT_WAIT_STATE;
873 | setup_once = false;
874 | }
875 |
876 | break;
877 |
878 |
879 | case RSSI_TEST_LOOP_PRE:
880 |
881 | pass_count = 0;
882 | adcDMA.stop();
883 | digitalWrite(D7, LOW);
884 | client.stop();
885 |
886 | accel_awake = false;
887 |
888 | state = RSSI_TEST_LOOP;
889 | break;
890 |
891 |
892 | case RSSI_TEST_LOOP:
893 | if(millis() - stateTime >= RSSI_DELAY){
894 |
895 | rssi_report();
896 | update_params();
897 | batt_report();
898 |
899 | stateTime = millis();
900 | }
901 | break;
902 |
903 | }
904 |
905 | }
906 |
907 |
908 |
909 |
910 | // all samples working!!
911 |
--------------------------------------------------------------------------------
/params.h:
--------------------------------------------------------------------------------
1 | //Window Length in s
2 | float windowLength=2;
3 |
4 | //Sampling Rate in Hz
5 | float samplingRate=6300;
6 |
7 |
--------------------------------------------------------------------------------
/particle.ignore:
--------------------------------------------------------------------------------
1 | # particle.ignore
2 | .ds_store
3 | wake_adxl.cpp
4 | blink.ino
5 | main_beta_mem_SHT.cpp
6 | main_beta_mem_MMA.cpp
7 | geolocate.cpp
8 | PCBA-test.cpp
9 | ADCDMA.cpp
10 | ADCDMA.h
11 |
--------------------------------------------------------------------------------
/softap_bb.h:
--------------------------------------------------------------------------------
1 | #include "Particle.h"
2 | #include "softap_http.h"
3 |
4 | struct Page
5 | {
6 | const char* url;
7 | const char* mime_type;
8 | const char* data;
9 | };
10 |
11 | const char index_html[] = "Setup your Buzzbox Setup your Buzzbox My device ID: Copy
Scan for visible WiFi networks Scan
Don't see your network? Move me closer to your router, then re-scan.
";
12 | const char rsa_js[] = "function parseBigInt(a,b){return new BigInteger(a,b);}function linebrk(a,b){var c='';var d=0;while(d+b=0&&b>0){var e=a.charCodeAt(d--);if(e<128)c[--b]=e;else if((e>127)&&(e<2048)){c[--b]=(e&63)|128;c[--b]=(e>>6)|192;}else{c[--b]=(e&63)|128;c[--b]=((e>>6)&63)|128;c[--b]=(e>>12)|224;}}c[--b]=0;var f=new SecureRandom();var g=new Array();while(b>2){g[0]=0;while(g[0]==0)f.nextBytes(g);c[--b]=g[0];}c[--b]=2;c[--b]=0;return new BigInteger(c);}function RSAKey(){this.n=null;this.e=0;this.d=null;this.p=null;this.q=null;this.dmp1=null;this.dmq1=null;this.coeff=null;}function RSASetPublic(a,b){if(a!=null&&b!=null&&a.length>0&&b.length>0){this.n=parseBigInt(a,16);this.e=parseInt(b,16);}else alert('Invalid RSA public key');}function RSADoPublic(a){return a.modPowInt(this.e,this.n);}function RSAEncrypt(a){var b=pkcs1pad2(a,(this.n.bitLength()+7)>>3);if(b==null)return null;var c=this.doPublic(b);if(c==null)return null;var d=c.toString(16);if((d.length&1)==0)return d;else return '0'+d;}RSAKey.prototype.doPublic=RSADoPublic;RSAKey.prototype.setPublic=RSASetPublic;RSAKey.prototype.encrypt=RSAEncrypt;";
13 | const char style_css[] = "html{height:100%;margin:auto;background-color:white}body{box-sizing:border-box;min-height:100%;padding:20px;background-color:#FFD042;font-family:'Lucida Sans Unicode','Lucida Grande',sans-serif;font-weight:normal;color:#8F6526;margin-top:0;margin-left:auto;margin-right:auto;margin-bottom:0;max-width:400px;text-align:center;border:1px solid #6e6e70;border-radius:4px}div{margin-top:25px;margin-bottom:25px}h1{margin-top:25px;margin-bottom:25px}button{border-color:#D4A037;background-color:#D4A037;color:white;border-radius:5px;height:30px;font-size:15px;font-weight:bold}button.input-helper{background-color:#bebebe;border-color:#bebebe;color:#6e6e70;margin-left:3px}button:disabled{background-color:#bebebe;border-color:#bebebe;color:white}input[type='text'],input[type='password']{background-color:white;color:#6e6e70;border-color:white;border-radius:5px;height:25px;text-align:center;font-size:15px}input:disabled{background-color:#bebebe;border-color:#bebebe}input[type='radio']{position:relative;bottom:-0.33em;margin:0;border:0;height:1.5em;width:15%}label{padding-top:7px;padding-bottom:7px;padding-left:5%;display:inline-block;width:80%;text-align:left}input[type='radio']:checked+label{font-weight:bold;color:#D4A037}.scanning-error{font-weight:bold;text-align:center}.radio-div{box-sizing:border-box;margin:2px;margin-left:auto;margin-right:auto;background-color:white;color:#6e6e70;border:1px solid #6e6e70;border-radius:3px;width:100%;padding:5px}#networks-div{margin-left:auto;margin-right:auto;text-align:left}#device-id{text-align:center}#scan-button{min-width:100px}#connect-button{display:block;min-width:100px;margin-top:10px;margin-left:auto;margin-right:auto;margin-bottom:20px}#password{margin-top:20px;margin-bottom:10px}";
14 | const char rng_js[] = "var rng_state;var rng_pool;var rng_pptr;function rng_seed_int(a){rng_pool[rng_pptr++]^=a&255;rng_pool[rng_pptr++]^=(a>>8)&255;rng_pool[rng_pptr++]^=(a>>16)&255;rng_pool[rng_pptr++]^=(a>>24)&255;if(rng_pptr>=rng_psize)rng_pptr-=rng_psize;}function rng_seed_time(){rng_seed_int(new Date().getTime());}if(rng_pool==null){rng_pool=new Array();rng_pptr=0;var t;if(window.crypto&&window.crypto.getRandomValues){var ua=new Uint8Array(32);window.crypto.getRandomValues(ua);for(t=0;t<32;++t)rng_pool[rng_pptr++]=ua[t];}if(navigator.appName=='Netscape'&&navigator.appVersion<'5'&&window.crypto){var z=window.crypto.random(32);for(t=0;t>>8;rng_pool[rng_pptr++]=t&255;}rng_pptr=0;rng_seed_time();}function rng_get_byte(){if(rng_state==null){rng_seed_time();rng_state=prng_newstate();rng_state.init(rng_pool);for(rng_pptr=0;rng_pptr=this.t){b.t=0;return;}var d=a%this.DB;var e=this.DB-d;var f=(1<>d;for(var g=c+1;g>d;}if(d>0)b[this.t-c-1]|=(this.s&f)<>=this.DB;}if(a.t>=this.DB;}d+=this.s;}else{d+=this.s;while(c>=this.DB;}d-=a.s;}b.s=(d<0)?-1:0;if(d<-1)b[c++]=this.DV+d;else if(d>0)b[c++]=d;b.t=c;b.clamp();}function bnpMultiplyTo(a,b){var c=this.abs(),d=a.abs();var e=c.t;b.t=e+d.t;while(--e>=0)b[e]=0;for(e=0;e=0)a[c]=0;for(c=0;c=b.DV){a[c+b.t]-=b.DV;a[c+b.t+1]=1;}}if(a.t>0)a[a.t-1]+=b.am(c,b[c],a,2*c,0,1);a.s=0;a.clamp();}function bnpDivRemTo(a,b,c){var d=a.abs();if(d.t<=0)return;var e=this.abs();if(e.t0){d.lShiftTo(i,f);e.lShiftTo(i,c);}else{d.copyTo(f);e.copyTo(c);}var j=f.t;var k=f[j-1];if(k==0)return;var l=k*(1<1)?f[j-2]>>this.F2:0);var m=this.FV/l,n=(1<=0){c[c.t++]=1;c.subTo(r,c);}BigInteger.ONE.dlShiftTo(j,r);r.subTo(f,f);while(f.t=0){var s=(c[--p]==k)?this.DM:Math.floor(c[p]*m+(c[p-1]+o)*n);if((c[p]+=f.am(0,s,c,q,0,j))0)c.rShiftTo(i,c);if(g<0)BigInteger.ZERO.subTo(c,c);}function bnMod(a){var b=nbi();this.abs().divRemTo(a,null,b);if(this.s<0&&b.compareTo(BigInteger.ZERO)>0)a.subTo(b,b);return b;}function Classic(a){this.m=a;}function cConvert(a){if(a.s<0||a.compareTo(this.m)>=0)return a.mod(this.m);else return a;}function cRevert(a){return a;}function cReduce(a){a.divRemTo(this.m,null,a);}function cMulTo(a,b,c){a.multiplyTo(b,c);this.reduce(c);}function cSqrTo(a,b){a.squareTo(b);this.reduce(b);}Classic.prototype.convert=cConvert;Classic.prototype.revert=cRevert;Classic.prototype.reduce=cReduce;Classic.prototype.mulTo=cMulTo;Classic.prototype.sqrTo=cSqrTo;function bnpInvDigit(){if(this.t<1)return 0;var a=this[0];if((a&1)==0)return 0;var b=a&3;b=(b*(2-(a&0xf)*b))&0xf;b=(b*(2-(a&0xff)*b))&0xff;b=(b*(2-(((a&0xffff)*b)&0xffff)))&0xffff;b=(b*(2-a*b%this.DV))%this.DV;return(b>0)?this.DV-b:-b;}function Montgomery(a){this.m=a;this.mp=a.invDigit();this.mpl=this.mp&0x7fff;this.mph=this.mp>>15;this.um=(1<<(a.DB-15))-1;this.mt2=2*a.t;}function montConvert(a){var b=nbi();a.abs().dlShiftTo(this.m.t,b);b.divRemTo(this.m,null,b);if(a.s<0&&b.compareTo(BigInteger.ZERO)>0)this.m.subTo(b,b);return b;}function montRevert(a){var b=nbi();a.copyTo(b);this.reduce(b);return b;}function montReduce(a){while(a.t<=this.mt2)a[a.t++]=0;for(var b=0;b>15)*this.mpl)&this.um)<<15))&a.DM;c=b+this.m.t;a[c]+=this.m.am(0,d,a,b,0,this.m.t);while(a[c]>=a.DV){a[c]-=a.DV;a[++c]++;}}a.clamp();a.drShiftTo(this.m.t,a);if(a.compareTo(this.m)>=0)a.subTo(this.m,a);}function montSqrTo(a,b){a.squareTo(b);this.reduce(b);}function montMulTo(a,b,c){a.multiplyTo(b,c);this.reduce(c);}Montgomery.prototype.convert=montConvert;Montgomery.prototype.revert=montRevert;Montgomery.prototype.reduce=montReduce;Montgomery.prototype.mulTo=montMulTo;Montgomery.prototype.sqrTo=montSqrTo;function bnpIsEven(){return((this.t>0)?(this[0]&1):this.s)==0;}function bnpExp(a,b){if(a>0xffffffff||a<1)return BigInteger.ONE;var c=nbi(),d=nbi(),e=b.convert(this),f=nbits(a)-1;e.copyTo(c);while(--f>=0){b.sqrTo(c,d);if((a&(1<0)b.mulTo(d,e,c);else{var g=c;c=d;d=g;}}return b.revert(c);}function bnModPowInt(a,b){var c;if(a<256||b.isEven())c=new Classic(b);else c=new Montgomery(b);return this.exp(a,c);}BigInteger.prototype.copyTo=bnpCopyTo;BigInteger.prototype.fromInt=bnpFromInt;BigInteger.prototype.fromString=bnpFromString;BigInteger.prototype.clamp=bnpClamp;BigInteger.prototype.dlShiftTo=bnpDLShiftTo;BigInteger.prototype.drShiftTo=bnpDRShiftTo;BigInteger.prototype.lShiftTo=bnpLShiftTo;BigInteger.prototype.rShiftTo=bnpRShiftTo;BigInteger.prototype.subTo=bnpSubTo;BigInteger.prototype.multiplyTo=bnpMultiplyTo;BigInteger.prototype.squareTo=bnpSquareTo;BigInteger.prototype.divRemTo=bnpDivRemTo;BigInteger.prototype.invDigit=bnpInvDigit;BigInteger.prototype.isEven=bnpIsEven;BigInteger.prototype.exp=bnpExp;BigInteger.prototype.toString=bnToString;BigInteger.prototype.negate=bnNegate;BigInteger.prototype.abs=bnAbs;BigInteger.prototype.compareTo=bnCompareTo;BigInteger.prototype.bitLength=bnBitLength;BigInteger.prototype.mod=bnMod;BigInteger.prototype.modPowInt=bnModPowInt;BigInteger.ZERO=nbv(0);BigInteger.ONE=nbv(1);";
16 | const char jsbn_1_js[] = "var dbits;var canary=0xdeadbeefcafe;var j_lm=((canary&0xffffff)==0xefcafe);function BigInteger(a,b,c){if(a!=null)if('number'==typeof a)this.fromNumber(a,b,c);else if(b==null&&'string'!=typeof a)this.fromString(a,256);else this.fromString(a,b);}function nbi(){return new BigInteger(null);}function am1(a,b,c,d,e,f){while(--f>=0){var g=b*this[a++]+c[d]+e;e=Math.floor(g/0x4000000);c[d++]=g&0x3ffffff;}return e;}function am2(a,b,c,d,e,f){var g=b&0x7fff,h=b>>15;while(--f>=0){var i=this[a]&0x7fff;var j=this[a++]>>15;var k=h*i+j*g;i=g*i+((k&0x7fff)<<15)+c[d]+(e&0x3fffffff);e=(i>>>30)+(k>>>15)+h*j+(e>>>30);c[d++]=i&0x3fffffff;}return e;}function am3(a,b,c,d,e,f){var g=b&0x3fff,h=b>>14;while(--f>=0){var i=this[a]&0x3fff;var j=this[a++]>>14;var k=h*i+j*g;i=g*i+((k&0x3fff)<<14)+c[d]+e;e=(i>>28)+(k>>14)+h*j;c[d++]=i&0xfffffff;}return e;}if(j_lm&&(navigator.appName=='Microsoft Internet Explorer')){BigInteger.prototype.am=am2;dbits=30;}else if(j_lm&&(navigator.appName!='Netscape')){BigInteger.prototype.am=am1;dbits=26;}else{BigInteger.prototype.am=am3;dbits=28;}BigInteger.prototype.DB=dbits;BigInteger.prototype.DM=((1<=0;--b)a[b]=this[b];a.t=this.t;a.s=this.s;}function bnpFromInt(a){this.t=1;this.s=(a<0)?-1:0;if(a>0)this[0]=a;else if(a<-1)this[0]=a+this.DV;else this.t=0;}function nbv(a){var b=nbi();b.fromInt(a);return b;}function bnpFromString(a,b){var c;if(b==16)c=4;else if(b==8)c=3;else if(b==256)c=8;else if(b==2)c=1;else if(b==32)c=5;else if(b==4)c=2;else{this.fromRadix(a,b);return;}this.t=0;this.s=0;var d=a.length,e=false,f=0;while(--d>=0){var g=(c==8)?a[d]&0xff:intAt(a,d);if(g<0){if(a.charAt(d)=='-')e=true;continue;}e=false;if(f==0)this[this.t++]=g;else if(f+c>this.DB){this[this.t-1]|=(g&((1<<(this.DB-f))-1))<>(this.DB-f));}else this[this.t-1]|=g<=this.DB)f-=this.DB;}if(c==8&&(a[0]&0x80)!=0){this.s=-1;if(f>0)this[this.t-1]|=((1<<(this.DB-f))-1)<0&&this[this.t-1]==a)--this.t;}function bnToString(a){if(this.s<0)return '-'+this.negate().toString(a);var b;if(a==16)b=4;else if(a==8)b=3;else if(a==2)b=1;else if(a==32)b=5;else if(a==4)b=2;else return this.toRadix(a);var c=(1<0){if(h>h)>0){e=true;f=int2char(d);}while(g>=0){if(h>(h+=this.DB-b);}else{d=(this[g]>>(h-=b))&c;if(h<=0){h+=this.DB;--g;}}if(d>0)e=true;if(e)f+=int2char(d);}}return e?f:'0';}function bnNegate(){var a=nbi();BigInteger.ZERO.subTo(this,a);return a;}function bnAbs(){return(this.s<0)?this.negate():this;}function bnCompareTo(a){var b=this.s-a.s;if(b!=0)return b;var c=this.t;b=c-a.t;if(b!=0)return(this.s<0)?-b:b;while(--c>=0)if((b=this[c]-a[c])!=0)return b;return 0;}function nbits(a){var b=1,c;if((c=a>>>16)!=0){a=c;b+=16;}if((c=a>>8)!=0){a=c;b+=8;}if((c=a>>4)!=0){a=c;b+=4;}if((c=a>>2)!=0){a=c;b+=2;}if((c=a>>1)!=0){a=c;b+=1;}return b;}function bnBitLength(){if(this.t<=0)return 0;return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM));}function bnpDLShiftTo(a,b){var c;for(c=this.t-1;c>=0;--c)b[c+a]=this[c];for(c=a-1;c>=0;--c)b[c]=0;b.t=this.t+a;b.s=this.s;}function bnpDRShiftTo(a,b){for(var c=a;c=0;--h){b[h+f+1]=(this[h]>>d)|g;g=(this[h]&e)<=0;--h)b[h]=0;b[f]=g;b.t=this.t+f+1;b.s=this.s;b.clamp();}";
17 | const char script_js[] = "var base_url='http://192.168.0.1/';var network_list;var public_key;var rsa=new RSAKey();var scanButton=document.getElementById('scan-button');var connectButton=document.getElementById('connect-button');var copyButton=document.getElementById('copy-button');var showButton=document.getElementById('show-button');var deviceID=document.getElementById('device-id');var connectForm=document.getElementById('connect-form');var public_key_callback={success:function(a){console.log('Public key: '+a.b);public_key=a.b;rsa.setPublic(public_key.substring(58,58+256),public_key.substring(318,318+6));},error:function(a,b){console.log(a);window.alert('There was a problem fetching important information from your device. Please verify your connection, then reload this page.');}};var device_id_callback={success:function(a){var b=a.id;deviceID.value=b;},error:function(a,b){console.log(a);var c='COMMUNICATION_ERROR';deviceID.value=c;}};var scan=function(){console.log('Scanning...!');disableButtons();scanButton.innerHTML='Scanning...';connectButton.innerHTML='Connect';document.getElementById('connect-div').style.display='none';document.getElementById('networks-div').style.display='none';getRequest(base_url+'scan-ap',scan_callback);};var scan_callback={success:function(a){network_list=a.scans;console.log('I found:');var b=document.getElementById('networks-div');b.innerHTML='';if(network_list.length>0)for(var c=0;cNo networks found. ';},error:function(a){console.log('Scanning error:'+a);document.getElementById('networks-div').innerHTML='Scanning error.
';},regardless:function(){scanButton.innerHTML='Re-Scan';enableButtons();document.getElementById('networks-div').style.display='block';}};var configure=function(a){a.preventDefault();var b=get_selected_network();var c=document.getElementById('password').value;if(!b){window.alert('Please select a network!');return false;}var d={idx:0,ssid:b.ssid,pwd:rsa.encrypt(c),sec:b.sec,ch:b.ch};connectButton.innerHTML='Sending credentials...';disableButtons();console.log('Sending credentials: '+JSON.stringify(d));postRequest(base_url+'configure-ap',d,configure_callback);};var configure_callback={success:function(a){console.log('Credentials received.');connectButton.innerHTML='Credentials received...';postRequest(base_url+'connect-ap',{idx:0},connect_callback);},error:function(a,b){console.log('Configure error: '+a);window.alert('The configuration command failed, check that you are still well connected to the device\\'s WiFi hotspot and retry.');connectButton.innerHTML='Retry';enableButtons();}};var connect_callback={success:function(a){console.log('Attempting to connect to the cloud.');connectButton.innerHTML='Attempting to connect...';window.alert('Your device should now start flashing green and attempt to connect to the cloud. This usually takes about 20 seconds, after which it will begin slowly breathing cyan.\\n\\nOnce it is breathing cyan, return to the BuzzBox mobile app to finish the registration process. \\n\\nIf this process fails because you entered the wrong password, the device will flash green indefinitely. In this case, hold the setup button for 6 seconds until the device starts blinking blue again. Then reconnect to the WiFi hotspot it generates and reload this page to try again.');},error:function(a,b){console.log('Connect error: '+a);window.alert('The connect command failed, check that you are still well connected to the device\\'s WiFi hotspot and retry.');connectButton.innerHTML='Retry';enableButtons();}};var disableButtons=function(){connectButton.disabled=true;scanButton.disabled=true;};var enableButtons=function(){connectButton.disabled=false;scanButton.disabled=false;};var add_wifi_option=function(a,b){var c=document.createElement('INPUT');c.type='radio';c.value=b;c.id=b;c.name='ssid';c.className='radio';var d=document.createElement('DIV');d.className='radio-div';d.appendChild(c);var e=document.createElement('label');e.htmlFor=b;e.innerHTML=b;d.appendChild(e);a.appendChild(d);};var get_selected_network=function(){for(var a=0;awrite(myPages[idx].data);
61 | }
62 | }
63 |
64 | STARTUP(softap_set_application_page_handler(myPage, nullptr));
65 |
66 |
--------------------------------------------------------------------------------