mirror of
https://github.com/IoTManagerProject/IoTManager.git
synced 2026-03-30 11:59:12 +03:00
gatewayTransportSend
This commit is contained in:
594
lib/MySensors/drivers/ATSHA204/ATSHA204.cpp
Normal file
594
lib/MySensors/drivers/ATSHA204/ATSHA204.cpp
Normal file
@@ -0,0 +1,594 @@
|
||||
#include "Arduino.h"
|
||||
#include "ATSHA204.h"
|
||||
|
||||
/* Local data and function prototypes */
|
||||
|
||||
static uint8_t device_pin;
|
||||
#ifdef ARDUINO_ARCH_AVR
|
||||
static volatile uint8_t *device_port_DDR, *device_port_OUT, *device_port_IN;
|
||||
#endif
|
||||
static void sha204c_calculate_crc(uint8_t length, uint8_t *data, uint8_t *crc);
|
||||
static uint8_t sha204c_check_crc(uint8_t *response);
|
||||
static void swi_set_signal_pin(uint8_t is_high);
|
||||
static uint8_t swi_receive_bytes(uint8_t count, uint8_t *buffer);
|
||||
static uint8_t swi_send_bytes(uint8_t count, uint8_t *buffer);
|
||||
static uint8_t swi_send_byte(uint8_t value);
|
||||
static uint8_t sha204p_receive_response(uint8_t size, uint8_t *response);
|
||||
static uint8_t sha204c_resync(uint8_t size, uint8_t *response);
|
||||
static uint8_t sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer,
|
||||
uint8_t execution_delay, uint8_t execution_timeout);
|
||||
|
||||
/* SWI bit bang functions */
|
||||
|
||||
static void swi_set_signal_pin(uint8_t is_high)
|
||||
{
|
||||
SHA204_SET_OUTPUT();
|
||||
|
||||
if (is_high) {
|
||||
SHA204_POUT_HIGH();
|
||||
} else {
|
||||
SHA204_POUT_LOW();
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t swi_send_bytes(uint8_t count, uint8_t *buffer)
|
||||
{
|
||||
uint8_t i, bit_mask;
|
||||
|
||||
// Disable interrupts while sending.
|
||||
noInterrupts(); //swi_disable_interrupts();
|
||||
|
||||
// Set signal pin as output.
|
||||
SHA204_POUT_HIGH();
|
||||
SHA204_SET_OUTPUT();
|
||||
|
||||
|
||||
// Wait turn around time.
|
||||
delayMicroseconds(RX_TX_DELAY); //RX_TX_DELAY;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
for (bit_mask = 1; bit_mask > 0; bit_mask <<= 1) {
|
||||
if (bit_mask & buffer[i]) {
|
||||
SHA204_POUT_LOW(); //*device_port_OUT &= ~device_pin;
|
||||
delayMicroseconds(BIT_DELAY); //BIT_DELAY_1;
|
||||
SHA204_POUT_HIGH(); //*device_port_OUT |= device_pin;
|
||||
delayMicroseconds(7*BIT_DELAY); //BIT_DELAY_7;
|
||||
} else {
|
||||
// Send a zero bit.
|
||||
SHA204_POUT_LOW(); //*device_port_OUT &= ~device_pin;
|
||||
delayMicroseconds(BIT_DELAY); //BIT_DELAY_1;
|
||||
SHA204_POUT_HIGH(); //*device_port_OUT |= device_pin;
|
||||
delayMicroseconds(BIT_DELAY); //BIT_DELAY_1;
|
||||
SHA204_POUT_LOW(); //*device_port_OUT &= ~device_pin;
|
||||
delayMicroseconds(BIT_DELAY); //BIT_DELAY_1;
|
||||
SHA204_POUT_HIGH(); //*device_port_OUT |= device_pin;
|
||||
delayMicroseconds(5*BIT_DELAY); //BIT_DELAY_5;
|
||||
}
|
||||
}
|
||||
}
|
||||
interrupts(); //swi_enable_interrupts();
|
||||
return SWI_FUNCTION_RETCODE_SUCCESS;
|
||||
}
|
||||
|
||||
static uint8_t swi_send_byte(uint8_t value)
|
||||
{
|
||||
return swi_send_bytes(1, &value);
|
||||
}
|
||||
|
||||
static uint8_t swi_receive_bytes(uint8_t count, uint8_t *buffer)
|
||||
{
|
||||
uint8_t status = SWI_FUNCTION_RETCODE_SUCCESS;
|
||||
uint8_t i;
|
||||
uint8_t bit_mask;
|
||||
uint8_t pulse_count;
|
||||
uint8_t timeout_count;
|
||||
|
||||
// Disable interrupts while receiving.
|
||||
noInterrupts(); //swi_disable_interrupts();
|
||||
|
||||
// Configure signal pin as input.
|
||||
SHA204_SET_INPUT();
|
||||
|
||||
// Receive bits and store in buffer.
|
||||
for (i = 0; i < count; i++) {
|
||||
for (bit_mask = 1; bit_mask > 0; bit_mask <<= 1) {
|
||||
pulse_count = 0;
|
||||
|
||||
// Make sure that the variable below is big enough.
|
||||
// Change it to uint16_t if 255 is too small, but be aware that
|
||||
// the loop resolution decreases on an 8-bit controller in that case.
|
||||
timeout_count = START_PULSE_TIME_OUT;
|
||||
|
||||
// Detect start bit.
|
||||
while (--timeout_count > 0) {
|
||||
// Wait for falling edge.
|
||||
if (SHA204_PIN_READ() == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (timeout_count == 0) {
|
||||
status = SWI_FUNCTION_RETCODE_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
|
||||
do {
|
||||
// Wait for rising edge.
|
||||
if (SHA204_PIN_READ() != 0) {
|
||||
// For an Atmel microcontroller this might be faster than "pulse_count++".
|
||||
pulse_count = 1;
|
||||
break;
|
||||
}
|
||||
} while (--timeout_count > 0);
|
||||
|
||||
if (pulse_count == 0) {
|
||||
status = SWI_FUNCTION_RETCODE_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
|
||||
// Trying to measure the time of start bit and calculating the timeout
|
||||
// for zero bit detection is not accurate enough for an 8 MHz 8-bit CPU.
|
||||
// So let's just wait the maximum time for the falling edge of a zero bit
|
||||
// to arrive after we have detected the rising edge of the start bit.
|
||||
timeout_count = ZERO_PULSE_TIME_OUT;
|
||||
|
||||
// Detect possible edge indicating zero bit.
|
||||
do {
|
||||
if (SHA204_PIN_READ() == 0) {
|
||||
// For an Atmel microcontroller this might be faster than "pulse_count++".
|
||||
pulse_count = 2;
|
||||
break;
|
||||
}
|
||||
} while (--timeout_count > 0);
|
||||
|
||||
// Wait for rising edge of zero pulse before returning. Otherwise we might interpret
|
||||
// its rising edge as the next start pulse.
|
||||
if (pulse_count == 2) {
|
||||
do {
|
||||
if (SHA204_PIN_READ() != 0) {
|
||||
break;
|
||||
}
|
||||
} while (timeout_count-- > 0);
|
||||
}
|
||||
|
||||
// Update byte at current buffer index.
|
||||
else {
|
||||
buffer[i] |= bit_mask; // received "one" bit
|
||||
}
|
||||
}
|
||||
|
||||
if (status != SWI_FUNCTION_RETCODE_SUCCESS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
interrupts(); //swi_enable_interrupts();
|
||||
|
||||
if (status == SWI_FUNCTION_RETCODE_TIMEOUT) {
|
||||
if (i > 0) {
|
||||
// Indicate that we timed out after having received at least one byte.
|
||||
status = SWI_FUNCTION_RETCODE_RX_FAIL;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Physical functions */
|
||||
|
||||
static uint8_t sha204p_receive_response(uint8_t size, uint8_t *response)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t ret_code;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
response[i] = 0;
|
||||
}
|
||||
|
||||
(void) swi_send_byte(SHA204_SWI_FLAG_TX);
|
||||
|
||||
ret_code = swi_receive_bytes(size, response);
|
||||
if (ret_code == SWI_FUNCTION_RETCODE_SUCCESS || ret_code == SWI_FUNCTION_RETCODE_RX_FAIL) {
|
||||
uint8_t count_byte;
|
||||
count_byte = response[SHA204_BUFFER_POS_COUNT];
|
||||
if ((count_byte < SHA204_RSP_SIZE_MIN) || (count_byte > size)) {
|
||||
return SHA204_INVALID_SIZE;
|
||||
}
|
||||
|
||||
return SHA204_SUCCESS;
|
||||
}
|
||||
|
||||
// Translate error so that the Communication layer
|
||||
// can distinguish between a real error or the
|
||||
// device being busy executing a command.
|
||||
if (ret_code == SWI_FUNCTION_RETCODE_TIMEOUT) {
|
||||
return SHA204_RX_NO_RESPONSE;
|
||||
} else {
|
||||
return SHA204_RX_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Communication functions */
|
||||
|
||||
static uint8_t sha204c_resync(uint8_t size, uint8_t *response)
|
||||
{
|
||||
// Try to re-synchronize without sending a Wake token
|
||||
// (step 1 of the re-synchronization process).
|
||||
delay(SHA204_SYNC_TIMEOUT);
|
||||
uint8_t ret_code = sha204p_receive_response(size, response);
|
||||
if (ret_code == SHA204_SUCCESS) {
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
// We lost communication. Send a Wake pulse and try
|
||||
// to receive a response (steps 2 and 3 of the
|
||||
// re-synchronization process).
|
||||
atsha204_sleep();
|
||||
ret_code = atsha204_wakeup(response);
|
||||
|
||||
// Translate a return value of success into one
|
||||
// that indicates that the device had to be woken up
|
||||
// and might have lost its TempKey.
|
||||
return (ret_code == SHA204_SUCCESS ? SHA204_RESYNC_WITH_WAKEUP : ret_code);
|
||||
}
|
||||
|
||||
static uint8_t sha204c_send_and_receive(uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer,
|
||||
uint8_t execution_delay, uint8_t execution_timeout)
|
||||
{
|
||||
uint8_t ret_code = SHA204_FUNC_FAIL;
|
||||
uint8_t ret_code_resync;
|
||||
uint8_t n_retries_send;
|
||||
uint8_t n_retries_receive;
|
||||
uint8_t i;
|
||||
uint8_t status_byte;
|
||||
uint8_t count = tx_buffer[SHA204_BUFFER_POS_COUNT];
|
||||
uint8_t count_minus_crc = count - SHA204_CRC_SIZE;
|
||||
uint16_t execution_timeout_us = (uint16_t) (execution_timeout * 1000) + SHA204_RESPONSE_TIMEOUT;
|
||||
volatile uint16_t timeout_countdown;
|
||||
|
||||
// Append CRC.
|
||||
sha204c_calculate_crc(count_minus_crc, tx_buffer, tx_buffer + count_minus_crc);
|
||||
|
||||
// Retry loop for sending a command and receiving a response.
|
||||
n_retries_send = SHA204_RETRY_COUNT + 1;
|
||||
|
||||
while ((n_retries_send-- > 0) && (ret_code != SHA204_SUCCESS)) {
|
||||
// Send command.
|
||||
ret_code = swi_send_byte(SHA204_SWI_FLAG_CMD);
|
||||
if (ret_code != SWI_FUNCTION_RETCODE_SUCCESS) {
|
||||
ret_code = SHA204_COMM_FAIL;
|
||||
} else {
|
||||
ret_code = swi_send_bytes(count, tx_buffer);
|
||||
}
|
||||
|
||||
if (ret_code != SHA204_SUCCESS) {
|
||||
if (sha204c_resync(rx_size, rx_buffer) == SHA204_RX_NO_RESPONSE) {
|
||||
return ret_code; // The device seems to be dead in the water.
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait minimum command execution time and then start polling for a response.
|
||||
delay(execution_delay);
|
||||
|
||||
// Retry loop for receiving a response.
|
||||
n_retries_receive = SHA204_RETRY_COUNT + 1;
|
||||
while (n_retries_receive-- > 0) {
|
||||
// Reset response buffer.
|
||||
for (i = 0; i < rx_size; i++) {
|
||||
rx_buffer[i] = 0;
|
||||
}
|
||||
|
||||
// Poll for response.
|
||||
timeout_countdown = execution_timeout_us;
|
||||
do {
|
||||
ret_code = sha204p_receive_response(rx_size, rx_buffer);
|
||||
timeout_countdown -= SHA204_RESPONSE_TIMEOUT;
|
||||
} while ((timeout_countdown > SHA204_RESPONSE_TIMEOUT) && (ret_code == SHA204_RX_NO_RESPONSE));
|
||||
|
||||
if (ret_code == SHA204_RX_NO_RESPONSE) {
|
||||
// We did not receive a response. Re-synchronize and send command again.
|
||||
if (sha204c_resync(rx_size, rx_buffer) == SHA204_RX_NO_RESPONSE) {
|
||||
// The device seems to be dead in the water.
|
||||
return ret_code;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether we received a valid response.
|
||||
if (ret_code == SHA204_INVALID_SIZE) {
|
||||
// We see 0xFF for the count when communication got out of sync.
|
||||
ret_code_resync = sha204c_resync(rx_size, rx_buffer);
|
||||
if (ret_code_resync == SHA204_SUCCESS) {
|
||||
// We did not have to wake up the device. Try receiving response again.
|
||||
continue;
|
||||
}
|
||||
if (ret_code_resync == SHA204_RESYNC_WITH_WAKEUP) {
|
||||
// We could re-synchronize, but only after waking up the device.
|
||||
// Re-send command.
|
||||
break;
|
||||
} else {
|
||||
// We failed to re-synchronize.
|
||||
return ret_code;
|
||||
}
|
||||
}
|
||||
|
||||
// We received a response of valid size.
|
||||
// Check the consistency of the response.
|
||||
ret_code = sha204c_check_crc(rx_buffer);
|
||||
if (ret_code == SHA204_SUCCESS) {
|
||||
// Received valid response.
|
||||
if (rx_buffer[SHA204_BUFFER_POS_COUNT] > SHA204_RSP_SIZE_MIN) {
|
||||
// Received non-status response. We are done.
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
// Received status response.
|
||||
status_byte = rx_buffer[SHA204_BUFFER_POS_STATUS];
|
||||
|
||||
// Translate the three possible device status error codes
|
||||
// into library return codes.
|
||||
if (status_byte == SHA204_STATUS_BYTE_PARSE) {
|
||||
return SHA204_PARSE_ERROR;
|
||||
}
|
||||
if (status_byte == SHA204_STATUS_BYTE_EXEC) {
|
||||
return SHA204_CMD_FAIL;
|
||||
}
|
||||
if (status_byte == SHA204_STATUS_BYTE_COMM) {
|
||||
// In case of the device status byte indicating a communication
|
||||
// error this function exits the retry loop for receiving a response
|
||||
// and enters the overall retry loop
|
||||
// (send command / receive response).
|
||||
ret_code = SHA204_STATUS_CRC;
|
||||
break;
|
||||
}
|
||||
|
||||
// Received status response from CheckMAC, DeriveKey, GenDig,
|
||||
// Lock, Nonce, Pause, UpdateExtra, or Write command.
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
else {
|
||||
// Received response with incorrect CRC.
|
||||
ret_code_resync = sha204c_resync(rx_size, rx_buffer);
|
||||
if (ret_code_resync == SHA204_SUCCESS) {
|
||||
// We did not have to wake up the device. Try receiving response again.
|
||||
continue;
|
||||
}
|
||||
if (ret_code_resync == SHA204_RESYNC_WITH_WAKEUP) {
|
||||
// We could re-synchronize, but only after waking up the device.
|
||||
// Re-send command.
|
||||
break;
|
||||
} else {
|
||||
// We failed to re-synchronize.
|
||||
return ret_code;
|
||||
}
|
||||
} // block end of check response consistency
|
||||
|
||||
} // block end of receive retry loop
|
||||
|
||||
} // block end of send and receive retry loop
|
||||
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
/* CRC Calculator and Checker */
|
||||
|
||||
static void sha204c_calculate_crc(uint8_t length, uint8_t *data, uint8_t *crc)
|
||||
{
|
||||
uint8_t counter;
|
||||
uint16_t crc_register = 0;
|
||||
uint16_t polynom = 0x8005;
|
||||
uint8_t shift_register;
|
||||
uint8_t data_bit, crc_bit;
|
||||
|
||||
for (counter = 0; counter < length; counter++) {
|
||||
for (shift_register = 0x01; shift_register > 0x00; shift_register <<= 1) {
|
||||
data_bit = (data[counter] & shift_register) ? 1 : 0;
|
||||
crc_bit = crc_register >> 15;
|
||||
|
||||
// Shift CRC to the left by 1.
|
||||
crc_register <<= 1;
|
||||
|
||||
if ((data_bit ^ crc_bit) != 0) {
|
||||
crc_register ^= polynom;
|
||||
}
|
||||
}
|
||||
}
|
||||
crc[0] = (uint8_t) (crc_register & 0x00FF);
|
||||
crc[1] = (uint8_t) (crc_register >> 8);
|
||||
}
|
||||
|
||||
static uint8_t sha204c_check_crc(uint8_t *response)
|
||||
{
|
||||
uint8_t crc[SHA204_CRC_SIZE];
|
||||
uint8_t count = response[SHA204_BUFFER_POS_COUNT];
|
||||
|
||||
count -= SHA204_CRC_SIZE;
|
||||
sha204c_calculate_crc(count, response, crc);
|
||||
|
||||
return (crc[0] == response[count] && crc[1] == response[count + 1])
|
||||
? SHA204_SUCCESS : SHA204_BAD_CRC;
|
||||
}
|
||||
|
||||
/* Public functions */
|
||||
|
||||
void atsha204_init(uint8_t pin)
|
||||
{
|
||||
#if defined(ARDUINO_ARCH_AVR)
|
||||
device_pin = digitalPinToBitMask(pin); // Find the bit value of the pin
|
||||
uint8_t port = digitalPinToPort(pin); // temoporarily used to get the next three registers
|
||||
|
||||
// Point to data direction register port of pin
|
||||
device_port_DDR = portModeRegister(port);
|
||||
// Point to output register of pin
|
||||
device_port_OUT = portOutputRegister(port);
|
||||
// Point to input register of pin
|
||||
device_port_IN = portInputRegister(port);
|
||||
#else
|
||||
device_pin = pin;
|
||||
#endif
|
||||
}
|
||||
|
||||
void atsha204_idle(void)
|
||||
{
|
||||
swi_send_byte(SHA204_SWI_FLAG_IDLE);
|
||||
}
|
||||
|
||||
void atsha204_sleep(void)
|
||||
{
|
||||
swi_send_byte(SHA204_SWI_FLAG_SLEEP);
|
||||
}
|
||||
|
||||
uint8_t atsha204_wakeup(uint8_t *response)
|
||||
{
|
||||
swi_set_signal_pin(0);
|
||||
delayMicroseconds(10*SHA204_WAKEUP_PULSE_WIDTH);
|
||||
swi_set_signal_pin(1);
|
||||
delay(SHA204_WAKEUP_DELAY);
|
||||
|
||||
uint8_t ret_code = sha204p_receive_response(SHA204_RSP_SIZE_MIN, response);
|
||||
if (ret_code != SHA204_SUCCESS) {
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
// Verify status response.
|
||||
if (response[SHA204_BUFFER_POS_COUNT] != SHA204_RSP_SIZE_MIN) {
|
||||
ret_code = SHA204_INVALID_SIZE;
|
||||
} else if (response[SHA204_BUFFER_POS_STATUS] != SHA204_STATUS_BYTE_WAKEUP) {
|
||||
ret_code = SHA204_COMM_FAIL;
|
||||
} else {
|
||||
if ((response[SHA204_RSP_SIZE_MIN - SHA204_CRC_SIZE] != 0x33)
|
||||
|| (response[SHA204_RSP_SIZE_MIN + 1 - SHA204_CRC_SIZE] != 0x43)) {
|
||||
ret_code = SHA204_BAD_CRC;
|
||||
}
|
||||
}
|
||||
if (ret_code != SHA204_SUCCESS) {
|
||||
delay(SHA204_COMMAND_EXEC_MAX);
|
||||
}
|
||||
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
uint8_t atsha204_execute(uint8_t op_code, uint8_t param1, uint16_t param2,
|
||||
uint8_t datalen1, uint8_t *data1, uint8_t tx_size, uint8_t *tx_buffer, uint8_t rx_size,
|
||||
uint8_t *rx_buffer)
|
||||
{
|
||||
uint8_t poll_delay, poll_timeout, response_size;
|
||||
uint8_t *p_buffer;
|
||||
uint8_t len;
|
||||
(void)tx_size;
|
||||
|
||||
// Supply delays and response size.
|
||||
switch (op_code) {
|
||||
case SHA204_GENDIG:
|
||||
poll_delay = GENDIG_DELAY;
|
||||
poll_timeout = GENDIG_EXEC_MAX - GENDIG_DELAY;
|
||||
response_size = GENDIG_RSP_SIZE;
|
||||
break;
|
||||
|
||||
case SHA204_HMAC:
|
||||
poll_delay = HMAC_DELAY;
|
||||
poll_timeout = HMAC_EXEC_MAX - HMAC_DELAY;
|
||||
response_size = HMAC_RSP_SIZE;
|
||||
break;
|
||||
|
||||
case SHA204_NONCE:
|
||||
poll_delay = NONCE_DELAY;
|
||||
poll_timeout = NONCE_EXEC_MAX - NONCE_DELAY;
|
||||
response_size = param1 == NONCE_MODE_PASSTHROUGH
|
||||
? NONCE_RSP_SIZE_SHORT : NONCE_RSP_SIZE_LONG;
|
||||
break;
|
||||
|
||||
case SHA204_RANDOM:
|
||||
poll_delay = RANDOM_DELAY;
|
||||
poll_timeout = RANDOM_EXEC_MAX - RANDOM_DELAY;
|
||||
response_size = RANDOM_RSP_SIZE;
|
||||
break;
|
||||
|
||||
case SHA204_SHA:
|
||||
poll_delay = SHA_DELAY;
|
||||
poll_timeout = SHA_EXEC_MAX - SHA_DELAY;
|
||||
response_size = param1 == SHA_INIT
|
||||
? SHA_RSP_SIZE_SHORT : SHA_RSP_SIZE_LONG;
|
||||
break;
|
||||
|
||||
case SHA204_WRITE:
|
||||
poll_delay = WRITE_DELAY;
|
||||
poll_timeout = WRITE_EXEC_MAX - WRITE_DELAY;
|
||||
response_size = WRITE_RSP_SIZE;
|
||||
break;
|
||||
|
||||
default:
|
||||
poll_delay = 0;
|
||||
poll_timeout = SHA204_COMMAND_EXEC_MAX;
|
||||
response_size = rx_size;
|
||||
}
|
||||
|
||||
// Assemble command.
|
||||
len = datalen1 + SHA204_CMD_SIZE_MIN;
|
||||
p_buffer = tx_buffer;
|
||||
*p_buffer++ = len;
|
||||
*p_buffer++ = op_code;
|
||||
*p_buffer++ = param1;
|
||||
*p_buffer++ = param2 & 0xFF;
|
||||
*p_buffer++ = param2 >> 8;
|
||||
|
||||
if (datalen1 > 0) {
|
||||
memcpy(p_buffer, data1, datalen1);
|
||||
p_buffer += datalen1;
|
||||
}
|
||||
|
||||
sha204c_calculate_crc(len - SHA204_CRC_SIZE, tx_buffer, p_buffer);
|
||||
|
||||
// Send command and receive response.
|
||||
return sha204c_send_and_receive(&tx_buffer[0], response_size,
|
||||
&rx_buffer[0], poll_delay, poll_timeout);
|
||||
}
|
||||
|
||||
uint8_t atsha204_getSerialNumber(uint8_t * response)
|
||||
{
|
||||
uint8_t readCommand[READ_COUNT];
|
||||
uint8_t readResponse[READ_4_RSP_SIZE];
|
||||
|
||||
/* read from bytes 0->3 of config zone */
|
||||
uint8_t returnCode = atsha204_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN03);
|
||||
if (!returnCode) {
|
||||
for (int i=0; i<4; i++) {// store bytes 0-3 into respones array
|
||||
response[i] = readResponse[SHA204_BUFFER_POS_DATA+i];
|
||||
}
|
||||
|
||||
/* read from bytes 8->11 of config zone */
|
||||
returnCode = atsha204_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN47);
|
||||
|
||||
for (int i=4; i<8; i++) {// store bytes 4-7 of SN into response array
|
||||
response[i] = readResponse[SHA204_BUFFER_POS_DATA+(i-4)];
|
||||
}
|
||||
|
||||
if (!returnCode) {
|
||||
/* Finally if last two reads were successful, read byte 8 of the SN */
|
||||
returnCode = atsha204_read(readCommand, readResponse, SHA204_ZONE_CONFIG, ADDRESS_SN8);
|
||||
response[8] = readResponse[SHA204_BUFFER_POS_DATA]; // Byte 8 of SN should always be 0xEE
|
||||
}
|
||||
}
|
||||
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
uint8_t atsha204_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address)
|
||||
{
|
||||
uint8_t rx_size;
|
||||
|
||||
address >>= 2;
|
||||
|
||||
tx_buffer[SHA204_COUNT_IDX] = READ_COUNT;
|
||||
tx_buffer[SHA204_OPCODE_IDX] = SHA204_READ;
|
||||
tx_buffer[READ_ZONE_IDX] = zone;
|
||||
tx_buffer[READ_ADDR_IDX] = (uint8_t) (address & SHA204_ADDRESS_MASK);
|
||||
tx_buffer[READ_ADDR_IDX + 1] = 0;
|
||||
|
||||
rx_size = (zone & SHA204_ZONE_COUNT_FLAG) ? READ_32_RSP_SIZE : READ_4_RSP_SIZE;
|
||||
|
||||
return sha204c_send_and_receive(&tx_buffer[0], rx_size, &rx_buffer[0], READ_DELAY,
|
||||
READ_EXEC_MAX - READ_DELAY);
|
||||
}
|
||||
253
lib/MySensors/drivers/ATSHA204/ATSHA204.h
Normal file
253
lib/MySensors/drivers/ATSHA204/ATSHA204.h
Normal file
@@ -0,0 +1,253 @@
|
||||
#ifndef ATSHA204_H
|
||||
#define ATSHA204_H
|
||||
#if !DOXYGEN
|
||||
#include <Arduino.h>
|
||||
|
||||
/* This is a scaled down variant of the ATSHA204 library, tweaked to meet the specific needs of the MySensors library. */
|
||||
|
||||
/* Library return codes */
|
||||
#define SHA204_SUCCESS ((uint8_t) 0x00) //!< Function succeeded.
|
||||
#define SHA204_PARSE_ERROR ((uint8_t) 0xD2) //!< response status byte indicates parsing error
|
||||
#define SHA204_CMD_FAIL ((uint8_t) 0xD3) //!< response status byte indicates command execution error
|
||||
#define SHA204_STATUS_CRC ((uint8_t) 0xD4) //!< response status byte indicates CRC error
|
||||
#define SHA204_STATUS_UNKNOWN ((uint8_t) 0xD5) //!< response status byte is unknown
|
||||
#define SHA204_FUNC_FAIL ((uint8_t) 0xE0) //!< Function could not execute due to incorrect condition / state.
|
||||
#define SHA204_GEN_FAIL ((uint8_t) 0xE1) //!< unspecified error
|
||||
#define SHA204_BAD_PARAM ((uint8_t) 0xE2) //!< bad argument (out of range, null pointer, etc.)
|
||||
#define SHA204_INVALID_ID ((uint8_t) 0xE3) //!< invalid device id, id not set
|
||||
#define SHA204_INVALID_SIZE ((uint8_t) 0xE4) //!< Count value is out of range or greater than buffer size.
|
||||
#define SHA204_BAD_CRC ((uint8_t) 0xE5) //!< incorrect CRC received
|
||||
#define SHA204_RX_FAIL ((uint8_t) 0xE6) //!< Timed out while waiting for response. Number of bytes received is > 0.
|
||||
#define SHA204_RX_NO_RESPONSE ((uint8_t) 0xE7) //!< Not an error while the Command layer is polling for a command response.
|
||||
#define SHA204_RESYNC_WITH_WAKEUP ((uint8_t) 0xE8) //!< re-synchronization succeeded, but only after generating a Wake-up
|
||||
|
||||
#define SHA204_COMM_FAIL ((uint8_t) 0xF0) //!< Communication with device failed. Same as in hardware dependent modules.
|
||||
#define SHA204_TIMEOUT ((uint8_t) 0xF1) //!< Timed out while waiting for response. Number of bytes received is 0.
|
||||
|
||||
/* bitbang_config.h */
|
||||
|
||||
#define PORT_ACCESS_TIME (630) //! time it takes to toggle the pin at CPU clock of 16 MHz (ns)
|
||||
#define START_PULSE_WIDTH (4340) //! width of start pulse (ns)
|
||||
#define BIT_DELAY (4) //! delay macro for width of one pulse (start pulse or zero pulse, in ns)
|
||||
#define RX_TX_DELAY (15) //! turn around time when switching from receive to transmit
|
||||
#define START_PULSE_TIME_OUT (255) //! This value is decremented while waiting for the falling edge of a start pulse.
|
||||
#define ZERO_PULSE_TIME_OUT (26) //! This value is decremented while waiting for the falling edge of a zero pulse.
|
||||
|
||||
/* swi_phys.h */
|
||||
|
||||
#define SWI_FUNCTION_RETCODE_SUCCESS ((uint8_t) 0x00) //!< Communication with device succeeded.
|
||||
#define SWI_FUNCTION_RETCODE_TIMEOUT ((uint8_t) 0xF1) //!< Communication timed out.
|
||||
#define SWI_FUNCTION_RETCODE_RX_FAIL ((uint8_t) 0xF9) //!< Communication failed after at least one byte was received.
|
||||
|
||||
/* sha204_physical.h */
|
||||
|
||||
#define SHA204_RSP_SIZE_MIN ((uint8_t) 4) //!< minimum number of bytes in response
|
||||
#define SHA204_RSP_SIZE_MAX ((uint8_t) 35) //!< maximum size of response packet
|
||||
#define SHA204_BUFFER_POS_COUNT (0) //!< buffer index of count byte in command or response
|
||||
#define SHA204_BUFFER_POS_DATA (1) //!< buffer index of data in response
|
||||
#define SHA204_WAKEUP_PULSE_WIDTH (uint8_t) (6.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5) //! width of Wakeup pulse in 10 us units
|
||||
#define SHA204_WAKEUP_DELAY (uint8_t) (3.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5) //! delay between Wakeup pulse and communication in ms
|
||||
|
||||
/* sha204_swi.c */
|
||||
#define SHA204_SWI_FLAG_CMD ((uint8_t) 0x77) //!< flag preceding a command
|
||||
#define SHA204_SWI_FLAG_TX ((uint8_t) 0x88) //!< flag requesting a response
|
||||
#define SHA204_SWI_FLAG_IDLE ((uint8_t) 0xBB) //!< flag requesting to go into Idle mode
|
||||
#define SHA204_SWI_FLAG_SLEEP ((uint8_t) 0xCC) //!< flag requesting to go into Sleep mode
|
||||
|
||||
/* sha204_comm_marshaling.h */
|
||||
// command op-code definitions
|
||||
#define SHA204_GENDIG ((uint8_t) 0x15) //!< GenDig command op-code
|
||||
#define SHA204_HMAC ((uint8_t) 0x11) //!< HMAC command op-code
|
||||
#define SHA204_NONCE ((uint8_t) 0x16) //!< Nonce command op-code
|
||||
#define SHA204_RANDOM ((uint8_t) 0x1B) //!< Random command op-code
|
||||
#define SHA204_READ ((uint8_t) 0x02) //!< Read command op-code
|
||||
#define SHA204_SHA ((uint8_t) 0x47) //!< SHA command op-code
|
||||
#define SHA204_WRITE ((uint8_t) 0x12) //!< Write command op-code
|
||||
|
||||
// packet size definitions
|
||||
#define SHA204_RSP_SIZE_VAL ((uint8_t) 7) //!< size of response packet containing four bytes of data
|
||||
|
||||
// definitions for command packet indexes common to all commands
|
||||
#define SHA204_COUNT_IDX ( 0) //!< command packet index for count
|
||||
#define SHA204_OPCODE_IDX ( 1) //!< command packet index for op-code
|
||||
#define SHA204_PARAM1_IDX ( 2) //!< command packet index for first parameter
|
||||
#define SHA204_PARAM2_IDX ( 3) //!< command packet index for second parameter
|
||||
#define SHA204_DATA_IDX ( 5) //!< command packet index for second parameter
|
||||
|
||||
// zone definitions
|
||||
#define SHA204_ZONE_CONFIG ((uint8_t) 0x00) //!< Configuration zone
|
||||
#define SHA204_ZONE_OTP ((uint8_t) 0x01) //!< OTP (One Time Programming) zone
|
||||
#define SHA204_ZONE_DATA ((uint8_t) 0x02) //!< Data zone
|
||||
#define SHA204_ZONE_MASK ((uint8_t) 0x03) //!< Zone mask
|
||||
#define SHA204_ZONE_COUNT_FLAG ((uint8_t) 0x80) //!< Zone bit 7 set: Access 32 bytes, otherwise 4 bytes.
|
||||
#define SHA204_ZONE_ACCESS_4 ((uint8_t) 4) //!< Read or write 4 bytes.
|
||||
#define SHA204_ZONE_ACCESS_32 ((uint8_t) 32) //!< Read or write 32 bytes.
|
||||
#define SHA204_ADDRESS_MASK_CONFIG ( 0x001F) //!< Address bits 5 to 7 are 0 for Configuration zone.
|
||||
#define SHA204_ADDRESS_MASK_OTP ( 0x000F) //!< Address bits 4 to 7 are 0 for OTP zone.
|
||||
#define SHA204_ADDRESS_MASK ( 0x007F) //!< Address bit 7 to 15 are always 0.
|
||||
|
||||
// GenDig command definitions
|
||||
#define GENDIG_ZONE_IDX SHA204_PARAM1_IDX //!< GenDig command index for zone
|
||||
#define GENDIG_KEYID_IDX SHA204_PARAM2_IDX //!< GenDig command index for key id
|
||||
#define GENDIG_DATA_IDX SHA204_DATA_IDX //!< GenDig command index for optional data
|
||||
#define GENDIG_COUNT SHA204_CMD_SIZE_MIN //!< GenDig command packet size without "other data"
|
||||
#define GENDIG_COUNT_DATA (11) //!< GenDig command packet size with "other data"
|
||||
#define GENDIG_OTHER_DATA_SIZE (4) //!< GenDig size of "other data"
|
||||
#define GENDIG_ZONE_CONFIG ((uint8_t) 0) //!< GenDig zone id config
|
||||
#define GENDIG_ZONE_OTP ((uint8_t) 1) //!< GenDig zone id OTP
|
||||
#define GENDIG_ZONE_DATA ((uint8_t) 2) //!< GenDig zone id data
|
||||
|
||||
// HMAC command definitions
|
||||
#define HMAC_MODE_IDX SHA204_PARAM1_IDX //!< HMAC command index for mode
|
||||
#define HMAC_KEYID_IDX SHA204_PARAM2_IDX //!< HMAC command index for key id
|
||||
#define HMAC_COUNT SHA204_CMD_SIZE_MIN //!< HMAC command packet size
|
||||
#define HMAC_MODE_MASK ((uint8_t) 0x74) //!< HMAC mode bits 0, 1, 3, and 7 are 0.
|
||||
#define HMAC_MODE_SOURCE_FLAG_MATCH ((uint8_t) 0x04) //!< HMAC mode bit 2: match TempKey.SourceFlag
|
||||
|
||||
// Nonce command definitions
|
||||
#define NONCE_MODE_IDX SHA204_PARAM1_IDX //!< Nonce command index for mode
|
||||
#define NONCE_PARAM2_IDX SHA204_PARAM2_IDX //!< Nonce command index for 2. parameter
|
||||
#define NONCE_INPUT_IDX SHA204_DATA_IDX //!< Nonce command index for input data
|
||||
#define NONCE_COUNT_SHORT (27) //!< Nonce command packet size for 20 bytes of data
|
||||
#define NONCE_COUNT_LONG (39) //!< Nonce command packet size for 32 bytes of data
|
||||
#define NONCE_MODE_MASK ((uint8_t) 3) //!< Nonce mode bits 2 to 7 are 0.
|
||||
#define NONCE_MODE_SEED_UPDATE ((uint8_t) 0x00) //!< Nonce mode: update seed
|
||||
#define NONCE_MODE_NO_SEED_UPDATE ((uint8_t) 0x01) //!< Nonce mode: do not update seed
|
||||
#define NONCE_MODE_INVALID ((uint8_t) 0x02) //!< Nonce mode 2 is invalid.
|
||||
#define NONCE_MODE_PASSTHROUGH ((uint8_t) 0x03) //!< Nonce mode: pass-through
|
||||
#define NONCE_NUMIN_SIZE (20) //!< Nonce data length
|
||||
#define NONCE_NUMIN_SIZE_PASSTHROUGH (32) //!< Nonce data length in pass-through mode (mode = 3)
|
||||
|
||||
// Random command definitions
|
||||
#define RANDOM_MODE_IDX SHA204_PARAM1_IDX //!< Random command index for mode
|
||||
#define RANDOM_PARAM2_IDX SHA204_PARAM2_IDX //!< Random command index for 2. parameter
|
||||
#define RANDOM_COUNT SHA204_CMD_SIZE_MIN //!< Random command packet size
|
||||
#define RANDOM_SEED_UPDATE ((uint8_t) 0x00) //!< Random mode for automatic seed update
|
||||
#define RANDOM_NO_SEED_UPDATE ((uint8_t) 0x01) //!< Random mode for no seed update
|
||||
|
||||
// Read command definitions
|
||||
#define READ_ZONE_IDX SHA204_PARAM1_IDX //!< Read command index for zone
|
||||
#define READ_ADDR_IDX SHA204_PARAM2_IDX //!< Read command index for address
|
||||
#define READ_COUNT SHA204_CMD_SIZE_MIN //!< Read command packet size
|
||||
#define READ_ZONE_MASK ((uint8_t) 0x83) //!< Read zone bits 2 to 6 are 0.
|
||||
#define READ_ZONE_MODE_32_BYTES ((uint8_t) 0x80) //!< Read mode: 32 bytes
|
||||
|
||||
// SHA command definitions
|
||||
#define SHA_MODE_IDX SHA204_PARAM1_IDX //!< SHA command index for mode
|
||||
#define SHA_PARAM2_IDX SHA204_PARAM2_IDX //!< SHA command index for 2. parameter
|
||||
#define SHA_COUNT_SHORT SHA204_CMD_SIZE_MIN //!< SHA command packet size for init
|
||||
#define SHA_COUNT_LONG (71) //!< SHA command packet size for calculation
|
||||
#define SHA_MSG_SIZE (64) //!< SHA message data size
|
||||
#define SHA_INIT ((uint8_t) 0x00) //!< SHA mode for init
|
||||
#define SHA_CALC ((uint8_t) 0x01) //!< SHA mode for calculation
|
||||
|
||||
// Write command definitions
|
||||
#define WRITE_ZONE_IDX SHA204_PARAM1_IDX //!< Write command index for zone
|
||||
#define WRITE_ADDR_IDX SHA204_PARAM2_IDX //!< Write command index for address
|
||||
#define WRITE_VALUE_IDX SHA204_DATA_IDX //!< Write command index for data
|
||||
#define WRITE_MAC_VS_IDX ( 9) //!< Write command index for MAC following short data
|
||||
#define WRITE_MAC_VL_IDX (37) //!< Write command index for MAC following long data
|
||||
#define WRITE_COUNT_SHORT (11) //!< Write command packet size with short data and no MAC
|
||||
#define WRITE_COUNT_LONG (39) //!< Write command packet size with long data and no MAC
|
||||
#define WRITE_COUNT_SHORT_MAC (43) //!< Write command packet size with short data and MAC
|
||||
#define WRITE_COUNT_LONG_MAC (71) //!< Write command packet size with long data and MAC
|
||||
#define WRITE_MAC_SIZE (32) //!< Write MAC size
|
||||
#define WRITE_ZONE_MASK ((uint8_t) 0xC3) //!< Write zone bits 2 to 5 are 0.
|
||||
#define WRITE_ZONE_WITH_MAC ((uint8_t) 0x40) //!< Write zone bit 6: write encrypted with MAC
|
||||
|
||||
// Response size definitions
|
||||
#define GENDIG_RSP_SIZE SHA204_RSP_SIZE_MIN //!< response size of GenDig command
|
||||
#define HMAC_RSP_SIZE SHA204_RSP_SIZE_MAX //!< response size of HMAC command
|
||||
#define NONCE_RSP_SIZE_SHORT SHA204_RSP_SIZE_MIN //!< response size of Nonce command with mode[0:1] = 3
|
||||
#define NONCE_RSP_SIZE_LONG SHA204_RSP_SIZE_MAX //!< response size of Nonce command
|
||||
#define RANDOM_RSP_SIZE SHA204_RSP_SIZE_MAX //!< response size of Random command
|
||||
#define READ_4_RSP_SIZE SHA204_RSP_SIZE_VAL //!< response size of Read command when reading 4 bytes
|
||||
#define READ_32_RSP_SIZE SHA204_RSP_SIZE_MAX //!< response size of Read command when reading 32 bytes
|
||||
#define SHA_RSP_SIZE_SHORT SHA204_RSP_SIZE_MIN //!< response size of SHA command with mode[0:1] = 0
|
||||
#define SHA_RSP_SIZE_LONG SHA204_RSP_SIZE_MAX //!< response size of SHA command
|
||||
#define WRITE_RSP_SIZE SHA204_RSP_SIZE_MIN //!< response size of Write command
|
||||
|
||||
// command timing definitions for minimum execution times (ms)
|
||||
#define GENDIG_DELAY ((uint8_t) (11.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
|
||||
#define HMAC_DELAY ((uint8_t) (27.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
|
||||
#define NONCE_DELAY ((uint8_t) (22.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
|
||||
#define RANDOM_DELAY ((uint8_t) (11.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
|
||||
#define READ_DELAY ((uint8_t) ( 0.4 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
|
||||
#define SHA_DELAY ((uint8_t) (11.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
|
||||
#define WRITE_DELAY ((uint8_t) ( 4.0 * CPU_CLOCK_DEVIATION_NEGATIVE - 0.5))
|
||||
|
||||
// command timing definitions for maximum execution times (ms)
|
||||
#define GENDIG_EXEC_MAX ((uint8_t) (43.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
|
||||
#define HMAC_EXEC_MAX ((uint8_t) (69.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
|
||||
#define NONCE_EXEC_MAX ((uint8_t) (60.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
|
||||
#define RANDOM_EXEC_MAX ((uint8_t) (50.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
|
||||
#define READ_EXEC_MAX ((uint8_t) ( 4.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
|
||||
#define SHA_EXEC_MAX ((uint8_t) (22.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
|
||||
#define WRITE_EXEC_MAX ((uint8_t) (42.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5))
|
||||
|
||||
/* from sha204_config.h */
|
||||
|
||||
#define CPU_CLOCK_DEVIATION_POSITIVE (1.01)
|
||||
#define CPU_CLOCK_DEVIATION_NEGATIVE (0.99)
|
||||
#define SHA204_RETRY_COUNT (1)
|
||||
#define SWI_RECEIVE_TIME_OUT ((uint16_t) 163) //! #START_PULSE_TIME_OUT in us instead of loop counts
|
||||
#define SWI_US_PER_BYTE ((uint16_t) 313) //! It takes 312.5 us to send a byte (9 single-wire bits / 230400 Baud * 8 flag bits).
|
||||
#define SHA204_SYNC_TIMEOUT ((uint8_t) 85)//! delay before sending a transmit flag in the synchronization routine
|
||||
#define SHA204_RESPONSE_TIMEOUT ((uint16_t) SWI_RECEIVE_TIME_OUT + SWI_US_PER_BYTE) //! SWI response timeout is the sum of receive timeout and the time it takes to send the TX flag.
|
||||
|
||||
/* from sha204_comm.h */
|
||||
|
||||
#define SHA204_COMMAND_EXEC_MAX ((uint8_t) (69.0 * CPU_CLOCK_DEVIATION_POSITIVE + 0.5)) //! maximum command delay
|
||||
#define SHA204_CMD_SIZE_MIN ((uint8_t) 7) //! minimum number of bytes in command (from count byte to second CRC byte)
|
||||
#ifndef SHA204_CMD_SIZE_MAX
|
||||
#define SHA204_CMD_SIZE_MAX ((uint8_t) SHA_COUNT_LONG) //! maximum size of command packet (SHA)
|
||||
#endif
|
||||
#define SHA204_CRC_SIZE ((uint8_t) 2) //! number of CRC bytes
|
||||
#define SHA204_BUFFER_POS_STATUS (1) //! buffer index of status byte in status response
|
||||
#define SHA204_BUFFER_POS_DATA (1) //! buffer index of first data byte in data response
|
||||
#define SHA204_STATUS_BYTE_WAKEUP ((uint8_t) 0x11) //! command parse error
|
||||
#define SHA204_STATUS_BYTE_PARSE ((uint8_t) 0x03) //! command parse error
|
||||
#define SHA204_STATUS_BYTE_EXEC ((uint8_t) 0x0F) //! command execution error
|
||||
#define SHA204_STATUS_BYTE_COMM ((uint8_t) 0xFF) //! communication error
|
||||
|
||||
/* EEPROM Addresses */
|
||||
/* Configuration Zone */
|
||||
#define ADDRESS_SN03 0 // SN[0:3] are bytes 0->3 of configuration zone
|
||||
#define ADDRESS_RevNum 4 // bytes 4->7 of config zone are RevNum
|
||||
#define ADDRESS_SN47 8 // SN[4:7] are bytes 8->11 of config zone
|
||||
#define ADDRESS_SN8 12 // SN[8] is byte 12 of config zone, should be 0xEE
|
||||
#define ADDRESS_I2CEN 14 // I2C Enable, bit 0 represents I2C enable status
|
||||
#define ADDRESS_I2CADD 16 // Defines I2C address of SHA204
|
||||
#define ADDRESS_OTPMODE 18 // Sets the One-time-programmable mode
|
||||
#define ADDRESS_SELECTOR 19 // Controls writability of Selector
|
||||
|
||||
#define SHA204_SERIAL_SZ 9 // The number of bytes the serial number consists of
|
||||
|
||||
/* Low level HW access macros */
|
||||
/* function calls is not working, as it will have too much overhead */
|
||||
#if !defined(ARDUINO_ARCH_AVR) // For everything else than AVR use pinMode / digitalWrite
|
||||
#define SHA204_SET_OUTPUT() pinMode(device_pin, OUTPUT)
|
||||
#define SHA204_SET_INPUT() pinMode(device_pin, INPUT)
|
||||
#define SHA204_POUT_HIGH() digitalWrite(device_pin, HIGH)
|
||||
#define SHA204_POUT_LOW() digitalWrite(device_pin, LOW)
|
||||
#define SHA204_PIN_READ() digitalRead(device_pin)
|
||||
#else
|
||||
#define SHA204_SET_INPUT() *device_port_DDR &= ~device_pin
|
||||
#define SHA204_SET_OUTPUT() *device_port_DDR |= device_pin
|
||||
#define SHA204_POUT_HIGH() *device_port_OUT |= device_pin
|
||||
#define SHA204_POUT_LOW() *device_port_OUT &= ~device_pin
|
||||
#define SHA204_PIN_READ() (*device_port_IN & device_pin)
|
||||
#endif
|
||||
|
||||
void atsha204_init(uint8_t pin);
|
||||
void atsha204_idle(void);
|
||||
void atsha204_sleep(void);
|
||||
uint8_t atsha204_wakeup(uint8_t *response);
|
||||
uint8_t atsha204_execute(uint8_t op_code, uint8_t param1, uint16_t param2,
|
||||
uint8_t datalen1, uint8_t *data1, uint8_t tx_size,
|
||||
uint8_t *tx_buffer, uint8_t rx_size, uint8_t *rx_buffer);
|
||||
uint8_t atsha204_getSerialNumber(uint8_t *response);
|
||||
uint8_t atsha204_read(uint8_t *tx_buffer, uint8_t *rx_buffer, uint8_t zone, uint16_t address);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
377
lib/MySensors/drivers/AltSoftSerial/AltSoftSerial.cpp
Normal file
377
lib/MySensors/drivers/AltSoftSerial/AltSoftSerial.cpp
Normal file
@@ -0,0 +1,377 @@
|
||||
/* An Alternative Software Serial Library
|
||||
* http://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
|
||||
* Copyright (c) 2014 PJRC.COM, LLC, Paul Stoffregen, paul@pjrc.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// Revisions are now tracked on GitHub
|
||||
// https://github.com/PaulStoffregen/AltSoftSerial
|
||||
//
|
||||
// Version 1.2: Support Teensy 3.x
|
||||
//
|
||||
// Version 1.1: Improve performance in receiver code
|
||||
//
|
||||
// Version 1.0: Initial Release
|
||||
|
||||
|
||||
#include "AltSoftSerial.h"
|
||||
#include "config/AltSoftSerial_Boards.h"
|
||||
#include "config/AltSoftSerial_Timers.h"
|
||||
|
||||
/****************************************/
|
||||
/** Initialization **/
|
||||
/****************************************/
|
||||
|
||||
static uint16_t ticks_per_bit=0;
|
||||
bool AltSoftSerial::timing_error=false;
|
||||
|
||||
static uint8_t rx_state;
|
||||
static uint8_t rx_byte;
|
||||
static uint8_t rx_bit = 0;
|
||||
static uint16_t rx_target;
|
||||
static uint16_t rx_stop_ticks=0;
|
||||
static volatile uint8_t rx_buffer_head;
|
||||
static volatile uint8_t rx_buffer_tail;
|
||||
#define RX_BUFFER_SIZE 80
|
||||
static volatile uint8_t rx_buffer[RX_BUFFER_SIZE];
|
||||
|
||||
static volatile uint8_t tx_state=0;
|
||||
static uint8_t tx_byte;
|
||||
static uint8_t tx_bit;
|
||||
static volatile uint8_t tx_buffer_head;
|
||||
static volatile uint8_t tx_buffer_tail;
|
||||
#define TX_BUFFER_SIZE 68
|
||||
static volatile uint8_t tx_buffer[TX_BUFFER_SIZE];
|
||||
|
||||
|
||||
#ifndef INPUT_PULLUP
|
||||
#define INPUT_PULLUP INPUT
|
||||
#endif
|
||||
|
||||
#define MAX_COUNTS_PER_BIT 6241 // 65536 / 10.5
|
||||
|
||||
void AltSoftSerial::init(uint32_t cycles_per_bit)
|
||||
{
|
||||
//Serial.printf("cycles_per_bit = %d\n", cycles_per_bit);
|
||||
if (cycles_per_bit < MAX_COUNTS_PER_BIT) {
|
||||
CONFIG_TIMER_NOPRESCALE();
|
||||
} else {
|
||||
cycles_per_bit /= 8;
|
||||
//Serial.printf("cycles_per_bit/8 = %d\n", cycles_per_bit);
|
||||
if (cycles_per_bit < MAX_COUNTS_PER_BIT) {
|
||||
CONFIG_TIMER_PRESCALE_8();
|
||||
} else {
|
||||
#if defined(CONFIG_TIMER_PRESCALE_256)
|
||||
cycles_per_bit /= 32;
|
||||
//Serial.printf("cycles_per_bit/256 = %d\n", cycles_per_bit);
|
||||
if (cycles_per_bit < MAX_COUNTS_PER_BIT) {
|
||||
CONFIG_TIMER_PRESCALE_256();
|
||||
} else {
|
||||
return; // baud rate too low for AltSoftSerial
|
||||
}
|
||||
#elif defined(CONFIG_TIMER_PRESCALE_128)
|
||||
cycles_per_bit /= 16;
|
||||
//Serial.printf("cycles_per_bit/128 = %d\n", cycles_per_bit);
|
||||
if (cycles_per_bit < MAX_COUNTS_PER_BIT) {
|
||||
CONFIG_TIMER_PRESCALE_128();
|
||||
} else {
|
||||
return; // baud rate too low for AltSoftSerial
|
||||
}
|
||||
#else
|
||||
return; // baud rate too low for AltSoftSerial
|
||||
#endif
|
||||
}
|
||||
}
|
||||
ticks_per_bit = cycles_per_bit;
|
||||
rx_stop_ticks = cycles_per_bit * 37 / 4;
|
||||
hwPinMode(INPUT_CAPTURE_PIN, INPUT_PULLUP);
|
||||
hwDigitalWrite(OUTPUT_COMPARE_A_PIN, HIGH);
|
||||
hwPinMode(OUTPUT_COMPARE_A_PIN, OUTPUT);
|
||||
rx_state = 0;
|
||||
rx_buffer_head = 0;
|
||||
rx_buffer_tail = 0;
|
||||
tx_state = 0;
|
||||
tx_buffer_head = 0;
|
||||
tx_buffer_tail = 0;
|
||||
ENABLE_INT_INPUT_CAPTURE();
|
||||
}
|
||||
|
||||
void AltSoftSerial::end(void)
|
||||
{
|
||||
DISABLE_INT_COMPARE_B();
|
||||
DISABLE_INT_INPUT_CAPTURE();
|
||||
flushInput();
|
||||
flushOutput();
|
||||
DISABLE_INT_COMPARE_A();
|
||||
// TODO: restore timer to original settings?
|
||||
}
|
||||
|
||||
|
||||
/****************************************/
|
||||
/** Transmission **/
|
||||
/****************************************/
|
||||
|
||||
void AltSoftSerial::writeByte(uint8_t b)
|
||||
{
|
||||
uint8_t intr_state, head;
|
||||
|
||||
head = tx_buffer_head + 1;
|
||||
if (head >= TX_BUFFER_SIZE) {
|
||||
head = 0;
|
||||
}
|
||||
while (tx_buffer_tail == head) ; // wait until space in buffer
|
||||
intr_state = SREG;
|
||||
cli();
|
||||
if (tx_state) {
|
||||
tx_buffer[head] = b;
|
||||
tx_buffer_head = head;
|
||||
} else {
|
||||
tx_state = 1;
|
||||
tx_byte = b;
|
||||
tx_bit = 0;
|
||||
ENABLE_INT_COMPARE_A();
|
||||
CONFIG_MATCH_CLEAR();
|
||||
SET_COMPARE_A(GET_TIMER_COUNT() + 16);
|
||||
}
|
||||
SREG = intr_state;
|
||||
}
|
||||
|
||||
|
||||
ISR(COMPARE_A_INTERRUPT)
|
||||
{
|
||||
uint8_t state, byte, bit, head, tail;
|
||||
uint16_t target;
|
||||
|
||||
state = tx_state;
|
||||
byte = tx_byte;
|
||||
target = GET_COMPARE_A();
|
||||
while (state < 10) {
|
||||
target += ticks_per_bit;
|
||||
if (state < 9) {
|
||||
bit = byte & 1;
|
||||
} else {
|
||||
bit = 1; // stopbit
|
||||
}
|
||||
byte >>= 1;
|
||||
state++;
|
||||
if (bit != tx_bit) {
|
||||
if (bit) {
|
||||
CONFIG_MATCH_SET();
|
||||
} else {
|
||||
CONFIG_MATCH_CLEAR();
|
||||
}
|
||||
SET_COMPARE_A(target);
|
||||
tx_bit = bit;
|
||||
tx_byte = byte;
|
||||
tx_state = state;
|
||||
// TODO: how to detect timing_error?
|
||||
return;
|
||||
}
|
||||
}
|
||||
head = tx_buffer_head;
|
||||
tail = tx_buffer_tail;
|
||||
if (head == tail) {
|
||||
if (state == 10) {
|
||||
// Wait for final stop bit to finish
|
||||
tx_state = 11;
|
||||
SET_COMPARE_A(target + ticks_per_bit);
|
||||
} else {
|
||||
tx_state = 0;
|
||||
CONFIG_MATCH_NORMAL();
|
||||
DISABLE_INT_COMPARE_A();
|
||||
}
|
||||
} else {
|
||||
if (++tail >= TX_BUFFER_SIZE) {
|
||||
tail = 0;
|
||||
}
|
||||
tx_buffer_tail = tail;
|
||||
tx_byte = tx_buffer[tail];
|
||||
tx_bit = 0;
|
||||
CONFIG_MATCH_CLEAR();
|
||||
if (state == 10) {
|
||||
SET_COMPARE_A(target + ticks_per_bit);
|
||||
} else {
|
||||
SET_COMPARE_A(GET_TIMER_COUNT() + 16);
|
||||
}
|
||||
tx_state = 1;
|
||||
// TODO: how to detect timing_error?
|
||||
}
|
||||
}
|
||||
|
||||
void AltSoftSerial::flushOutput(void)
|
||||
{
|
||||
while (tx_state) /* wait */ ;
|
||||
}
|
||||
|
||||
|
||||
/****************************************/
|
||||
/** Reception **/
|
||||
/****************************************/
|
||||
|
||||
ISR(CAPTURE_INTERRUPT)
|
||||
{
|
||||
uint8_t state, bit;
|
||||
uint16_t capture;
|
||||
|
||||
capture = GET_INPUT_CAPTURE();
|
||||
bit = rx_bit;
|
||||
if (bit) {
|
||||
CONFIG_CAPTURE_FALLING_EDGE();
|
||||
rx_bit = 0;
|
||||
} else {
|
||||
CONFIG_CAPTURE_RISING_EDGE();
|
||||
rx_bit = 0x80;
|
||||
}
|
||||
state = rx_state;
|
||||
if (state == 0) {
|
||||
if (!bit) {
|
||||
uint16_t end = capture + rx_stop_ticks;
|
||||
SET_COMPARE_B(end);
|
||||
ENABLE_INT_COMPARE_B();
|
||||
rx_target = capture + ticks_per_bit + ticks_per_bit/2;
|
||||
rx_state = 1;
|
||||
}
|
||||
} else {
|
||||
uint16_t target = rx_target;
|
||||
const uint16_t offset_overflow = 65535 - ticks_per_bit;
|
||||
while (1) {
|
||||
const uint16_t offset = capture - target;
|
||||
if (offset > offset_overflow) {
|
||||
break;
|
||||
}
|
||||
rx_byte = (rx_byte >> 1) | rx_bit;
|
||||
target += ticks_per_bit;
|
||||
state++;
|
||||
if (state >= 9) {
|
||||
DISABLE_INT_COMPARE_B();
|
||||
uint8_t head = rx_buffer_head + 1;
|
||||
if (head >= RX_BUFFER_SIZE) {
|
||||
head = 0;
|
||||
}
|
||||
if (head != rx_buffer_tail) {
|
||||
rx_buffer[head] = rx_byte;
|
||||
rx_buffer_head = head;
|
||||
}
|
||||
CONFIG_CAPTURE_FALLING_EDGE();
|
||||
rx_bit = 0;
|
||||
rx_state = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
rx_target = target;
|
||||
rx_state = state;
|
||||
}
|
||||
//if (GET_TIMER_COUNT() - capture > ticks_per_bit) AltSoftSerial::timing_error = true;
|
||||
}
|
||||
|
||||
ISR(COMPARE_B_INTERRUPT)
|
||||
{
|
||||
uint8_t head, state, bit;
|
||||
|
||||
DISABLE_INT_COMPARE_B();
|
||||
CONFIG_CAPTURE_FALLING_EDGE();
|
||||
state = rx_state;
|
||||
bit = rx_bit ^ 0x80;
|
||||
while (state < 9) {
|
||||
rx_byte = (rx_byte >> 1) | bit;
|
||||
state++;
|
||||
}
|
||||
head = rx_buffer_head + 1;
|
||||
if (head >= RX_BUFFER_SIZE) {
|
||||
head = 0;
|
||||
}
|
||||
if (head != rx_buffer_tail) {
|
||||
rx_buffer[head] = rx_byte;
|
||||
rx_buffer_head = head;
|
||||
}
|
||||
rx_state = 0;
|
||||
CONFIG_CAPTURE_FALLING_EDGE();
|
||||
rx_bit = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int AltSoftSerial::read(void)
|
||||
{
|
||||
uint8_t head, tail, out;
|
||||
|
||||
head = rx_buffer_head;
|
||||
tail = rx_buffer_tail;
|
||||
if (head == tail) {
|
||||
return -1;
|
||||
}
|
||||
if (++tail >= RX_BUFFER_SIZE) {
|
||||
tail = 0;
|
||||
}
|
||||
out = rx_buffer[tail];
|
||||
rx_buffer_tail = tail;
|
||||
return out;
|
||||
}
|
||||
|
||||
int AltSoftSerial::peek(void)
|
||||
{
|
||||
uint8_t head, tail;
|
||||
|
||||
head = rx_buffer_head;
|
||||
tail = rx_buffer_tail;
|
||||
if (head == tail) {
|
||||
return -1;
|
||||
}
|
||||
if (++tail >= RX_BUFFER_SIZE) {
|
||||
tail = 0;
|
||||
}
|
||||
return rx_buffer[tail];
|
||||
}
|
||||
|
||||
int AltSoftSerial::available(void)
|
||||
{
|
||||
uint8_t head, tail;
|
||||
|
||||
head = rx_buffer_head;
|
||||
tail = rx_buffer_tail;
|
||||
if (head >= tail) {
|
||||
return head - tail;
|
||||
}
|
||||
return RX_BUFFER_SIZE + head - tail;
|
||||
}
|
||||
|
||||
void AltSoftSerial::flushInput(void)
|
||||
{
|
||||
rx_buffer_head = rx_buffer_tail;
|
||||
}
|
||||
|
||||
|
||||
#ifdef ALTSS_USE_FTM0
|
||||
void ftm0_isr(void)
|
||||
{
|
||||
uint32_t flags = FTM0_STATUS;
|
||||
FTM0_STATUS = 0;
|
||||
if (flags & (1<<0) && (FTM0_C0SC & 0x40)) {
|
||||
altss_compare_b_interrupt();
|
||||
}
|
||||
if (flags & (1<<5)) {
|
||||
altss_capture_interrupt();
|
||||
}
|
||||
if (flags & (1<<6) && (FTM0_C6SC & 0x40)) {
|
||||
altss_compare_a_interrupt();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
117
lib/MySensors/drivers/AltSoftSerial/AltSoftSerial.h
Normal file
117
lib/MySensors/drivers/AltSoftSerial/AltSoftSerial.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/* An Alternative Software Serial Library
|
||||
* http://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
|
||||
* Copyright (c) 2014 PJRC.COM, LLC, Paul Stoffregen, paul@pjrc.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef AltSoftSerial_h
|
||||
#define AltSoftSerial_h
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#if ARDUINO >= 100
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#include "pins_arduino.h"
|
||||
#endif
|
||||
|
||||
#if defined(__arm__) && defined(CORE_TEENSY)
|
||||
#define ALTSS_BASE_FREQ F_BUS
|
||||
#else
|
||||
#define ALTSS_BASE_FREQ F_CPU
|
||||
#endif
|
||||
|
||||
/** AltSoftSerial class */
|
||||
class AltSoftSerial : public Stream
|
||||
{
|
||||
public:
|
||||
AltSoftSerial() { } //!< Constructor
|
||||
~AltSoftSerial()
|
||||
{
|
||||
end(); //!< Destructor
|
||||
}
|
||||
static void begin(uint32_t baud)
|
||||
{
|
||||
init((ALTSS_BASE_FREQ + baud / 2) / baud); //!< begin
|
||||
}
|
||||
static void end(); //!< end
|
||||
int peek(); //!< peek
|
||||
int read(); //!< read
|
||||
int available(); //!< available
|
||||
#if ARDUINO >= 100
|
||||
size_t write(uint8_t byte)
|
||||
{
|
||||
writeByte(byte); //!< write
|
||||
return 1;
|
||||
}
|
||||
void flush()
|
||||
{
|
||||
flushOutput(); //!< flush
|
||||
}
|
||||
#else
|
||||
void write(uint8_t byte)
|
||||
{
|
||||
writeByte(byte); //!< write
|
||||
}
|
||||
void flush()
|
||||
{
|
||||
flushInput(); //!< flush
|
||||
}
|
||||
#endif
|
||||
using Print::write;
|
||||
static void flushInput(); //!< flushInput
|
||||
static void flushOutput(); //!< flushOutput
|
||||
// for drop-in compatibility with NewSoftSerial, rxPin & txPin ignored
|
||||
AltSoftSerial(uint8_t rxPin, uint8_t txPin, bool inverse = false)
|
||||
{
|
||||
(void)rxPin; //!< AltSoftSerial
|
||||
(void)txPin;
|
||||
(void)inverse;
|
||||
}
|
||||
bool listen()
|
||||
{
|
||||
return false; //!< listen
|
||||
}
|
||||
bool isListening()
|
||||
{
|
||||
return true; //!< isListening
|
||||
}
|
||||
bool overflow()
|
||||
{
|
||||
bool r = timing_error; //!< overflow
|
||||
timing_error = false;
|
||||
return r;
|
||||
}
|
||||
static int library_version()
|
||||
{
|
||||
return 1; //!< library_version
|
||||
}
|
||||
static void enable_timer0(bool enable)
|
||||
{
|
||||
(void)enable; //!< enable_timer0
|
||||
}
|
||||
static bool timing_error; //!< timing_error
|
||||
private:
|
||||
static void init(uint32_t cycles_per_bit);
|
||||
static void writeByte(uint8_t byte);
|
||||
};
|
||||
|
||||
#endif
|
||||
9
lib/MySensors/drivers/AltSoftSerial/README.md
Normal file
9
lib/MySensors/drivers/AltSoftSerial/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# AltSoftSerial Library
|
||||
|
||||
Improved software emulated serial, using hardware timers for precise signal
|
||||
timing and availability of CPU time for other libraries to respond to interrupts
|
||||
during data AltSoftSerial data transmission and reception.
|
||||
|
||||
http://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
|
||||
|
||||

|
||||
@@ -0,0 +1,147 @@
|
||||
/* An Alternative Software Serial Library
|
||||
* http://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
|
||||
* Copyright (c) 2014 PJRC.COM, LLC, Paul Stoffregen, paul@pjrc.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
// Teensy 2.0
|
||||
//
|
||||
#if defined(__AVR_ATmega32U4__) && defined(CORE_TEENSY)
|
||||
|
||||
//#define ALTSS_USE_TIMER1
|
||||
//#define INPUT_CAPTURE_PIN 22 // receive
|
||||
//#define OUTPUT_COMPARE_A_PIN 14 // transmit
|
||||
//#define OUTPUT_COMPARE_B_PIN 15 // unusable PWM
|
||||
//#define OUTPUT_COMPARE_C_PIN 4 // unusable PWM
|
||||
|
||||
#define ALTSS_USE_TIMER3
|
||||
#define INPUT_CAPTURE_PIN 10 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 9 // transmit
|
||||
|
||||
|
||||
|
||||
// Teensy++ 2.0
|
||||
//
|
||||
#elif defined(__AVR_AT90USB1286__) && defined(CORE_TEENSY)
|
||||
|
||||
#define ALTSS_USE_TIMER1
|
||||
#define INPUT_CAPTURE_PIN 4 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 25 // transmit
|
||||
#define OUTPUT_COMPARE_B_PIN 26 // unusable PWM
|
||||
#define OUTPUT_COMPARE_C_PIN 27 // unusable PWM
|
||||
|
||||
//#define ALTSS_USE_TIMER3
|
||||
//#define INPUT_CAPTURE_PIN 17 // receive
|
||||
//#define OUTPUT_COMPARE_A_PIN 16 // transmit
|
||||
//#define OUTPUT_COMPARE_B_PIN 15 // unusable PWM
|
||||
//#define OUTPUT_COMPARE_C_PIN 14 // unusable PWM
|
||||
|
||||
|
||||
// Teensy 3.x
|
||||
//
|
||||
#elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
|
||||
#define ALTSS_USE_FTM0
|
||||
#define INPUT_CAPTURE_PIN 20 // receive (FTM0_CH5)
|
||||
#define OUTPUT_COMPARE_A_PIN 21 // transmit (FTM0_CH6)
|
||||
#define OUTPUT_COMPARE_B_PIN 22 // unusable PWM (FTM0_CH0)
|
||||
#define OUTPUT_COMPARE_C_PIN 23 // PWM usable fixed freq
|
||||
#define OUTPUT_COMPARE_D_PIN 5 // PWM usable fixed freq
|
||||
#define OUTPUT_COMPARE_E_PIN 6 // PWM usable fixed freq
|
||||
#define OUTPUT_COMPARE_F_PIN 9 // PWM usable fixed freq
|
||||
#define OUTPUT_COMPARE_G_PIN 10 // PWM usable fixed freq
|
||||
|
||||
|
||||
// Wiring-S
|
||||
//
|
||||
#elif defined(__AVR_ATmega644P__) && defined(WIRING)
|
||||
|
||||
#define ALTSS_USE_TIMER1
|
||||
#define INPUT_CAPTURE_PIN 6 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 5 // transmit
|
||||
#define OUTPUT_COMPARE_B_PIN 4 // unusable PWM
|
||||
|
||||
|
||||
|
||||
// Arduino Uno, Duemilanove, LilyPad, etc
|
||||
//
|
||||
#elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__)
|
||||
|
||||
#define ALTSS_USE_TIMER1
|
||||
#define INPUT_CAPTURE_PIN 8 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 9 // transmit
|
||||
#define OUTPUT_COMPARE_B_PIN 10 // unusable PWM
|
||||
|
||||
|
||||
// Arduino Leonardo & Yun (from Cristian Maglie)
|
||||
//
|
||||
#elif defined(ARDUINO_AVR_YUN) || defined(ARDUINO_AVR_LEONARDO) || defined(__AVR_ATmega32U4__)
|
||||
|
||||
//#define ALTSS_USE_TIMER1
|
||||
//#define INPUT_CAPTURE_PIN 4 // receive
|
||||
//#define OUTPUT_COMPARE_A_PIN 9 // transmit
|
||||
//#define OUTPUT_COMPARE_B_PIN 10 // unusable PWM
|
||||
//#define OUTPUT_COMPARE_C_PIN 11 // unusable PWM
|
||||
|
||||
#define ALTSS_USE_TIMER3
|
||||
#define INPUT_CAPTURE_PIN 13 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 5 // transmit
|
||||
|
||||
|
||||
// Arduino Mega
|
||||
//
|
||||
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
||||
|
||||
//#define ALTSS_USE_TIMER4
|
||||
//#define INPUT_CAPTURE_PIN 49 // receive
|
||||
//#define OUTPUT_COMPARE_A_PIN 6 // transmit
|
||||
//#define OUTPUT_COMPARE_B_PIN 7 // unusable PWM
|
||||
//#define OUTPUT_COMPARE_C_PIN 8 // unusable PWM
|
||||
|
||||
#define ALTSS_USE_TIMER5
|
||||
#define INPUT_CAPTURE_PIN 48 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 46 // transmit
|
||||
#define OUTPUT_COMPARE_B_PIN 45 // unusable PWM
|
||||
#define OUTPUT_COMPARE_C_PIN 44 // unusable PWM
|
||||
|
||||
|
||||
|
||||
// EnviroDIY Mayfly, Sodaq Mbili
|
||||
#elif defined ARDUINO_AVR_ENVIRODIY_MAYFLY || defined ARDUINO_AVR_SODAQ_MBILI
|
||||
#define ALTSS_USE_TIMER1
|
||||
#define INPUT_CAPTURE_PIN 6 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 5 // transmit
|
||||
#define OUTPUT_COMPARE_B_PIN 4 // unusable PWM
|
||||
|
||||
|
||||
|
||||
// Sanguino, Mighty 1284
|
||||
#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284__)
|
||||
#define ALTSS_USE_TIMER1
|
||||
#define INPUT_CAPTURE_PIN 14 // receive
|
||||
#define OUTPUT_COMPARE_A_PIN 13 // transmit
|
||||
#define OUTPUT_COMPARE_B_PIN 12 // unusable PWM
|
||||
|
||||
|
||||
|
||||
// Unknown board
|
||||
#else
|
||||
#error "Please define your board timer and pins"
|
||||
#endif
|
||||
@@ -0,0 +1,187 @@
|
||||
/* An Alternative Software Serial Library
|
||||
* http://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
|
||||
* Copyright (c) 2014 PJRC.COM, LLC, Paul Stoffregen, paul@pjrc.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#if defined(ALTSS_USE_TIMER1)
|
||||
#define CONFIG_TIMER_NOPRESCALE() (TIMSK1 = 0, TCCR1A = 0, TCCR1B = (1<<ICNC1) | (1<<CS10))
|
||||
#define CONFIG_TIMER_PRESCALE_8() (TIMSK1 = 0, TCCR1A = 0, TCCR1B = (1<<ICNC1) | (1<<CS11))
|
||||
#define CONFIG_TIMER_PRESCALE_256() (TIMSK1 = 0, TCCR1A = 0, TCCR1B = (1<<ICNC1) | (1<<CS12))
|
||||
#define CONFIG_MATCH_NORMAL() (TCCR1A = TCCR1A & ~((1<<COM1A1) | (1<<COM1A0)))
|
||||
#define CONFIG_MATCH_TOGGLE() (TCCR1A = (TCCR1A & ~(1<<COM1A1)) | (1<<COM1A0))
|
||||
#define CONFIG_MATCH_CLEAR() (TCCR1A = (TCCR1A | (1<<COM1A1)) & ~(1<<COM1A0))
|
||||
#define CONFIG_MATCH_SET() (TCCR1A = TCCR1A | ((1<<COM1A1) | (1<<COM1A0)))
|
||||
#define CONFIG_CAPTURE_FALLING_EDGE() (TCCR1B &= ~(1<<ICES1))
|
||||
#define CONFIG_CAPTURE_RISING_EDGE() (TCCR1B |= (1<<ICES1))
|
||||
#define ENABLE_INT_INPUT_CAPTURE() (TIFR1 = (1<<ICF1), TIMSK1 = (1<<ICIE1))
|
||||
#define ENABLE_INT_COMPARE_A() (TIFR1 = (1<<OCF1A), TIMSK1 |= (1<<OCIE1A))
|
||||
#define ENABLE_INT_COMPARE_B() (TIFR1 = (1<<OCF1B), TIMSK1 |= (1<<OCIE1B))
|
||||
#define DISABLE_INT_INPUT_CAPTURE() (TIMSK1 &= ~(1<<ICIE1))
|
||||
#define DISABLE_INT_COMPARE_A() (TIMSK1 &= ~(1<<OCIE1A))
|
||||
#define DISABLE_INT_COMPARE_B() (TIMSK1 &= ~(1<<OCIE1B))
|
||||
#define GET_TIMER_COUNT() (TCNT1)
|
||||
#define GET_INPUT_CAPTURE() (ICR1)
|
||||
#define GET_COMPARE_A() (OCR1A)
|
||||
#define GET_COMPARE_B() (OCR1B)
|
||||
#define SET_COMPARE_A(val) (OCR1A = (val))
|
||||
#define SET_COMPARE_B(val) (OCR1B = (val))
|
||||
#define CAPTURE_INTERRUPT TIMER1_CAPT_vect
|
||||
#define COMPARE_A_INTERRUPT TIMER1_COMPA_vect
|
||||
#define COMPARE_B_INTERRUPT TIMER1_COMPB_vect
|
||||
|
||||
|
||||
#elif defined(ALTSS_USE_TIMER3)
|
||||
#define CONFIG_TIMER_NOPRESCALE() (TIMSK3 = 0, TCCR3A = 0, TCCR3B = (1<<ICNC3) | (1<<CS30))
|
||||
#define CONFIG_TIMER_PRESCALE_8() (TIMSK3 = 0, TCCR3A = 0, TCCR3B = (1<<ICNC3) | (1<<CS31))
|
||||
#define CONFIG_TIMER_PRESCALE_256() (TIMSK3 = 0, TCCR3A = 0, TCCR3B = (1<<ICNC3) | (1<<CS32))
|
||||
#define CONFIG_MATCH_NORMAL() (TCCR3A = TCCR3A & ~((1<<COM3A1) | (1<<COM3A0)))
|
||||
#define CONFIG_MATCH_TOGGLE() (TCCR3A = (TCCR3A & ~(1<<COM3A1)) | (1<<COM3A0))
|
||||
#define CONFIG_MATCH_CLEAR() (TCCR3A = (TCCR3A | (1<<COM3A1)) & ~(1<<COM3A0))
|
||||
#define CONFIG_MATCH_SET() (TCCR3A = TCCR3A | ((1<<COM3A1) | (1<<COM3A0)))
|
||||
#define CONFIG_CAPTURE_FALLING_EDGE() (TCCR3B &= ~(1<<ICES3))
|
||||
#define CONFIG_CAPTURE_RISING_EDGE() (TCCR3B |= (1<<ICES3))
|
||||
#define ENABLE_INT_INPUT_CAPTURE() (TIFR3 = (1<<ICF3), TIMSK3 = (1<<ICIE3))
|
||||
#define ENABLE_INT_COMPARE_A() (TIFR3 = (1<<OCF3A), TIMSK3 |= (1<<OCIE3A))
|
||||
#define ENABLE_INT_COMPARE_B() (TIFR3 = (1<<OCF3B), TIMSK3 |= (1<<OCIE3B))
|
||||
#define DISABLE_INT_INPUT_CAPTURE() (TIMSK3 &= ~(1<<ICIE3))
|
||||
#define DISABLE_INT_COMPARE_A() (TIMSK3 &= ~(1<<OCIE3A))
|
||||
#define DISABLE_INT_COMPARE_B() (TIMSK3 &= ~(1<<OCIE3B))
|
||||
#define GET_TIMER_COUNT() (TCNT3)
|
||||
#define GET_INPUT_CAPTURE() (ICR3)
|
||||
#define GET_COMPARE_A() (OCR3A)
|
||||
#define GET_COMPARE_B() (OCR3B)
|
||||
#define SET_COMPARE_A(val) (OCR3A = (val))
|
||||
#define SET_COMPARE_B(val) (OCR3B = (val))
|
||||
#define CAPTURE_INTERRUPT TIMER3_CAPT_vect
|
||||
#define COMPARE_A_INTERRUPT TIMER3_COMPA_vect
|
||||
#define COMPARE_B_INTERRUPT TIMER3_COMPB_vect
|
||||
|
||||
|
||||
#elif defined(ALTSS_USE_TIMER4)
|
||||
#define CONFIG_TIMER_NOPRESCALE() (TIMSK4 = 0, TCCR4A = 0, TCCR4B = (1<<ICNC4) | (1<<CS40))
|
||||
#define CONFIG_TIMER_PRESCALE_8() (TIMSK4 = 0, TCCR4A = 0, TCCR4B = (1<<ICNC4) | (1<<CS41))
|
||||
#define CONFIG_TIMER_PRESCALE_256() (TIMSK4 = 0, TCCR4A = 0, TCCR4B = (1<<ICNC4) | (1<<CS42))
|
||||
#define CONFIG_MATCH_NORMAL() (TCCR4A = TCCR4A & ~((1<<COM4A1) | (1<<COM4A0)))
|
||||
#define CONFIG_MATCH_TOGGLE() (TCCR4A = (TCCR4A & ~(1<<COM4A1)) | (1<<COM4A0))
|
||||
#define CONFIG_MATCH_CLEAR() (TCCR4A = (TCCR4A | (1<<COM4A1)) & ~(1<<COM4A0))
|
||||
#define CONFIG_MATCH_SET() (TCCR4A = TCCR4A | ((1<<COM4A1) | (1<<COM4A0)))
|
||||
#define CONFIG_CAPTURE_FALLING_EDGE() (TCCR4B &= ~(1<<ICES4))
|
||||
#define CONFIG_CAPTURE_RISING_EDGE() (TCCR4B |= (1<<ICES4))
|
||||
#define ENABLE_INT_INPUT_CAPTURE() (TIFR4 = (1<<ICF4), TIMSK4 = (1<<ICIE4))
|
||||
#define ENABLE_INT_COMPARE_A() (TIFR4 = (1<<OCF4A), TIMSK4 |= (1<<OCIE4A))
|
||||
#define ENABLE_INT_COMPARE_B() (TIFR4 = (1<<OCF4B), TIMSK4 |= (1<<OCIE4B))
|
||||
#define DISABLE_INT_INPUT_CAPTURE() (TIMSK4 &= ~(1<<ICIE4))
|
||||
#define DISABLE_INT_COMPARE_A() (TIMSK4 &= ~(1<<OCIE4A))
|
||||
#define DISABLE_INT_COMPARE_B() (TIMSK4 &= ~(1<<OCIE4B))
|
||||
#define GET_TIMER_COUNT() (TCNT4)
|
||||
#define GET_INPUT_CAPTURE() (ICR4)
|
||||
#define GET_COMPARE_A() (OCR4A)
|
||||
#define GET_COMPARE_B() (OCR4B)
|
||||
#define SET_COMPARE_A(val) (OCR4A = (val))
|
||||
#define SET_COMPARE_B(val) (OCR4B = (val))
|
||||
#define CAPTURE_INTERRUPT TIMER4_CAPT_vect
|
||||
#define COMPARE_A_INTERRUPT TIMER4_COMPA_vect
|
||||
#define COMPARE_B_INTERRUPT TIMER4_COMPB_vect
|
||||
|
||||
|
||||
#elif defined(ALTSS_USE_TIMER5)
|
||||
#define CONFIG_TIMER_NOPRESCALE() (TIMSK5 = 0, TCCR5A = 0, TCCR5B = (1<<ICNC5) | (1<<CS50))
|
||||
#define CONFIG_TIMER_PRESCALE_8() (TIMSK5 = 0, TCCR5A = 0, TCCR5B = (1<<ICNC5) | (1<<CS51))
|
||||
#define CONFIG_TIMER_PRESCALE_256() (TIMSK5 = 0, TCCR5A = 0, TCCR5B = (1<<ICNC5) | (1<<CS52))
|
||||
#define CONFIG_MATCH_NORMAL() (TCCR5A = TCCR5A & ~((1<<COM5A1) | (1<<COM5A0)))
|
||||
#define CONFIG_MATCH_TOGGLE() (TCCR5A = (TCCR5A & ~(1<<COM5A1)) | (1<<COM5A0))
|
||||
#define CONFIG_MATCH_CLEAR() (TCCR5A = (TCCR5A | (1<<COM5A1)) & ~(1<<COM5A0))
|
||||
#define CONFIG_MATCH_SET() (TCCR5A = TCCR5A | ((1<<COM5A1) | (1<<COM5A0)))
|
||||
#define CONFIG_CAPTURE_FALLING_EDGE() (TCCR5B &= ~(1<<ICES5))
|
||||
#define CONFIG_CAPTURE_RISING_EDGE() (TCCR5B |= (1<<ICES5))
|
||||
#define ENABLE_INT_INPUT_CAPTURE() (TIFR5 = (1<<ICF5), TIMSK5 = (1<<ICIE5))
|
||||
#define ENABLE_INT_COMPARE_A() (TIFR5 = (1<<OCF5A), TIMSK5 |= (1<<OCIE5A))
|
||||
#define ENABLE_INT_COMPARE_B() (TIFR5 = (1<<OCF5B), TIMSK5 |= (1<<OCIE5B))
|
||||
#define DISABLE_INT_INPUT_CAPTURE() (TIMSK5 &= ~(1<<ICIE5))
|
||||
#define DISABLE_INT_COMPARE_A() (TIMSK5 &= ~(1<<OCIE5A))
|
||||
#define DISABLE_INT_COMPARE_B() (TIMSK5 &= ~(1<<OCIE5B))
|
||||
#define GET_TIMER_COUNT() (TCNT5)
|
||||
#define GET_INPUT_CAPTURE() (ICR5)
|
||||
#define GET_COMPARE_A() (OCR5A)
|
||||
#define GET_COMPARE_B() (OCR5B)
|
||||
#define SET_COMPARE_A(val) (OCR5A = (val))
|
||||
#define SET_COMPARE_B(val) (OCR5B = (val))
|
||||
#define CAPTURE_INTERRUPT TIMER5_CAPT_vect
|
||||
#define COMPARE_A_INTERRUPT TIMER5_COMPA_vect
|
||||
#define COMPARE_B_INTERRUPT TIMER5_COMPB_vect
|
||||
|
||||
|
||||
#elif defined(ALTSS_USE_FTM0)
|
||||
// CH5 = input capture (input, pin 20)
|
||||
// CH6 = compare a (output, pin 21)
|
||||
// CH0 = compare b (input timeout)
|
||||
#define CONFIG_TIMER_NOPRESCALE() FTM0_SC = 0; FTM0_CNT = 0; FTM0_MOD = 0xFFFF; \
|
||||
FTM0_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0); \
|
||||
digitalWriteFast(21, HIGH); \
|
||||
NVIC_SET_PRIORITY(IRQ_FTM0, 48); \
|
||||
FTM0_C0SC = 0x18; \
|
||||
NVIC_ENABLE_IRQ(IRQ_FTM0);
|
||||
#define CONFIG_TIMER_PRESCALE_8() FTM0_SC = 0; FTM0_CNT = 0; FTM0_MOD = 0xFFFF; \
|
||||
FTM0_SC = FTM_SC_CLKS(1) | FTM_SC_PS(3); \
|
||||
digitalWriteFast(21, HIGH); \
|
||||
NVIC_SET_PRIORITY(IRQ_FTM0, 48); \
|
||||
FTM0_C0SC = 0x18; \
|
||||
NVIC_ENABLE_IRQ(IRQ_FTM0);
|
||||
#define CONFIG_TIMER_PRESCALE_128() FTM0_SC = 0; FTM0_CNT = 0; FTM0_MOD = 0xFFFF; \
|
||||
FTM0_SC = FTM_SC_CLKS(1) | FTM_SC_PS(7); \
|
||||
digitalWriteFast(21, HIGH); \
|
||||
NVIC_SET_PRIORITY(IRQ_FTM0, 48); \
|
||||
FTM0_C0SC = 0x18; \
|
||||
NVIC_ENABLE_IRQ(IRQ_FTM0);
|
||||
#define CONFIG_MATCH_NORMAL() (FTM0_C6SC = 0)
|
||||
#define CONFIG_MATCH_TOGGLE() (FTM0_C6SC = (FTM0_C6SC & 0xC3) | 0x14)
|
||||
#define CONFIG_MATCH_CLEAR() (FTM0_C6SC = (FTM0_C6SC & 0xC3) | 0x18)
|
||||
#define CONFIG_MATCH_SET() (FTM0_C6SC = (FTM0_C6SC & 0xC3) | 0x1C)
|
||||
#define CONFIG_CAPTURE_FALLING_EDGE() (FTM0_C5SC = (FTM0_C5SC & 0xC3) | 0x08)
|
||||
#define CONFIG_CAPTURE_RISING_EDGE() (FTM0_C5SC = (FTM0_C5SC & 0xC3) | 0x04)
|
||||
#define ENABLE_INT_INPUT_CAPTURE() FTM0_C5SC = 0x48; \
|
||||
CORE_PIN20_CONFIG = PORT_PCR_MUX(4)|PORT_PCR_PE|PORT_PCR_PS
|
||||
#define ENABLE_INT_COMPARE_A() FTM0_C6SC |= 0x40; \
|
||||
CORE_PIN21_CONFIG = PORT_PCR_MUX(4)|PORT_PCR_DSE|PORT_PCR_SRE
|
||||
#define ENABLE_INT_COMPARE_B() (FTM0_C0SC = 0x58)
|
||||
#define DISABLE_INT_INPUT_CAPTURE() FTM0_C5SC &= ~0x40; \
|
||||
CORE_PIN20_CONFIG = PORT_PCR_MUX(1)|PORT_PCR_PE|PORT_PCR_PS
|
||||
#define DISABLE_INT_COMPARE_A() FTM0_C6SC &= ~0x40; \
|
||||
CORE_PIN21_CONFIG = PORT_PCR_MUX(1)|PORT_PCR_DSE|PORT_PCR_SRE; \
|
||||
digitalWriteFast(21, HIGH)
|
||||
#define DISABLE_INT_COMPARE_B() (FTM0_C0SC &= ~0x40)
|
||||
#define GET_TIMER_COUNT() (FTM0_CNT)
|
||||
#define GET_INPUT_CAPTURE() (FTM0_C5V)
|
||||
#define GET_COMPARE_A() (FTM0_C6V)
|
||||
#define GET_COMPARE_B() (FTM0_C0V)
|
||||
#define SET_COMPARE_A(val) (FTM0_C6V = val)
|
||||
#define SET_COMPARE_B(val) if (FTM0_C0SC & FTM_CSC_CHF) FTM0_C0SC = 0x18; \
|
||||
do { FTM0_C0V = (val); } while (FTM0_C0V != (val));
|
||||
#define CAPTURE_INTERRUPT altss_capture_interrupt
|
||||
#define COMPARE_A_INTERRUPT altss_compare_a_interrupt
|
||||
#define COMPARE_B_INTERRUPT altss_compare_b_interrupt
|
||||
#ifdef ISR
|
||||
#undef ISR
|
||||
#endif
|
||||
#define ISR(f) static void f (void)
|
||||
|
||||
|
||||
#endif
|
||||
64
lib/MySensors/drivers/AltSoftSerial/docs/issue_template.md
Normal file
64
lib/MySensors/drivers/AltSoftSerial/docs/issue_template.md
Normal file
@@ -0,0 +1,64 @@
|
||||
Please use this form only to report code defects or bugs.
|
||||
|
||||
For any question, even questions directly pertaining to this code, post your question on the forums related to the board you are using.
|
||||
|
||||
Arduino: forum.arduino.cc
|
||||
Teensy: forum.pjrc.com
|
||||
ESP8266: www.esp8266.com
|
||||
ESP32: www.esp32.com
|
||||
Adafruit Feather/Metro/Trinket: forums.adafruit.com
|
||||
Particle Photon: community.particle.io
|
||||
|
||||
If you are experiencing trouble but not certain of the cause, or need help using this code, ask on the appropriate forum. This is not the place to ask for support or help, even directly related to this code. Only use this form you are certain you have discovered a defect in this code!
|
||||
|
||||
Please verify the problem occurs when using the very latest version, using the newest version of Arduino and any other related software.
|
||||
|
||||
|
||||
----------------------------- Remove above -----------------------------
|
||||
|
||||
|
||||
|
||||
### Description
|
||||
|
||||
Describe your problem.
|
||||
|
||||
|
||||
|
||||
### Steps To Reproduce Problem
|
||||
|
||||
Please give detailed instructions needed for anyone to attempt to reproduce the problem.
|
||||
|
||||
|
||||
|
||||
### Hardware & Software
|
||||
|
||||
Board
|
||||
Shields / modules used
|
||||
Arduino IDE version
|
||||
Teensyduino version (if using Teensy)
|
||||
Version info & package name (from Tools > Boards > Board Manager)
|
||||
Operating system & version
|
||||
Any other software or hardware?
|
||||
|
||||
|
||||
### Arduino Sketch
|
||||
|
||||
```cpp
|
||||
// Change the code below by your sketch (please try to give the smallest code which demonstrates the problem)
|
||||
#include <Arduino.h>
|
||||
|
||||
// libraries: give links/details so anyone can compile your code for the same result
|
||||
|
||||
void setup() {
|
||||
}
|
||||
|
||||
void loop() {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Errors or Incorrect Output
|
||||
|
||||
If you see any errors or incorrect output, please show it here. Please use copy & paste to give an exact copy of the message. Details matter, so please show (not merely describe) the actual message or error exactly as it appears.
|
||||
|
||||
|
||||
40
lib/MySensors/drivers/AltSoftSerial/examples/Echo/Echo.ino
Normal file
40
lib/MySensors/drivers/AltSoftSerial/examples/Echo/Echo.ino
Normal file
@@ -0,0 +1,40 @@
|
||||
#include <AltSoftSerial.h>
|
||||
|
||||
// AltSoftSerial always uses these pins:
|
||||
//
|
||||
// Board Transmit Receive PWM Unusable
|
||||
// ----- -------- ------- ------------
|
||||
// Teensy 3.0 & 3.1 21 20 22
|
||||
// Teensy 2.0 9 10 (none)
|
||||
// Teensy++ 2.0 25 4 26, 27
|
||||
// Arduino Uno 9 8 10
|
||||
// Arduino Leonardo 5 13 (none)
|
||||
// Arduino Mega 46 48 44, 45
|
||||
// Wiring-S 5 6 4
|
||||
// Sanguino 13 14 12
|
||||
|
||||
// This example code is in the public domain.
|
||||
|
||||
AltSoftSerial altSerial;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial) ; // wait for Arduino Serial Monitor to open
|
||||
Serial.println("AltSoftSerial Test Begin");
|
||||
altSerial.begin(9600);
|
||||
altSerial.println("Hello World");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
char c;
|
||||
|
||||
if (Serial.available()) {
|
||||
c = Serial.read();
|
||||
altSerial.print(c);
|
||||
}
|
||||
if (altSerial.available()) {
|
||||
c = altSerial.read();
|
||||
Serial.print(c);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// AltSoftSerial Receive Test
|
||||
//
|
||||
// Transmit data with Serial1 and try to receive
|
||||
// it with AltSoftSerial. You must connect a wire
|
||||
// from Serial1 TX to AltSoftSerial RX.
|
||||
|
||||
#include <AltSoftSerial.h>
|
||||
|
||||
AltSoftSerial altser;
|
||||
const int mybaud = 9600;
|
||||
|
||||
// Board Serial1 TX AltSoftSerial RX
|
||||
// ----- ---------- ----------------
|
||||
// Teensy 3.x 1 20
|
||||
// Teensy 2.0 8 (D3) 10 (C7)
|
||||
// Teensy++ 2.0 3 (D3) 4 (D4)
|
||||
// Arduino Leonardo 1 13
|
||||
// Arduino Mega 18 48
|
||||
|
||||
// Serial1 on AVR @ 16 MHz minimum baud is 245
|
||||
// Serial1 on Teensy 3.2 @ 96 MHz minimum baud is 733
|
||||
|
||||
// This example code is in the public domain.
|
||||
|
||||
byte sentbyte;
|
||||
unsigned long prevmillis;
|
||||
byte testbyte=0xF0;
|
||||
|
||||
void setup()
|
||||
{
|
||||
delay(200);
|
||||
Serial.begin(9600);
|
||||
while (!Serial) ; // wait for Arduino Serial Monitor
|
||||
Serial1.begin(mybaud); // connect a wire from TX1
|
||||
altser.begin(mybaud); // to AltSoftSerial RX
|
||||
Serial.println("AltSoftSerial Receive Test");
|
||||
prevmillis = millis();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// transmit a test byte on Serial 1
|
||||
if (millis() - prevmillis > 250) {
|
||||
sentbyte = testbyte++;
|
||||
Serial1.write(sentbyte);
|
||||
prevmillis = millis();
|
||||
}
|
||||
// attempt to receive it by AltSoftSerial
|
||||
if (altser.available() > 0) {
|
||||
byte b = altser.read();
|
||||
Serial.println(b);
|
||||
if (b != sentbyte) {
|
||||
Serial.println("***** ERROR *****");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
#include <AltSoftSerial.h>
|
||||
|
||||
// AltSoftSerial show configuration example
|
||||
// Will print library configuration to default serial port
|
||||
// Open your serial monitor to see config for your board
|
||||
// Printout would repeat every 10sec (just in case you missed it somehow)
|
||||
|
||||
// Print Configuration
|
||||
// -----------------------
|
||||
|
||||
// Direct include AltSoftSerial internals to obtaion PIN setup (you do not need this in regular code)
|
||||
|
||||
// This example code is in the public domain.
|
||||
|
||||
#include <config/AltSoftSerial_Boards.h>
|
||||
|
||||
void printAltSoftSerialSetup(Stream &port)
|
||||
{
|
||||
#define PRINT_PFX "AltSoftSerial:"
|
||||
#define PRINT_PIN_NAME(pin,name) { char buffer[128+1]; sprintf(buffer, PRINT_PFX "PIN:%2d %s", (int)pin, (const char*)name); port.println(buffer); }
|
||||
|
||||
port.println(PRINT_PFX "Setup info: begin");
|
||||
|
||||
#if defined(ALTSS_USE_FTM0)
|
||||
port.println(PRINT_PFX "USE FTM0");
|
||||
#endif
|
||||
|
||||
#if defined(ALTSS_USE_TIMER1)
|
||||
port.println(PRINT_PFX "USE TIMER1");
|
||||
#endif
|
||||
|
||||
#if defined(ALTSS_USE_TIMER2)
|
||||
port.println(PRINT_PFX "USE TIMER2");
|
||||
#endif
|
||||
|
||||
#if defined(ALTSS_USE_TIMER3)
|
||||
port.println(PRINT_PFX "USE TIMER3");
|
||||
#endif
|
||||
|
||||
#if defined(ALTSS_USE_TIMER4)
|
||||
port.println(PRINT_PFX "USE TIMER4");
|
||||
#endif
|
||||
|
||||
#if defined(ALTSS_USE_TIMER5)
|
||||
port.println(PRINT_PFX "USE TIMER5");
|
||||
#endif
|
||||
|
||||
#if defined(INPUT_CAPTURE_PIN)
|
||||
PRINT_PIN_NAME(INPUT_CAPTURE_PIN,"RX");
|
||||
#endif
|
||||
|
||||
#if defined(OUTPUT_COMPARE_A_PIN)
|
||||
PRINT_PIN_NAME(OUTPUT_COMPARE_A_PIN,"TX");
|
||||
#endif
|
||||
|
||||
#if defined(OUTPUT_COMPARE_B_PIN)
|
||||
PRINT_PIN_NAME(OUTPUT_COMPARE_B_PIN,"(unused PWM)");
|
||||
#endif
|
||||
|
||||
#if defined(OUTPUT_COMPARE_C_PIN)
|
||||
PRINT_PIN_NAME(OUTPUT_COMPARE_C_PIN,"(unused PWM)");
|
||||
#endif
|
||||
|
||||
#if defined(OUTPUT_COMPARE_D_PIN)
|
||||
PRINT_PIN_NAME(OUTPUT_COMPARE_D_PIN,"(unused PWM)");
|
||||
#endif
|
||||
|
||||
#if defined(OUTPUT_COMPARE_E_PIN)
|
||||
PRINT_PIN_NAME(OUTPUT_COMPARE_E_PIN,"(unused PWM)");
|
||||
#endif
|
||||
|
||||
#if defined(OUTPUT_COMPARE_F_PIN)
|
||||
PRINT_PIN_NAME(OUTPUT_COMPARE_F_PIN,"(unused PWM)");
|
||||
#endif
|
||||
|
||||
port.println(PRINT_PFX "Setup info: end");
|
||||
|
||||
#undef PRINT_PIN_NAME
|
||||
#undef PRINT_PFX
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
// Open default serial to dump config to
|
||||
Serial.begin(9600);
|
||||
while (!Serial) ; // wait for serial monitor
|
||||
printAltSoftSerialSetup(Serial);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Repeat every 10 sec (just in case)
|
||||
delay(10000);
|
||||
Serial.println("");
|
||||
printAltSoftSerialSetup(Serial);
|
||||
}
|
||||
|
||||
4
lib/MySensors/drivers/AltSoftSerial/keywords.txt
Normal file
4
lib/MySensors/drivers/AltSoftSerial/keywords.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
AltSoftSerial KEYWORD1
|
||||
active KEYWORD2
|
||||
overflow KEYWORD2
|
||||
library_version KEYWORD2
|
||||
197
lib/MySensors/drivers/CircularBuffer/CircularBuffer.h
Normal file
197
lib/MySensors/drivers/CircularBuffer/CircularBuffer.h
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
CircularBuffer - An Arduino circular buffering library for arbitrary types.
|
||||
|
||||
Created by Ivo Pullens, Emmission, 2014-2016 -- www.emmission.nl
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file CircularBuffer.h
|
||||
*
|
||||
* Circular buffering for arbitrary types.
|
||||
*/
|
||||
|
||||
#ifndef CircularBuffer_h
|
||||
#define CircularBuffer_h
|
||||
|
||||
/**
|
||||
* The circular buffer class.
|
||||
* Pass the datatype to be stored in the buffer as template parameter.
|
||||
*/
|
||||
template <class T> class CircularBuffer
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* @param buffer Preallocated buffer of at least size records.
|
||||
* @param size Number of records available in the buffer.
|
||||
*/
|
||||
CircularBuffer(T* buffer, const uint8_t size )
|
||||
: m_size(size), m_buff(buffer)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all entries in the circular buffer.
|
||||
*/
|
||||
void clear(void)
|
||||
{
|
||||
MY_CRITICAL_SECTION {
|
||||
m_front = 0;
|
||||
m_fill = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the circular buffer is empty.
|
||||
* @return True, when empty.
|
||||
*/
|
||||
inline bool empty(void) const
|
||||
{
|
||||
bool empty;
|
||||
MY_CRITICAL_SECTION {
|
||||
empty = !m_fill;
|
||||
}
|
||||
return empty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the circular buffer is full.
|
||||
* @return True, when full.
|
||||
*/
|
||||
inline bool full(void) const
|
||||
{
|
||||
bool full;
|
||||
MY_CRITICAL_SECTION {
|
||||
full = m_fill == m_size;
|
||||
}
|
||||
return full;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of records stored in the buffer.
|
||||
* @return number of records.
|
||||
*/
|
||||
inline uint8_t available(void) const
|
||||
{
|
||||
uint8_t ret_value;
|
||||
MY_CRITICAL_SECTION {
|
||||
ret_value = m_fill;
|
||||
}
|
||||
return ret_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aquire unused record on front of the buffer, for writing.
|
||||
* After filling the record, it has to be pushed to actually
|
||||
* add it to the buffer.
|
||||
* @return Pointer to record, or NULL when buffer is full.
|
||||
*/
|
||||
T* getFront(void) const
|
||||
{
|
||||
MY_CRITICAL_SECTION {
|
||||
if (!full())
|
||||
{
|
||||
return get(m_front);
|
||||
}
|
||||
}
|
||||
return static_cast<T*>(NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Push record to front of the buffer.
|
||||
* @param record Record to push. If record was aquired previously (using getFront) its
|
||||
* data will not be copied as it is already present in the buffer.
|
||||
* @return True, when record was pushed successfully.
|
||||
*/
|
||||
bool pushFront(T* record)
|
||||
{
|
||||
MY_CRITICAL_SECTION {
|
||||
if (!full())
|
||||
{
|
||||
T* f = get(m_front);
|
||||
if (f != record) {
|
||||
*f = *record;
|
||||
}
|
||||
m_front = (m_front+1) % m_size;
|
||||
m_fill++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aquire record on back of the buffer, for reading.
|
||||
* After reading the record, it has to be pop'ed to actually
|
||||
* remove it from the buffer.
|
||||
* @return Pointer to record, or NULL when buffer is empty.
|
||||
*/
|
||||
T* getBack(void) const
|
||||
{
|
||||
MY_CRITICAL_SECTION {
|
||||
if (!empty())
|
||||
{
|
||||
return get(back());
|
||||
}
|
||||
}
|
||||
return static_cast<T*>(NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove record from back of the buffer.
|
||||
* @return True, when record was pop'ed successfully.
|
||||
*/
|
||||
bool popBack(void)
|
||||
{
|
||||
MY_CRITICAL_SECTION {
|
||||
if (!empty())
|
||||
{
|
||||
m_fill--;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Internal getter for records.
|
||||
* @param idx Record index in buffer.
|
||||
* @return Ptr to record.
|
||||
*/
|
||||
inline T * get(const uint8_t idx) const
|
||||
{
|
||||
return &(m_buff[idx]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal getter for index of last used record in buffer.
|
||||
* @return Index of last record.
|
||||
*/
|
||||
inline uint8_t back(void) const
|
||||
{
|
||||
return (m_front - m_fill + m_size) % m_size;
|
||||
}
|
||||
|
||||
const uint8_t m_size; //!< Total number of records that can be stored in the buffer.
|
||||
T* const m_buff; //!< Ptr to buffer holding all records.
|
||||
volatile uint8_t m_front; //!< Index of front element (not pushed yet).
|
||||
volatile uint8_t m_fill; //!< Amount of records currently pushed.
|
||||
};
|
||||
|
||||
#endif // CircularBuffer_h
|
||||
81
lib/MySensors/drivers/I2CEeprom/I2CEeprom.cpp
Normal file
81
lib/MySensors/drivers/I2CEeprom/I2CEeprom.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
// Copyright (C) 2016 Krister W. <kisse66@hobbylabs.org>
|
||||
//
|
||||
// Original SPI flash driver this is based on:
|
||||
// Copyright (c) 2013-2015 by Felix Rusu, LowPowerLab.com
|
||||
// **********************************************************************************
|
||||
// License
|
||||
// **********************************************************************************
|
||||
// This program is free software; you can redistribute it
|
||||
// and/or modify it under the terms of the GNU General
|
||||
// Public License as published by the Free Software
|
||||
// Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without even the
|
||||
// implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
// PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General
|
||||
// Public License along with this program.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Licence can be viewed at
|
||||
// http://www.gnu.org/licenses/gpl-3.0.txt
|
||||
//
|
||||
// Please maintain this license information along with authorship
|
||||
// and copyright notices in any redistribution of this code
|
||||
|
||||
#include <Wire.h>
|
||||
#include "I2CEeprom.h"
|
||||
|
||||
I2CEeprom::I2CEeprom(uint8_t addr) : extEEPROM(I2CEEPROM_CHIP_SIZE, 1, I2CEEPROM_PAGE_SIZE)
|
||||
{
|
||||
m_addr = addr; // we only need this for busy()
|
||||
}
|
||||
|
||||
/// setup
|
||||
bool I2CEeprom::initialize()
|
||||
{
|
||||
return extEEPROM::begin(I2CEEPROM_TWI_CLK) ? false : true;
|
||||
}
|
||||
|
||||
/// read 1 byte
|
||||
uint8_t I2CEeprom::readByte(uint32_t addr)
|
||||
{
|
||||
uint8_t val;
|
||||
readBytes(addr, &val, 1);
|
||||
return val;
|
||||
}
|
||||
|
||||
/// read multiple bytes
|
||||
void I2CEeprom::readBytes(uint32_t addr, void* buf, uint16_t len)
|
||||
{
|
||||
extEEPROM::read((unsigned long)addr, (byte *)buf, (unsigned int) len);
|
||||
}
|
||||
|
||||
/// check if the chip is busy
|
||||
bool I2CEeprom::busy()
|
||||
{
|
||||
Wire.beginTransmission(m_addr);
|
||||
Wire.write(0);
|
||||
Wire.write(0);
|
||||
if (Wire.endTransmission() == 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true; // busy
|
||||
}
|
||||
}
|
||||
|
||||
/// Write 1 byte
|
||||
void I2CEeprom::writeByte(uint32_t addr, uint8_t byt)
|
||||
{
|
||||
writeBytes(addr, &byt, 1);
|
||||
}
|
||||
|
||||
/// write multiple bytes
|
||||
void I2CEeprom::writeBytes(uint32_t addr, const void* buf, uint16_t len)
|
||||
{
|
||||
extEEPROM::write((unsigned long) addr, (byte *)buf, (unsigned int) len);
|
||||
}
|
||||
116
lib/MySensors/drivers/I2CEeprom/I2CEeprom.h
Normal file
116
lib/MySensors/drivers/I2CEeprom/I2CEeprom.h
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright (C) 2016 Krister W. <kisse66@hobbylabs.org>
|
||||
//
|
||||
// Original SPI flash driver this is based on:
|
||||
// Copyright (c) 2013-2015 by Felix Rusu, LowPowerLab.com
|
||||
//
|
||||
// I2C EEPROM library for MySensors OTA. Based on SPI Flash memory library for
|
||||
// arduino/moteino.
|
||||
// This driver is made to look like the SPI flash driver so changes needed to
|
||||
// MySensors OTA code is minimized.
|
||||
// This works with 32 or 64kB I2C EEPROM like an 24(L)C256. AVR HW I2C is assumed
|
||||
// and error handling is quite minimal. Uses extEEPROM as the underlying driver.
|
||||
// DEPENDS ON: Arduino Wire library, extEEPROM
|
||||
// **********************************************************************************
|
||||
// License
|
||||
// **********************************************************************************
|
||||
// This program is free software; you can redistribute it
|
||||
// and/or modify it under the terms of the GNU General
|
||||
// Public License as published by the Free Software
|
||||
// Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without even the
|
||||
// implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
// PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General
|
||||
// Public License along with this program.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Licence can be viewed at
|
||||
// http://www.gnu.org/licenses/gpl-3.0.txt
|
||||
//
|
||||
// Please maintain this license information along with authorship
|
||||
// and copyright notices in any redistribution of this code
|
||||
|
||||
///
|
||||
/// @file I2CEeprom.h
|
||||
///
|
||||
/// @brief I2CEeprom provides access to a I2C EEPROM IC for OTA update or storing data
|
||||
///
|
||||
/// This is a wrapper over extEEPROM to make it look like a flash chip.
|
||||
///
|
||||
#ifndef _I2CEeprom_H_
|
||||
#define _I2CEeprom_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <extEEPROM.h>
|
||||
|
||||
/// I2C speed
|
||||
// 400kHz clock as default. Use extEEPROM type
|
||||
#ifndef I2CEEPROM_TWI_CLK
|
||||
#define I2CEEPROM_TWI_CLK twiClock400kHz
|
||||
#endif
|
||||
|
||||
/// EEPROM page size
|
||||
//Typically 64 (see data sheet for your EEPROM)
|
||||
// Some 512kbit chips use 128 byte pages (e.g. Atmel AT24C512)
|
||||
#ifndef I2CEEPROM_PAGE_SIZE
|
||||
#define I2CEEPROM_PAGE_SIZE 64
|
||||
#endif
|
||||
|
||||
/// EEPROM size
|
||||
// 24C256 is 32kB, minimum that fits code for ATmega328
|
||||
// Use extEEPROM type
|
||||
#ifndef I2CEEPROM_CHIP_SIZE
|
||||
#define I2CEEPROM_CHIP_SIZE kbits_256
|
||||
#endif
|
||||
|
||||
/** I2CEeprom class */
|
||||
class I2CEeprom : extEEPROM
|
||||
{
|
||||
public:
|
||||
|
||||
explicit I2CEeprom(uint8_t addr); //!< Constructor
|
||||
bool initialize(); //!< setup
|
||||
uint8_t readByte(uint32_t addr); //!< read 1 byte from flash memory
|
||||
void readBytes(uint32_t addr, void* buf, uint16_t len); //!< read multiple bytes
|
||||
void writeByte(uint32_t addr, uint8_t byt); //!< Write 1 byte to flash memory
|
||||
void writeBytes(uint32_t addr, const void* buf,
|
||||
uint16_t len); //!< write multiple bytes to flash memory (up to 64K), if define SPIFLASH_SST25TYPE is set AAI Word Programming will be used
|
||||
bool busy(); //!< check if the chip is busy erasing/writing
|
||||
|
||||
// the rest not needed for EEPROMs, but kept so SPI flash code compiles as is (functions are NOP)
|
||||
|
||||
/// dummy function for SPI flash compatibility
|
||||
uint16_t readDeviceId()
|
||||
{
|
||||
return 0xDEAD;
|
||||
};
|
||||
/// dummy function for SPI flash compatibility
|
||||
void chipErase() {};
|
||||
/// dummy function for SPI flash compatibility
|
||||
void blockErase4K(uint32_t address)
|
||||
{
|
||||
(void)address;
|
||||
};
|
||||
/// dummy function for SPI flash compatibility
|
||||
void blockErase32K(uint32_t address)
|
||||
{
|
||||
(void)address;
|
||||
};
|
||||
/// dummy function for SPI flash compatibility
|
||||
void sleep() {};
|
||||
/// dummy function for SPI flash compatibility
|
||||
void wakeup() {};
|
||||
/// dummy function for SPI flash compatibility
|
||||
void end() {};
|
||||
|
||||
protected:
|
||||
|
||||
uint8_t m_addr; ///< I2C address for busy()
|
||||
};
|
||||
|
||||
#endif
|
||||
178
lib/MySensors/drivers/NVM/Flash.h
Normal file
178
lib/MySensors/drivers/NVM/Flash.h
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Flash.h - Flash library
|
||||
* Original Copyright (c) 2017 Frank Holtz. All right reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
/**
|
||||
* @file Flash.h
|
||||
* @brief Flash abstraction layer
|
||||
*
|
||||
* @ingroup NVM
|
||||
* @details Nonvolatile Memory Class
|
||||
* @{
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <stdio.h> // for size_t
|
||||
|
||||
/*
|
||||
* Define characteristics of Flash
|
||||
*
|
||||
* @def FLASH_ERASE_CYCLES
|
||||
* @brief Specified number of erase cycles
|
||||
*
|
||||
* @def FLASH_PAGE_SIZE
|
||||
* @brief Used/supported Flash page size
|
||||
*
|
||||
* @def FLASH_ERASE_PAGE_TIME
|
||||
* @brief Time in ms to delete a page
|
||||
*
|
||||
* @def FLASH_WRITES_PER_WORD
|
||||
* @brief How often a dataword (32 bit) can be written
|
||||
*
|
||||
* @def FLASH_WRITES_PER_PAGE
|
||||
* @brief How many writes are allowed into a page
|
||||
*
|
||||
* @def FLASH_SUPPORTS_RANDOM_WRITE
|
||||
* @brief Set this if it is allowed to write to a page in random order.
|
||||
*/
|
||||
|
||||
#if defined(NRF51)
|
||||
#define FLASH_ERASE_CYCLES 20000
|
||||
#define FLASH_PAGE_SIZE 1024
|
||||
#define FLASH_ERASE_PAGE_TIME 23
|
||||
#define FLASH_SUPPORTS_RANDOM_WRITE true
|
||||
#define FLASH_WRITES_PER_WORD 2
|
||||
#define FLASH_WRITES_PER_PAGE 512
|
||||
#elif defined(NRF52)
|
||||
#define FLASH_ERASE_CYCLES 10000
|
||||
#define FLASH_PAGE_SIZE 4096
|
||||
#define FLASH_ERASE_PAGE_TIME 90
|
||||
#define FLASH_SUPPORTS_RANDOM_WRITE true
|
||||
#define FLASH_WRITES_PER_WORD 32
|
||||
#define FLASH_WRITES_PER_PAGE 181
|
||||
#elif defined(NRF52840)
|
||||
#define FLASH_ERASE_CYCLES 10000
|
||||
#define FLASH_PAGE_SIZE 4096
|
||||
#define FLASH_ERASE_PAGE_TIME 90
|
||||
#define FLASH_SUPPORTS_RANDOM_WRITE true
|
||||
#define FLASH_WRITES_PER_WORD 2
|
||||
#define FLASH_WRITES_PER_PAGE 403
|
||||
#else
|
||||
#define FLASH_ERASE_CYCLES 10000 //!< FLASH_ERASE_CYCLES
|
||||
#define FLASH_PAGE_SIZE 4096 //!< FLASH_PAGE_SIZE
|
||||
#define FLASH_ERASE_PAGE_TIME 100 //!< FLASH_ERASE_PAGE_TIME
|
||||
//#define FLASH_SUPPORTS_RANDOM_WRITE true
|
||||
#define FLASH_WRITES_PER_WORD 1 //!< FLASH_WRITES_PER_WORD
|
||||
#warning "Unknown platform. Please check the code."
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @class FlashClass
|
||||
* @brief This class provides low-level access to internal Flash memory.
|
||||
*/
|
||||
class FlashClass
|
||||
{
|
||||
public:
|
||||
//----------------------------------------------------------------------------
|
||||
/** Constructor */
|
||||
FlashClass() {};
|
||||
//----------------------------------------------------------------------------
|
||||
/** Initialize Flash */
|
||||
void begin() {};
|
||||
/** Deinitialize Flash */
|
||||
void end() {};
|
||||
//----------------------------------------------------------------------------
|
||||
/*
|
||||
* Physical flash geometry
|
||||
*/
|
||||
//----------------------------------------------------------------------------
|
||||
/** Page size in bytes
|
||||
* @return Number of bytes
|
||||
*/
|
||||
uint32_t page_size() const;
|
||||
//----------------------------------------------------------------------------
|
||||
/** Page address width in bits. Page size is 2^x
|
||||
* @return Number of bits
|
||||
*/
|
||||
uint8_t page_size_bits() const;
|
||||
//----------------------------------------------------------------------------
|
||||
/** Number of managed flash pages
|
||||
* @return Number of pages
|
||||
*/
|
||||
uint32_t page_count() const;
|
||||
//----------------------------------------------------------------------------
|
||||
/** Number of page erase cycles
|
||||
* @return Number of page erase cycles
|
||||
*/
|
||||
uint32_t specified_erase_cycles() const;
|
||||
//----------------------------------------------------------------------------
|
||||
/** Get a address of a page
|
||||
* @param[in] page Page number, starting at 0
|
||||
* @return address of given page
|
||||
*/
|
||||
uint32_t *page_address(size_t page);
|
||||
/** Get top of available flash for application data
|
||||
* @return Last available address + 1
|
||||
*/
|
||||
uint32_t *top_app_page_address();
|
||||
//----------------------------------------------------------------------------
|
||||
/*
|
||||
* Accessing flash memory
|
||||
*/
|
||||
//----------------------------------------------------------------------------
|
||||
/** Erase a page of given size. Size must be page_size aligned!
|
||||
* Take care about RADIO, WDT and Interrupt timing!
|
||||
* @param[in] *address Pointer to page
|
||||
* @param[in] size number of page aligned bytes to erase
|
||||
*/
|
||||
void erase(uint32_t *address, size_t size);
|
||||
//----------------------------------------------------------------------------
|
||||
/** Erase the complete MCU. This can brick your device!
|
||||
*/
|
||||
void erase_all();
|
||||
//----------------------------------------------------------------------------
|
||||
/** write a aligned 32 bit word to flash.
|
||||
* @param[in] *address 32 bit aligned pointer to destination word
|
||||
* @param[in] value Data word to write
|
||||
*/
|
||||
void write(uint32_t *address, uint32_t value);
|
||||
//----------------------------------------------------------------------------
|
||||
/** write a aligned block to flash.
|
||||
* @param[in] *dst_address 32 bit aligned pointer to destination
|
||||
* @param[in] *src_address 32 bit aligned pointer to source
|
||||
* @param[in] word_count Number of words to write
|
||||
*/
|
||||
void write_block(uint32_t *dst_address, uint32_t *src_address,
|
||||
uint16_t word_count);
|
||||
|
||||
private:
|
||||
// Wait until flash is ready
|
||||
void wait_for_ready();
|
||||
};
|
||||
|
||||
extern FlashClass Flash; //!< extern FlashClass
|
||||
|
||||
/** Load Hardwarespecific files */
|
||||
#ifdef NRF5
|
||||
#include "hal/architecture/NRF5/drivers/Flash.cpp"
|
||||
#else
|
||||
#error "Unsupported platform."
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
342
lib/MySensors/drivers/NVM/NVRAM.cpp
Normal file
342
lib/MySensors/drivers/NVM/NVRAM.cpp
Normal file
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
NVRAM.cpp - Byte wise storage for Virtual Pages.
|
||||
Original Copyright (c) 2017 Frank Holtz. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include "NVRAM.h"
|
||||
|
||||
// VirtualPage magic
|
||||
#define NVRAM_MAGIC (0x7710fdb9)
|
||||
// Number of emulated cells
|
||||
#define NVRAM_LENGTH 3072
|
||||
// Log configuration: Address index in bits
|
||||
#define NVRAM_ADDR_POS 20
|
||||
// Log configuration: Mask for comparsion (4k space)
|
||||
#define NVRAM_ADDR_MASK 0xfff00000
|
||||
// Log configuration: Bit position of used address bitmap
|
||||
#define NVRAM_BITMAP_POS 8
|
||||
// Log configuration: used address bitmap calulation
|
||||
#define NVRAM_BITMAP_ADDR_SHIFT 8
|
||||
// Log configuration: Mask for bitmap extraction
|
||||
#define NVRAM_BITMAP_MASK 0x000fff00
|
||||
#define ADDR2BIT(index) \
|
||||
((1 << (index >> NVRAM_BITMAP_ADDR_SHIFT)) << NVRAM_BITMAP_POS)
|
||||
|
||||
NVRAMClass NVRAM;
|
||||
|
||||
uint16_t NVRAMClass::length() const
|
||||
{
|
||||
return (NVRAM_LENGTH);
|
||||
}
|
||||
|
||||
void NVRAMClass::read_block(uint8_t *dst, uint16_t idx, uint16_t n)
|
||||
{
|
||||
uint32_t *vpage;
|
||||
uint16_t log_start, log_end;
|
||||
|
||||
// find correct page
|
||||
vpage = get_page();
|
||||
|
||||
// copy 0xff to dst when no page is available
|
||||
if (vpage == (uint32_t *)~0) {
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
((uint8_t *)dst)[i] = 0xff;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate actual log position
|
||||
log_end = get_log_position(vpage);
|
||||
if (log_end == 0) {
|
||||
log_start = 1;
|
||||
} else {
|
||||
log_start = vpage[0] + 1;
|
||||
}
|
||||
/*
|
||||
Serial.print("\r\nread_block idx=");
|
||||
Serial.print(idx);
|
||||
Serial.print(" n=");
|
||||
Serial.print(n);
|
||||
Serial.print("("); */
|
||||
while (n > 0) {
|
||||
// Read cell
|
||||
*dst = get_byte_from_page(vpage, log_start, log_end, idx);
|
||||
// Serial.print(*dst, HEX);
|
||||
// calculate next address
|
||||
n--;
|
||||
dst++;
|
||||
idx++;
|
||||
}
|
||||
// Serial.println(")");
|
||||
}
|
||||
|
||||
uint8_t NVRAMClass::read(const uint16_t idx)
|
||||
{
|
||||
uint8_t ret;
|
||||
read_block(&ret, idx, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool NVRAMClass::write_block(uint8_t *src, uint16_t idx, uint16_t n)
|
||||
{
|
||||
uint32_t *vpage;
|
||||
uint32_t bitmap;
|
||||
uint16_t log_start, log_end;
|
||||
|
||||
// find correct page
|
||||
vpage = get_page();
|
||||
|
||||
// return on invalid page
|
||||
if (vpage == (uint32_t *)~0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// calculate actual log position
|
||||
log_start = vpage[0] + 1;
|
||||
log_end = get_log_position(vpage);
|
||||
if (log_end > log_start) {
|
||||
bitmap = vpage[log_end - 1] & NVRAM_BITMAP_MASK;
|
||||
} else {
|
||||
bitmap = 0;
|
||||
}
|
||||
|
||||
while (n > 0) {
|
||||
// Read cell
|
||||
uint8_t old_value = get_byte_from_page(vpage, log_start, log_end, idx);
|
||||
uint8_t new_value = *src;
|
||||
|
||||
// Have to write into log?
|
||||
if (new_value != old_value) {
|
||||
|
||||
// need to calculate a new page?
|
||||
if (log_end >= VirtualPage.length()) {
|
||||
vpage = switch_page(vpage, &log_start, &log_end);
|
||||
if (vpage == (uint32_t *)~0) {
|
||||
// do nothing if no page is available
|
||||
return false;
|
||||
}
|
||||
bitmap = 0;
|
||||
}
|
||||
|
||||
// Add Entry into log
|
||||
Flash.write(&vpage[log_end], (idx << NVRAM_ADDR_POS) | bitmap |
|
||||
ADDR2BIT(idx) | (uint32_t)new_value);
|
||||
log_end++;
|
||||
}
|
||||
|
||||
// calculate next address
|
||||
n--;
|
||||
src++;
|
||||
idx++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NVRAMClass::write(uint16_t idx, uint8_t value)
|
||||
{
|
||||
return (write_block(&value, idx, 1));
|
||||
}
|
||||
|
||||
int NVRAMClass::write_prepare(uint16_t number)
|
||||
{
|
||||
// find correct page
|
||||
uint32_t *vpage = get_page();
|
||||
// Want to write to much or into an invalid page?
|
||||
if ((vpage == (uint32_t *)~0) || (number > length())) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// calculate actual log position
|
||||
uint16_t log_end = get_log_position(vpage);
|
||||
|
||||
// Calculate number of free bytes in log
|
||||
int free_bytes = ((VirtualPage.length() - 1) - log_end);
|
||||
|
||||
// switch page when
|
||||
if (free_bytes < number) {
|
||||
uint16_t log_start = vpage[0] + 1;
|
||||
vpage = switch_page(vpage, &log_start, &log_end);
|
||||
if (vpage == (uint32_t *)~0) {
|
||||
// do nothing if no page is available
|
||||
return -1;
|
||||
}
|
||||
log_end = get_log_position(vpage);
|
||||
free_bytes = ((VirtualPage.length() - 1) - log_end);
|
||||
}
|
||||
return free_bytes;
|
||||
}
|
||||
|
||||
void NVRAMClass::clean_up(uint16_t write_preserve)
|
||||
{
|
||||
VirtualPage.clean_up();
|
||||
if (write_preserve > 0) {
|
||||
write_prepare(write_preserve);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t *NVRAMClass::switch_page(uint32_t *old_vpage, uint16_t *log_start,
|
||||
uint16_t *log_end)
|
||||
{
|
||||
// Mark old page as in release
|
||||
VirtualPage.release_prepare(old_vpage);
|
||||
|
||||
// Get a blank page
|
||||
uint32_t *new_vpage = VirtualPage.allocate(NVRAM_MAGIC, VirtualPage.length());
|
||||
if (new_vpage == (uint32_t *)~0) {
|
||||
// failed
|
||||
return new_vpage;
|
||||
}
|
||||
|
||||
// Store four bytes for map creation
|
||||
uint32_t value;
|
||||
|
||||
// Length of new map
|
||||
uint16_t map_length = 0;
|
||||
|
||||
// Build map
|
||||
#ifdef FLASH_SUPPORTS_RANDOM_WRITE
|
||||
// Copy current values
|
||||
for (uint16_t i = 0; i < (NVRAM_LENGTH >> 2); i++) {
|
||||
read_block((uint8_t *)&value, i << 2, 4);
|
||||
if (value != (uint32_t)~0) {
|
||||
// Value found
|
||||
map_length = i + 1;
|
||||
Flash.write(&new_vpage[i + 1], value);
|
||||
}
|
||||
}
|
||||
// Store map length
|
||||
Flash.write(new_vpage, map_length);
|
||||
#else
|
||||
// find map length
|
||||
for (uint16_t i = (NVRAM_LENGTH >> 2); i > 0; i--) {
|
||||
read_block((uint8_t *)&value, i << 2, 4);
|
||||
if (value != (uint32_t)~0) {
|
||||
// Value found
|
||||
map_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
map_length++;
|
||||
|
||||
// Store map length
|
||||
Flash.write(new_vpage, map_length);
|
||||
|
||||
// Copy current values
|
||||
for (uint16_t i = 0; i <= map_length; i++) {
|
||||
read_block((uint8_t *)&value, i << 2, 4);
|
||||
if (value != (uint32_t)~0) {
|
||||
// Value found
|
||||
map_length = i;
|
||||
Flash.write(&new_vpage[i + 1], value);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Release old page
|
||||
VirtualPage.release(old_vpage);
|
||||
|
||||
// Set log position
|
||||
*log_start = map_length + 1;
|
||||
*log_end = *log_start;
|
||||
|
||||
return new_vpage;
|
||||
}
|
||||
|
||||
uint32_t *NVRAMClass::get_page()
|
||||
{
|
||||
uint32_t *vpage = VirtualPage.get(NVRAM_MAGIC);
|
||||
// Invalid page?
|
||||
if (vpage == (uint32_t *)~0) {
|
||||
// Allocate a new page
|
||||
vpage = VirtualPage.allocate(NVRAM_MAGIC, VirtualPage.length());
|
||||
// Set map length to 0
|
||||
Flash.write(&vpage[0], 0x0);
|
||||
}
|
||||
return vpage;
|
||||
}
|
||||
|
||||
uint16_t NVRAMClass::get_log_position(uint32_t *vpage)
|
||||
{
|
||||
uint16_t position_min = vpage[0] + 1;
|
||||
uint16_t position_max = VirtualPage.length();
|
||||
|
||||
// Return if page is not filled
|
||||
if ((vpage[0] == (uint32_t)~0) || (position_min >= position_max)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// loop until postition_min != position_max-1
|
||||
while (position_min != position_max - 1) {
|
||||
// Calculate middle between min and max
|
||||
uint16_t mid = position_min + ((position_max - position_min) >> 1);
|
||||
// Set max or min to current position
|
||||
if (vpage[mid] == (uint32_t)~0) {
|
||||
position_max = mid;
|
||||
} else {
|
||||
position_min = mid;
|
||||
}
|
||||
}
|
||||
|
||||
return position_max;
|
||||
}
|
||||
|
||||
uint8_t NVRAMClass::get_byte_from_page(uint32_t *vpage, uint16_t log_start,
|
||||
uint16_t log_end, uint16_t idx)
|
||||
{
|
||||
// mask matching a bit signaling wich address range is in log
|
||||
uint32_t address_mask = ADDR2BIT(idx);
|
||||
// mask matching the index address
|
||||
uint32_t address_match = idx << NVRAM_ADDR_POS;
|
||||
|
||||
// Check the log backwards
|
||||
while (log_end > log_start) {
|
||||
log_end--;
|
||||
uint32_t value = vpage[log_end];
|
||||
// end here if address map bit is not set
|
||||
if ((value & address_mask) == 0) {
|
||||
break;
|
||||
}
|
||||
// check address match -> update found -> return
|
||||
if ((value & NVRAM_ADDR_MASK) == address_match) {
|
||||
return (uint8_t)value;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate address in the eeprom map at the beginning of a vpage
|
||||
uint16_t map_address = (idx >> 2);
|
||||
map_address++; // jump over log offset field
|
||||
|
||||
// look at map if calculated addess before log start position
|
||||
if (map_address < log_start) {
|
||||
switch (idx % 4) {
|
||||
case 3:
|
||||
return (uint8_t)(vpage[map_address] >> 24);
|
||||
break;
|
||||
case 2:
|
||||
return (uint8_t)(vpage[map_address] >> 16);
|
||||
break;
|
||||
case 1:
|
||||
return (uint8_t)(vpage[map_address] >> 8);
|
||||
break;
|
||||
default:
|
||||
return (uint8_t)(vpage[map_address]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// empty cell
|
||||
return 0xff;
|
||||
}
|
||||
113
lib/MySensors/drivers/NVM/NVRAM.h
Normal file
113
lib/MySensors/drivers/NVM/NVRAM.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* NVRAM.h - Byte-wise storage for Virtual Pages.
|
||||
* Original Copyright (c) 2017 Frank Holtz. All right reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
/**
|
||||
* @file NVRAM.h
|
||||
* @brief Byte-wise storage for Virtual Pages.
|
||||
*
|
||||
* @defgroup NVM Nonvolatile Memory
|
||||
* @ingroup internals
|
||||
* @details Nonvolatile Memory Class
|
||||
* @{
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "Flash.h"
|
||||
#include "VirtualPage.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
/**
|
||||
* @class NVRAMClass
|
||||
* @brief Nonvolatile Memory
|
||||
*/
|
||||
class NVRAMClass
|
||||
{
|
||||
public:
|
||||
//----------------------------------------------------------------------------
|
||||
/** Constructor. */
|
||||
NVRAMClass() {};
|
||||
//----------------------------------------------------------------------------
|
||||
/** Initialize Class */
|
||||
void begin() {};
|
||||
//----------------------------------------------------------------------------
|
||||
/** Deinitialize Class */
|
||||
void end() {};
|
||||
//----------------------------------------------------------------------------
|
||||
/** NVM available space in bytes
|
||||
* @return Number of bytes
|
||||
*/
|
||||
uint16_t length() const;
|
||||
//----------------------------------------------------------------------------
|
||||
/** Read a block of bytes
|
||||
* @param[in] *dst Write bytes to given pointer
|
||||
* @param[in] idx First NVM address to read
|
||||
* @param[in] n Number of bytes to read
|
||||
*/
|
||||
void read_block(uint8_t *dst, uint16_t idx, uint16_t n);
|
||||
//----------------------------------------------------------------------------
|
||||
/** Read a byte
|
||||
* @param[in] idx NVM Address to read
|
||||
* @return Byte from given address
|
||||
*/
|
||||
uint8_t read(const uint16_t idx);
|
||||
//----------------------------------------------------------------------------
|
||||
/** Write a block
|
||||
* @param[in] *src Read bytes from given pointer
|
||||
* @param[in] idx First NVM address to write
|
||||
* @param[in] n Number of bytes to write
|
||||
* @return success
|
||||
*/
|
||||
bool write_block(uint8_t *src, uint16_t idx, uint16_t n);
|
||||
//----------------------------------------------------------------------------
|
||||
/** Write a byte
|
||||
* @param[in] idx NVM Address to write
|
||||
* @param[in] value Byte to write
|
||||
* @return success
|
||||
*/
|
||||
bool write(const uint16_t idx, uint8_t value);
|
||||
//----------------------------------------------------------------------------
|
||||
/** Preserve the number of bytes in NVM log for time critical writes. Runtime
|
||||
* up to 5000ms!
|
||||
* @param[in] number Bytes to preserve, can 0 to find out free log space
|
||||
* @return Bytes available for writing
|
||||
*/
|
||||
int write_prepare(uint16_t number);
|
||||
//----------------------------------------------------------------------------
|
||||
/** lear log if full and prepare released pages for faster reallocation.
|
||||
* Runtime up to 5000ms!
|
||||
* @param[in] write_preserve Byte to preserve
|
||||
*/
|
||||
void clean_up(uint16_t write_preserve);
|
||||
|
||||
private:
|
||||
// Return a virtual page
|
||||
uint32_t *get_page();
|
||||
// Get actual log position
|
||||
uint16_t get_log_position(uint32_t *vpage);
|
||||
// Read a byte from page
|
||||
uint8_t get_byte_from_page(uint32_t *vpage, uint16_t log_start,
|
||||
uint16_t log_end, uint16_t idx);
|
||||
// switch a page
|
||||
uint32_t *switch_page(uint32_t *old_vpage, uint16_t *log_start,
|
||||
uint16_t *log_end);
|
||||
};
|
||||
|
||||
/** Variable to access the NVRAMClass */
|
||||
extern NVRAMClass NVRAM;
|
||||
|
||||
/** @} */
|
||||
51
lib/MySensors/drivers/NVM/README.md
Normal file
51
lib/MySensors/drivers/NVM/README.md
Normal file
@@ -0,0 +1,51 @@
|
||||
This code is based on (arduino-NVM)[https://github.com/d00616/arduino-NVM]. If you change code, please create a Pull Request for arduino-NVM to keep code synchronized.
|
||||
|
||||
# arduino-NVM
|
||||
|
||||
[](https://travis-ci.org/d00616/arduino-NVM)
|
||||
|
||||
This library allows the usage of internal Flash memory. To enhance the limited erase cycles a VirtualPage layer is available. On top of VirtualPage, there is an NVRAM class to allow a lot of writes by using a log-based storage.
|
||||
|
||||
For Arduino compatibility, a subset of avr/eeprom.h functionality and a complete port of EEPROM.h is provided.
|
||||
|
||||
Accessing bytes via NVRAM or EEPROM is faster than an AVR controller until the internal log is full. At this point, a new page must build. This process takes up to 3400ms (nRF51) or 1300ms (nRF52) depending on your hardware and the highest written address.
|
||||
|
||||
To find out more about timing, please run "test_all" example.
|
||||
|
||||
_This code is not compatible with any SoftDevice. You have to use the [radio notification](https://devzone.nordicsemi.com/tutorials/14/radio-notification/) and VirtualPage.clean_up()/NVRAM.write_prepare(NUMBER) to ensure that writes are only used in a time without radio activation._
|
||||
|
||||
## Flash.h
|
||||
|
||||
This class is the hardware abstraction to the Flash controller. Please look into Flash.h for a more detailed description.
|
||||
|
||||
Please read the documentation of your microcontroller to find out limitations about writing into flash. You can use the FLASH_... defines in your code to take care about quirks.
|
||||
|
||||
## VirtualPage.h
|
||||
|
||||
This class provides manages Flash pages. This helps you to reach more erase cycles and handle page faults. The underlying Flash page needs to hold some metadata so a VirtualPage is some bytes smaller than 4k. The size can differ between different hardware.
|
||||
|
||||
If you need to allocate VirtualPages in performance critical situations, call VirtualPage.clean_up(), when you have a time slot of more than 100ms.
|
||||
|
||||
For VirtualPages the last 16k(nRF51) or 32k(nRF52) are used. This allows the same number of erase cycles on both platforms.
|
||||
|
||||
## NVRAM.h
|
||||
|
||||
This class provides a 3072 bytes large memory. You can access this memory in a random order without needing to take care of the underlying flash architecture. This class is stateless, this means there is nothing cached in RAM. With every access, the data structure is parsed. This saves RAM and avoids conflicts when you have more than one instance of NVRAM class in your code.
|
||||
|
||||
To reach a maximum of write cycles and performance, place all your data at the beginning of the memory. This allows a maximum of write cycles.
|
||||
|
||||
When you only use the first 8 Bytes of the NVRAM, you have 5,100,000 write cycles per byte. If you use all 3072 bytes, you have only 3,300 write cycles per byte.
|
||||
|
||||
For your own calculation of write cycles, you can calculate the sum of available writes with: (VirtualPage.length()-4-HIGHEST_ADDRESS/4)*40,000
|
||||
|
||||
Reading or writing the NVRAM is fast until the internal log is full. On nRF51 you can calculate with 1.2ms and on nRF52 with 0,5ms. If the log is full a new VirtualPage is allocated and written. This procedure can take a time of 3400ms (nRF51) or 1300ms (nRF52).
|
||||
|
||||
If you use this code in performance critical situations. Use NVRAM.write_prepare(NUMBER) before to guarantee a fast write for the given number of bytes.
|
||||
|
||||
## EEPROM.h and avr_eeprom.h
|
||||
|
||||
Both libraries are for Arduino compatibility. Instead of avr/eeprom.h, you have to include avr_eeprom.h. This file maps a limited set of functions to NVRAM.
|
||||
|
||||
The EEPROM.h is fully compatible with the AVR version without committing changes.
|
||||
|
||||
If you use one of both files, please keep in mind that writing a single byte is fast until the log becomes full. In that case, a single write operation can take up to 3400ms (nRF51) or 1300ms (nRF52).
|
||||
349
lib/MySensors/drivers/NVM/VirtualPage.cpp
Normal file
349
lib/MySensors/drivers/NVM/VirtualPage.cpp
Normal file
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
VirtualPage.cpp - Flash page management
|
||||
Original Copyright (c) 2017 Frank Holtz. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "VirtualPage.h"
|
||||
|
||||
VirtualPageClass VirtualPage;
|
||||
|
||||
#ifndef NVM_VIRTUAL_PAGE_SIZE_BITS
|
||||
#define NVM_VIRTUAL_PAGE_SIZE_BITS 12
|
||||
#elif NVM_VIRTUAL_PAGE_SIZE_BITS < 12
|
||||
#error "NVM_VIRTUAL_PAGE_SIZE_BITS must be >= 12"
|
||||
#endif
|
||||
|
||||
// Calculate virtual page count, when mcuboot is present
|
||||
#if defined(MCUBOOT_PRESENT) && !defined(NVM_VIRTUAL_PAGE_COUNT)
|
||||
// mcuboot zephyr build via generated_dts_board.h
|
||||
#include "generated_dts_board.h"
|
||||
// Calculate number of free pages after scratch area
|
||||
#define NVM_VIRTUAL_PAGE_COUNT (((CONFIG_FLASH_SIZE_0<<10)-(FLASH_AREA_IMAGE_SCRATCH_OFFSET_0+FLASH_AREA_IMAGE_SCRATCH_SIZE_0)) >> NVM_VIRTUAL_PAGE_SIZE_BITS)
|
||||
#endif
|
||||
|
||||
// check page size
|
||||
#ifndef NVM_VIRTUAL_PAGE_COUNT
|
||||
#if FLASH_ERASE_CYCLES >= 20000
|
||||
// use 16k of flash memory
|
||||
#define NVM_VIRTUAL_PAGE_COUNT 4
|
||||
#else
|
||||
// use 32k of flash memory
|
||||
#define NVM_VIRTUAL_PAGE_COUNT 8
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* How many virtual pages are skipped from top of flash
|
||||
*/
|
||||
#ifndef NVM_VIRTUAL_PAGE_SKIP_FROM_TOP
|
||||
#define NVM_VIRTUAL_PAGE_SKIP_FROM_TOP 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Calculate things around NVM_VIRTUAL_PAGE_SIZE
|
||||
*/
|
||||
#define NVM_VIRTUAL_PAGE_SIZE (1 << (NVM_VIRTUAL_PAGE_SIZE_BITS))
|
||||
#define NVM_VIRTUAL_PAGE_ADDRESS_MASK (~(NVM_VIRTUAL_PAGE_SIZE - 1))
|
||||
#define NVM_VIRTUAL_PAGE_ALIGN(address) \
|
||||
{ address = (uint32_t *)((uint32_t)address & NVM_VIRTUAL_PAGE_ADDRESS_MASK); }
|
||||
|
||||
/*
|
||||
* Defines the position of status words in a page.
|
||||
* Offsets are defined in words!
|
||||
*/
|
||||
#ifdef FLASH_SUPPORTS_RANDOM_WRITE
|
||||
// use first 8 byte for magic, erase counter and status
|
||||
#define OFFSET_MAGIC 0
|
||||
#define OFFSET_ERASE_COUNTER 1
|
||||
#if FLASH_WRITES_PER_WORD > 2
|
||||
// use first 12 bytes for magic, erase counter and status
|
||||
#define MASK_ERASE_COUNTER 0x00FFFFFF
|
||||
#define OFFSET_STATUS_RELEASE_PREPARE 1
|
||||
#define OFFSET_STATUS_RELEASE_END 1
|
||||
#define METADATA_SIZE 8
|
||||
#define OFFSET_DATA 2
|
||||
#elif FLASH_WRITES_PER_WORD == 2
|
||||
#define MASK_ERASE_COUNTER 0x00FFFFFF
|
||||
#define OFFSET_STATUS_RELEASE_PREPARE 2
|
||||
#define OFFSET_STATUS_RELEASE_END 2
|
||||
#define METADATA_SIZE 12
|
||||
#define OFFSET_DATA 3
|
||||
#else
|
||||
// use first 12 bytes for erase counter, and magic
|
||||
#define OFFSET_MAGIC 1
|
||||
#define OFFSET_COUNTER 0
|
||||
#define MASK_ERASE_COUNTER 0x00FFFFFF
|
||||
#define OFFSET_STATUS_RELEASE_PREPARE NVM_VIRTUAL_PAGE_SIZE - 8
|
||||
#define OFFSET_STATUS_RELEASE_END NVM_VIRTUAL_PAGE_SIZE - 4
|
||||
#define METADATA_SIZE 16
|
||||
#define OFFSET_DATA 4
|
||||
#endif
|
||||
|
||||
#define BIT_STATUS_RELEASE_PREPARE (1 << 30)
|
||||
#define BIT_STATUS_RELEASE_END (1 << 31)
|
||||
|
||||
#define NVM_VIRTUAL_PAGE_DATA_SIZE (NVM_VIRTUAL_PAGE_SIZE - METADATA_SIZE)
|
||||
#else
|
||||
// use first 8 byte for magic and erase counter and last 8 byte for page release
|
||||
#define OFFSET_MAGIC 1
|
||||
#define OFFSET_ERASE_COUNTER 0
|
||||
#define OFFSET_DATA 2
|
||||
#define OFFSET_STATUS_RELEASE_PREPARE \
|
||||
((NVM_VIRTUAL_PAGE_SIZE - 8) / sizeof(uint32_t))
|
||||
#define OFFSET_STATUS_RELEASE_END \
|
||||
((NVM_VIRTUAL_PAGE_SIZE - 4) / sizeof(uint32_t))
|
||||
|
||||
#define MASK_ERASE_COUNTER 0xFFFFFFFF
|
||||
|
||||
#define BIT_STATUS_RELEASE_PREPARE 1
|
||||
#define BIT_STATUS_RELEASE_END 1
|
||||
|
||||
#define NVM_VIRTUAL_PAGE_DATA_SIZE (NVM_VIRTUAL_PAGE_SIZE - 16)
|
||||
|
||||
#endif
|
||||
|
||||
uint16_t VirtualPageClass::size() const
|
||||
{
|
||||
return (NVM_VIRTUAL_PAGE_DATA_SIZE);
|
||||
}
|
||||
|
||||
uint16_t VirtualPageClass::length() const
|
||||
{
|
||||
return (NVM_VIRTUAL_PAGE_DATA_SIZE / 4);
|
||||
}
|
||||
|
||||
uint16_t VirtualPageClass::page_count() const
|
||||
{
|
||||
return (NVM_VIRTUAL_PAGE_COUNT - 1);
|
||||
}
|
||||
|
||||
uint32_t VirtualPageClass::wear_level()
|
||||
{
|
||||
uint32_t max_erase_cycles = 0;
|
||||
for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) {
|
||||
uint32_t erase_cycles = get_page_erase_cycles(get_page_address(i));
|
||||
if (erase_cycles > max_erase_cycles) {
|
||||
max_erase_cycles = erase_cycles;
|
||||
}
|
||||
}
|
||||
return (uint32_t)((((uint64_t)max_erase_cycles * 10000)) /
|
||||
Flash.specified_erase_cycles());
|
||||
}
|
||||
|
||||
uint32_t *VirtualPageClass::get(uint32_t magic)
|
||||
{
|
||||
|
||||
// Give back a page prepared for release and not closed
|
||||
for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) {
|
||||
uint32_t *page = get_page_address(i);
|
||||
if (
|
||||
// correct magic is set
|
||||
(page[OFFSET_MAGIC] == magic) &&
|
||||
// page is in release_prepare mode
|
||||
((page[OFFSET_STATUS_RELEASE_PREPARE] & BIT_STATUS_RELEASE_PREPARE) ==
|
||||
0) &&
|
||||
// page is not released
|
||||
((page[OFFSET_STATUS_RELEASE_END] & BIT_STATUS_RELEASE_END) > 0)) {
|
||||
// Return page in release process with priority
|
||||
return &page[OFFSET_DATA];
|
||||
}
|
||||
}
|
||||
|
||||
// check if a unreleased page is available
|
||||
for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) {
|
||||
uint32_t *page = get_page_address(i);
|
||||
if (
|
||||
// correct magic is set
|
||||
(page[OFFSET_MAGIC] == magic) &&
|
||||
// page is not released
|
||||
((page[OFFSET_STATUS_RELEASE_END] & BIT_STATUS_RELEASE_END) > 0)) {
|
||||
// return page in normal operation
|
||||
return &page[OFFSET_DATA];
|
||||
}
|
||||
}
|
||||
|
||||
return (uint32_t *)(~0);
|
||||
}
|
||||
|
||||
uint32_t *VirtualPageClass::allocate(uint32_t magic)
|
||||
{
|
||||
uint32_t *return_page = (uint32_t *)(~0);
|
||||
uint32_t max_erase_cycles = (uint32_t)~0;
|
||||
|
||||
// Avoid duplicate allocation of pages, look for the less used page
|
||||
for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) {
|
||||
uint32_t *page = get_page_address(i);
|
||||
|
||||
// Delete duplicated pages
|
||||
if (
|
||||
// same magic
|
||||
(page[OFFSET_MAGIC] == magic) &&
|
||||
// Not in release_end state
|
||||
((page[OFFSET_STATUS_RELEASE_END] & BIT_STATUS_RELEASE_END) > 0) &&
|
||||
// Not in release_prepare state
|
||||
(!release_started(page))) {
|
||||
// clear the page
|
||||
build_page(page, (uint32_t)~0);
|
||||
}
|
||||
|
||||
uint32_t erase_cycles = get_page_erase_cycles(page);
|
||||
// When the page has less erase cycles and is not marked as failed
|
||||
if ((erase_cycles < max_erase_cycles) && (page[OFFSET_MAGIC] > 0) &&
|
||||
(
|
||||
// magic is empty
|
||||
(page[OFFSET_MAGIC] == (uint32_t)~0) ||
|
||||
// marked as released
|
||||
((page[OFFSET_STATUS_RELEASE_END] & BIT_STATUS_RELEASE_END) ==
|
||||
0))) {
|
||||
max_erase_cycles = erase_cycles;
|
||||
return_page = page;
|
||||
}
|
||||
}
|
||||
|
||||
// return if no page was found
|
||||
if (return_page == (uint32_t *)~0) {
|
||||
return return_page;
|
||||
}
|
||||
|
||||
build_page(return_page, magic);
|
||||
return &return_page[OFFSET_DATA];
|
||||
}
|
||||
|
||||
uint32_t *VirtualPageClass::allocate(uint32_t magic, uint32_t max_writes)
|
||||
{
|
||||
// max_writes is not implemented yet -> page is erased with every allocate
|
||||
(void)max_writes;
|
||||
return allocate(magic);
|
||||
}
|
||||
|
||||
void VirtualPageClass::release_prepare(uint32_t *address)
|
||||
{
|
||||
// move pointer to beginning of the page
|
||||
NVM_VIRTUAL_PAGE_ALIGN(address);
|
||||
|
||||
// Nothing to do at a empty page
|
||||
if (address[OFFSET_MAGIC] == (uint32_t)~0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (release_started(address) == false) {
|
||||
// Clear bit BIT_PAGE_RELEASED
|
||||
Flash.write(&address[OFFSET_STATUS_RELEASE_PREPARE],
|
||||
address[OFFSET_STATUS_RELEASE_PREPARE] &
|
||||
~BIT_STATUS_RELEASE_PREPARE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void VirtualPageClass::release(uint32_t *address)
|
||||
{
|
||||
// move pointer to beginning of the page
|
||||
NVM_VIRTUAL_PAGE_ALIGN(address);
|
||||
|
||||
// Nothing to do at a empty page
|
||||
if (address[OFFSET_MAGIC] == (uint32_t)~0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if status bit already cleared
|
||||
if ((address[OFFSET_STATUS_RELEASE_END] & BIT_STATUS_RELEASE_END) > 0) {
|
||||
// Clear bit BIT_PAGE_RELEASED
|
||||
Flash.write(&address[OFFSET_STATUS_RELEASE_END],
|
||||
address[OFFSET_STATUS_RELEASE_END] & ~BIT_STATUS_RELEASE_END);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
bool VirtualPageClass::release_started(uint32_t *address)
|
||||
{
|
||||
// move pointer to beginning of the page
|
||||
NVM_VIRTUAL_PAGE_ALIGN(address);
|
||||
|
||||
return (address[OFFSET_STATUS_RELEASE_PREPARE] &
|
||||
BIT_STATUS_RELEASE_PREPARE) == 0;
|
||||
}
|
||||
|
||||
void VirtualPageClass::fail(uint32_t *address)
|
||||
{
|
||||
// move pointer to beginning of the page
|
||||
NVM_VIRTUAL_PAGE_ALIGN(address);
|
||||
|
||||
build_page(address, 0x00000000);
|
||||
return;
|
||||
}
|
||||
|
||||
void VirtualPageClass::clean_up()
|
||||
{
|
||||
// No page found -> try to give back a page prepared for release
|
||||
for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) {
|
||||
uint32_t *page = get_page_address(i);
|
||||
if ((page[OFFSET_STATUS_RELEASE_END] & BIT_STATUS_RELEASE_END) == 0) {
|
||||
build_page(get_page_address(i), ~0);
|
||||
return; // a maximum of a page is cleaned -> return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VirtualPageClass::format()
|
||||
{
|
||||
for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) {
|
||||
uint32_t *address = get_page_address(i);
|
||||
build_page(address, (uint32_t)~0);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t *VirtualPageClass::get_page_address(uint16_t page)
|
||||
{
|
||||
return (uint32_t *)(Flash.top_app_page_address() -
|
||||
((page + NVM_VIRTUAL_PAGE_SKIP_FROM_TOP)
|
||||
<< NVM_VIRTUAL_PAGE_SIZE_BITS));
|
||||
}
|
||||
|
||||
void VirtualPageClass::build_page(uint32_t *address, uint32_t magic)
|
||||
{
|
||||
// move pointer to beginning of the page
|
||||
NVM_VIRTUAL_PAGE_ALIGN(address);
|
||||
// get erase counter
|
||||
uint32_t erase_counter = get_page_erase_cycles(address);
|
||||
|
||||
// Check if a magic is set
|
||||
if (address[OFFSET_MAGIC] != (uint32_t)~0) {
|
||||
Flash.erase(address, NVM_VIRTUAL_PAGE_SIZE);
|
||||
} else {
|
||||
// check if page is empty
|
||||
for (int i = OFFSET_DATA; i < (NVM_VIRTUAL_PAGE_SIZE / 4); i++) {
|
||||
if (address[i] != (uint32_t)~0) {
|
||||
Flash.erase(address, NVM_VIRTUAL_PAGE_SIZE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write a new page
|
||||
Flash.write(&address[OFFSET_MAGIC], magic);
|
||||
if (address[OFFSET_ERASE_COUNTER] == (uint32_t)~0) {
|
||||
Flash.write(&address[OFFSET_ERASE_COUNTER],
|
||||
erase_counter | ~MASK_ERASE_COUNTER);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t VirtualPageClass::get_page_erase_cycles(uint32_t *address)
|
||||
{
|
||||
// Return number of cycles
|
||||
return ((((uint32_t)address[OFFSET_ERASE_COUNTER])+1) &
|
||||
(uint32_t)MASK_ERASE_COUNTER);
|
||||
}
|
||||
139
lib/MySensors/drivers/NVM/VirtualPage.h
Normal file
139
lib/MySensors/drivers/NVM/VirtualPage.h
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
VirtualPage.h - Flash page management
|
||||
Original Copyright (c) 2017 Frank Holtz. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
/**
|
||||
* @file VirtualPage.h
|
||||
* @brief Virtual page management on top of Flash
|
||||
* The managed pages are organized into VirtualPage.size() sized pages.
|
||||
* Some Bytes of a Flash page are reserved for a magic number and page management.
|
||||
*
|
||||
* @ingroup NVM
|
||||
* @details Nonvolatile Memory Class
|
||||
* @{
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "Flash.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
/**
|
||||
* @class VirtualPageClass
|
||||
* @brief Virtual page management on top of Flash
|
||||
*/
|
||||
class VirtualPageClass
|
||||
{
|
||||
public:
|
||||
//----------------------------------------------------------------------------
|
||||
/** Constructor */
|
||||
VirtualPageClass() {};
|
||||
//----------------------------------------------------------------------------
|
||||
/** Initialize Virtual Pages */
|
||||
void begin() {};
|
||||
//----------------------------------------------------------------------------
|
||||
/** Deinitilize Virtual Pages */
|
||||
void end() {};
|
||||
//----------------------------------------------------------------------------
|
||||
/** Reports usable page size in bytes
|
||||
* @return Number of bytes
|
||||
*/
|
||||
uint16_t size() const;
|
||||
//----------------------------------------------------------------------------
|
||||
/** Reports usable page size in 32 Bit words
|
||||
* @return Number of words
|
||||
*/
|
||||
uint16_t length() const;
|
||||
//----------------------------------------------------------------------------
|
||||
/** Reports the maximum number of allocatable pages
|
||||
* @return Number of pages
|
||||
*/
|
||||
uint16_t page_count() const;
|
||||
//----------------------------------------------------------------------------
|
||||
/** Calculates the rate of wear in percent*100.
|
||||
* Values greater than 10000 indicates exceeding the chip specification.
|
||||
* This value is only valid for controllers, never erased completely.
|
||||
* @return calculated level
|
||||
*/
|
||||
uint32_t wear_level();
|
||||
//----------------------------------------------------------------------------
|
||||
/** Search for a page by given, unique magic number.
|
||||
* Returns a pointer to (uint32_t *)~0 if there is no page. Don't write
|
||||
* to this address.
|
||||
* @param[in] magic A magic number to identify the page.
|
||||
* @return Pointer to a page
|
||||
*/
|
||||
uint32_t *get(uint32_t magic);
|
||||
//----------------------------------------------------------------------------
|
||||
/** Returns an address to a blank page or (uint32_t *)~0 if no space
|
||||
* available. Take care about RADIO, WDT and Interrupt timing! Calculate with
|
||||
* 0-100ms until a page is available.
|
||||
* @param[in] magic A magic number to identify the page.
|
||||
* @return Pointer to a page
|
||||
*/
|
||||
uint32_t *allocate(uint32_t magic);
|
||||
//----------------------------------------------------------------------------
|
||||
/** Returns an address to a blank page or (uint32_t *)~0 if no space
|
||||
* available. Take care about RADIO, WDT and Interrupt timing! Calculate with
|
||||
* 0-100ms until a page is available.
|
||||
* This function allows using a page multiple times without erasing on some platforms.
|
||||
* @param[in] magic A magic number to identify the page.
|
||||
* @param[in] max_writes The number of planned write operations in page lifecycle.
|
||||
* @return Pointer to a page
|
||||
*/
|
||||
uint32_t *allocate(uint32_t magic, uint32_t max_writes);
|
||||
//----------------------------------------------------------------------------
|
||||
/** Start releasing a page. You have to allocate a new one and then release the
|
||||
* old page.
|
||||
* @param[in] *address A pointer to the page to release
|
||||
*/
|
||||
void release_prepare(uint32_t *address);
|
||||
//----------------------------------------------------------------------------
|
||||
/** End releasing a page.
|
||||
* @param[in] *address A pointer to the page to release
|
||||
*/
|
||||
void release(uint32_t *address);
|
||||
//----------------------------------------------------------------------------
|
||||
/** Returns true if page is in release_prepare state
|
||||
* @param[in] *address A pointer to a page
|
||||
* @return Release state
|
||||
*/
|
||||
bool release_started(uint32_t *address);
|
||||
//----------------------------------------------------------------------------
|
||||
/** Mark a page as a defect page.
|
||||
*/
|
||||
void fail(uint32_t *address);
|
||||
//----------------------------------------------------------------------------
|
||||
/** Prepare released pages for faster reallocation. Plan with 0-100ms.
|
||||
*/
|
||||
void clean_up();
|
||||
//----------------------------------------------------------------------------
|
||||
/** Release all pages
|
||||
*/
|
||||
void format();
|
||||
|
||||
private:
|
||||
// convert page number 1..max_pages into an address
|
||||
uint32_t *get_page_address(uint16_t page);
|
||||
// build a page
|
||||
void build_page(uint32_t *address, uint32_t magic);
|
||||
// return number of erase cycles
|
||||
uint32_t get_page_erase_cycles(uint32_t *address);
|
||||
};
|
||||
|
||||
extern VirtualPageClass VirtualPage; //!< extern VirtualPageClass
|
||||
|
||||
/** @} */
|
||||
76
lib/MySensors/drivers/PubSubClient/CHANGES.txt
Normal file
76
lib/MySensors/drivers/PubSubClient/CHANGES.txt
Normal file
@@ -0,0 +1,76 @@
|
||||
2.7
|
||||
* Fix remaining-length handling to prevent buffer overrun
|
||||
* Add large-payload API - beginPublish/write/publish/endPublish
|
||||
* Add yield call to improve reliability on ESP
|
||||
* Add Clean Session flag to connect options
|
||||
* Add ESP32 support for functional callback signature
|
||||
* Various other fixes
|
||||
|
||||
2.4
|
||||
* Add MQTT_SOCKET_TIMEOUT to prevent it blocking indefinitely
|
||||
whilst waiting for inbound data
|
||||
* Fixed return code when publishing >256 bytes
|
||||
|
||||
2.3
|
||||
* Add publish(topic,payload,retained) function
|
||||
|
||||
2.2
|
||||
* Change code layout to match Arduino Library reqs
|
||||
|
||||
2.1
|
||||
* Add MAX_TRANSFER_SIZE def to chunk messages if needed
|
||||
* Reject topic/payloads that exceed MQTT_MAX_PACKET_SIZE
|
||||
|
||||
2.0
|
||||
* Add (and default to) MQTT 3.1.1 support
|
||||
* Fix PROGMEM handling for Intel Galileo/ESP8266
|
||||
* Add overloaded constructors for convenience
|
||||
* Add chainable setters for server/callback/client/stream
|
||||
* Add state function to return connack return code
|
||||
|
||||
1.9
|
||||
* Do not split MQTT packets over multiple calls to _client->write()
|
||||
* API change: All constructors now require an instance of Client
|
||||
to be passed in.
|
||||
* Fixed example to match 1.8 api changes - dpslwk
|
||||
* Added username/password support - WilHall
|
||||
* Added publish_P - publishes messages from PROGMEM - jobytaffey
|
||||
|
||||
1.8
|
||||
* KeepAlive interval is configurable in PubSubClient.h
|
||||
* Maximum packet size is configurable in PubSubClient.h
|
||||
* API change: Return bool rather than int from various functions
|
||||
* API change: Length parameter in message callback changed
|
||||
from int to unsigned int
|
||||
* Various internal tidy-ups around types
|
||||
1.7
|
||||
* Improved keepalive handling
|
||||
* Updated to the Arduino-1.0 API
|
||||
1.6
|
||||
* Added the ability to publish a retained message
|
||||
|
||||
1.5
|
||||
* Added default constructor
|
||||
* Fixed compile error when used with arduino-0021 or later
|
||||
|
||||
1.4
|
||||
* Fixed connection lost handling
|
||||
|
||||
1.3
|
||||
* Fixed packet reading bug in PubSubClient.readPacket
|
||||
|
||||
1.2
|
||||
* Fixed compile error when used with arduino-0016 or later
|
||||
|
||||
|
||||
1.1
|
||||
* Reduced size of library
|
||||
* Added support for Will messages
|
||||
* Clarified licensing - see LICENSE.txt
|
||||
|
||||
|
||||
1.0
|
||||
* Only Quality of Service (QOS) 0 messaging is supported
|
||||
* The maximum message size, including header, is 128 bytes
|
||||
* The keepalive interval is set to 30 seconds
|
||||
* No support for Will messages
|
||||
20
lib/MySensors/drivers/PubSubClient/LICENSE.txt
Normal file
20
lib/MySensors/drivers/PubSubClient/LICENSE.txt
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2008-2015 Nicholas O'Leary
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
736
lib/MySensors/drivers/PubSubClient/PubSubClient.cpp
Normal file
736
lib/MySensors/drivers/PubSubClient/PubSubClient.cpp
Normal file
@@ -0,0 +1,736 @@
|
||||
/*
|
||||
PubSubClient.cpp - A simple client for MQTT.
|
||||
Nick O'Leary
|
||||
http://knolleary.net
|
||||
*/
|
||||
|
||||
#include "PubSubClient.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
// Suppress uninitialized member variable in all constructors because some memory can be saved with
|
||||
// on-demand initialization of these members
|
||||
// cppcheck-suppress uninitMemberVar
|
||||
PubSubClient::PubSubClient()
|
||||
{
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
this->_client = NULL;
|
||||
this->stream = NULL;
|
||||
setCallback(NULL);
|
||||
}
|
||||
// cppcheck-suppress uninitMemberVar
|
||||
PubSubClient::PubSubClient(Client& client)
|
||||
{
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
}
|
||||
// cppcheck-suppress uninitMemberVar
|
||||
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client)
|
||||
{
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(addr, port);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
}
|
||||
// cppcheck-suppress uninitMemberVar
|
||||
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client, Stream& stream)
|
||||
{
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(addr,port);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
}
|
||||
// cppcheck-suppress uninitMemberVar
|
||||
// cppcheck-suppress passedByValue
|
||||
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client)
|
||||
{
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(addr, port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
}
|
||||
// cppcheck-suppress uninitMemberVar
|
||||
// cppcheck-suppress passedByValue
|
||||
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client,
|
||||
Stream& stream)
|
||||
{
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(addr,port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
}
|
||||
// cppcheck-suppress uninitMemberVar
|
||||
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client)
|
||||
{
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(ip, port);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
}
|
||||
// cppcheck-suppress uninitMemberVar
|
||||
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client, Stream& stream)
|
||||
{
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(ip,port);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
}
|
||||
// cppcheck-suppress uninitMemberVar
|
||||
// cppcheck-suppress passedByValue
|
||||
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client)
|
||||
{
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(ip, port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
}
|
||||
// cppcheck-suppress uninitMemberVar
|
||||
// cppcheck-suppress passedByValue
|
||||
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client,
|
||||
Stream& stream)
|
||||
{
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(ip,port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
}
|
||||
// cppcheck-suppress uninitMemberVar
|
||||
PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client)
|
||||
{
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(domain,port);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
}
|
||||
// cppcheck-suppress uninitMemberVar
|
||||
PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client, Stream& stream)
|
||||
{
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(domain,port);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
}
|
||||
// cppcheck-suppress uninitMemberVar
|
||||
// cppcheck-suppress passedByValue
|
||||
PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE,
|
||||
Client& client)
|
||||
{
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(domain,port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
this->stream = NULL;
|
||||
}
|
||||
// cppcheck-suppress uninitMemberVar
|
||||
// cppcheck-suppress passedByValue
|
||||
PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE,
|
||||
Client& client, Stream& stream)
|
||||
{
|
||||
this->_state = MQTT_DISCONNECTED;
|
||||
setServer(domain,port);
|
||||
setCallback(callback);
|
||||
setClient(client);
|
||||
setStream(stream);
|
||||
}
|
||||
|
||||
bool PubSubClient::connect(const char *id)
|
||||
{
|
||||
return connect(id,NULL,NULL,0,0,0,0,1);
|
||||
}
|
||||
|
||||
bool PubSubClient::connect(const char *id, const char *user, const char *pass)
|
||||
{
|
||||
return connect(id,user,pass,0,0,0,0,1);
|
||||
}
|
||||
|
||||
bool PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos,
|
||||
bool willRetain, const char* willMessage)
|
||||
{
|
||||
return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage,1);
|
||||
}
|
||||
|
||||
bool PubSubClient::connect(const char *id, const char *user, const char *pass,
|
||||
const char* willTopic, uint8_t willQos, bool willRetain, const char* willMessage)
|
||||
{
|
||||
return connect(id,user,pass,willTopic,willQos,willRetain,willMessage,1);
|
||||
}
|
||||
|
||||
bool PubSubClient::connect(const char *id, const char *user, const char *pass,
|
||||
const char* willTopic, uint8_t willQos, bool willRetain, const char* willMessage,
|
||||
bool cleanSession)
|
||||
{
|
||||
if (!connected()) {
|
||||
int result = 0;
|
||||
|
||||
if (domain != NULL) {
|
||||
result = _client->connect(this->domain, this->port);
|
||||
} else {
|
||||
result = _client->connect(this->ip, this->port);
|
||||
}
|
||||
if (result == 1) {
|
||||
nextMsgId = 1;
|
||||
// Leave room in the buffer for header and variable length field
|
||||
uint16_t length = MQTT_MAX_HEADER_SIZE;
|
||||
unsigned int j;
|
||||
|
||||
#if MQTT_VERSION == MQTT_VERSION_3_1
|
||||
uint8_t d[9] = {0x00,0x06,'M','Q','I','s','d','p', MQTT_VERSION};
|
||||
#define MQTT_HEADER_VERSION_LENGTH 9
|
||||
#elif MQTT_VERSION == MQTT_VERSION_3_1_1
|
||||
uint8_t d[7] = {0x00,0x04,'M','Q','T','T',MQTT_VERSION};
|
||||
#define MQTT_HEADER_VERSION_LENGTH 7
|
||||
#endif
|
||||
for (j = 0; j<MQTT_HEADER_VERSION_LENGTH; j++) {
|
||||
buffer[length++] = d[j];
|
||||
}
|
||||
|
||||
uint8_t v;
|
||||
if (willTopic) {
|
||||
v = 0x04|(willQos<<3)|(willRetain<<5);
|
||||
} else {
|
||||
v = 0x00;
|
||||
}
|
||||
if (cleanSession) {
|
||||
v = v|0x02;
|
||||
}
|
||||
|
||||
if(user != NULL) {
|
||||
v = v|0x80;
|
||||
|
||||
if(pass != NULL) {
|
||||
v = v|(0x80>>1);
|
||||
}
|
||||
}
|
||||
|
||||
buffer[length++] = v;
|
||||
|
||||
buffer[length++] = ((MQTT_KEEPALIVE) >> 8);
|
||||
buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF);
|
||||
|
||||
CHECK_STRING_LENGTH(length,id)
|
||||
length = writeString(id,buffer,length);
|
||||
if (willTopic) {
|
||||
CHECK_STRING_LENGTH(length,willTopic)
|
||||
length = writeString(willTopic,buffer,length);
|
||||
CHECK_STRING_LENGTH(length,willMessage)
|
||||
length = writeString(willMessage,buffer,length);
|
||||
}
|
||||
|
||||
if(user != NULL) {
|
||||
CHECK_STRING_LENGTH(length,user)
|
||||
length = writeString(user,buffer,length);
|
||||
if(pass != NULL) {
|
||||
CHECK_STRING_LENGTH(length,pass)
|
||||
length = writeString(pass,buffer,length);
|
||||
}
|
||||
}
|
||||
|
||||
write(MQTTCONNECT,buffer,length-MQTT_MAX_HEADER_SIZE);
|
||||
|
||||
lastInActivity = lastOutActivity = millis();
|
||||
|
||||
while (!_client->available()) {
|
||||
unsigned long t = millis();
|
||||
if (t-lastInActivity >= ((int32_t) MQTT_SOCKET_TIMEOUT*1000UL)) {
|
||||
_state = MQTT_CONNECTION_TIMEOUT;
|
||||
_client->stop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
uint8_t llen;
|
||||
uint16_t len = readPacket(&llen);
|
||||
|
||||
if (len == 4) {
|
||||
if (buffer[3] == 0) {
|
||||
lastInActivity = millis();
|
||||
pingOutstanding = false;
|
||||
_state = MQTT_CONNECTED;
|
||||
return true;
|
||||
} else {
|
||||
_state = buffer[3];
|
||||
}
|
||||
}
|
||||
_client->stop();
|
||||
} else {
|
||||
_state = MQTT_CONNECT_FAILED;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// reads a byte into result
|
||||
bool PubSubClient::readByte(uint8_t * result)
|
||||
{
|
||||
uint32_t previousMillis = millis();
|
||||
while(!_client->available()) {
|
||||
yield();
|
||||
uint32_t currentMillis = millis();
|
||||
if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*result = _client->read();
|
||||
return true;
|
||||
}
|
||||
|
||||
// reads a byte into result[*index] and increments index
|
||||
bool PubSubClient::readByte(uint8_t * result, uint16_t * index)
|
||||
{
|
||||
uint16_t current_index = *index;
|
||||
uint8_t * write_address = &(result[current_index]);
|
||||
if(readByte(write_address)) {
|
||||
*index = current_index + 1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t PubSubClient::readPacket(uint8_t* lengthLength)
|
||||
{
|
||||
uint16_t len = 0;
|
||||
if(!readByte(buffer, &len)) {
|
||||
return 0;
|
||||
}
|
||||
bool isPublish = (buffer[0]&0xF0) == MQTTPUBLISH;
|
||||
uint32_t multiplier = 1;
|
||||
uint16_t length = 0;
|
||||
uint8_t digit = 0;
|
||||
uint16_t skip = 0;
|
||||
uint8_t start = 0;
|
||||
|
||||
do {
|
||||
if (len == 5) {
|
||||
// Invalid remaining length encoding - kill the connection
|
||||
_state = MQTT_DISCONNECTED;
|
||||
_client->stop();
|
||||
return 0;
|
||||
}
|
||||
if(!readByte(&digit)) {
|
||||
return 0;
|
||||
}
|
||||
buffer[len++] = digit;
|
||||
length += (digit & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
} while ((digit & 128) != 0);
|
||||
*lengthLength = len-1;
|
||||
|
||||
if (isPublish) {
|
||||
// Read in topic length to calculate bytes to skip over for Stream writing
|
||||
if(!readByte(buffer, &len)) {
|
||||
return 0;
|
||||
}
|
||||
if(!readByte(buffer, &len)) {
|
||||
return 0;
|
||||
}
|
||||
skip = (buffer[*lengthLength+1]<<8)+buffer[*lengthLength+2];
|
||||
start = 2;
|
||||
if (buffer[0]&MQTTQOS1) {
|
||||
// skip message id
|
||||
skip += 2;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint16_t i = start; i<length; i++) {
|
||||
if(!readByte(&digit)) {
|
||||
return 0;
|
||||
}
|
||||
if (this->stream) {
|
||||
if (isPublish && len-*lengthLength-2>skip) {
|
||||
this->stream->write(digit);
|
||||
}
|
||||
}
|
||||
if (len < MQTT_MAX_PACKET_SIZE) {
|
||||
buffer[len] = digit;
|
||||
}
|
||||
len++;
|
||||
}
|
||||
|
||||
if (!this->stream && len > MQTT_MAX_PACKET_SIZE) {
|
||||
len = 0; // This will cause the packet to be ignored.
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
bool PubSubClient::loop()
|
||||
{
|
||||
if (connected()) {
|
||||
unsigned long t = millis();
|
||||
if ((t - lastInActivity > MQTT_KEEPALIVE*1000UL) || (t - lastOutActivity > MQTT_KEEPALIVE*1000UL)) {
|
||||
if (pingOutstanding) {
|
||||
this->_state = MQTT_CONNECTION_TIMEOUT;
|
||||
_client->stop();
|
||||
return false;
|
||||
} else {
|
||||
buffer[0] = MQTTPINGREQ;
|
||||
buffer[1] = 0;
|
||||
_client->write(buffer,2);
|
||||
lastOutActivity = t;
|
||||
lastInActivity = t;
|
||||
pingOutstanding = true;
|
||||
}
|
||||
}
|
||||
if (_client->available()) {
|
||||
uint8_t llen;
|
||||
uint16_t len = readPacket(&llen);
|
||||
if (len > 0) {
|
||||
lastInActivity = t;
|
||||
uint8_t type = buffer[0]&0xF0;
|
||||
if (type == MQTTPUBLISH) {
|
||||
if (callback) {
|
||||
uint16_t tl = (buffer[llen+1]<<8)+buffer[llen+2]; /* topic length in bytes */
|
||||
memmove(buffer+llen+2,buffer+llen+3,tl); /* move topic inside buffer 1 byte to front */
|
||||
buffer[llen+2+tl] = 0; /* end the topic as a 'C' string with \x00 */
|
||||
char *topic = (char*) buffer+llen+2;
|
||||
uint8_t *payload;
|
||||
// msgId only present for QOS>0
|
||||
if ((buffer[0]&0x06) == MQTTQOS1) {
|
||||
uint16_t msgId = 0;
|
||||
msgId = (buffer[llen+3+tl]<<8)+buffer[llen+3+tl+1];
|
||||
payload = buffer+llen+3+tl+2;
|
||||
callback(topic,payload,len-llen-3-tl-2);
|
||||
|
||||
buffer[0] = MQTTPUBACK;
|
||||
buffer[1] = 2;
|
||||
buffer[2] = (msgId >> 8);
|
||||
buffer[3] = (msgId & 0xFF);
|
||||
_client->write(buffer,4);
|
||||
lastOutActivity = t;
|
||||
|
||||
} else {
|
||||
payload = buffer+llen+3+tl;
|
||||
callback(topic,payload,len-llen-3-tl);
|
||||
}
|
||||
}
|
||||
} else if (type == MQTTPINGREQ) {
|
||||
buffer[0] = MQTTPINGRESP;
|
||||
buffer[1] = 0;
|
||||
_client->write(buffer,2);
|
||||
} else if (type == MQTTPINGRESP) {
|
||||
pingOutstanding = false;
|
||||
}
|
||||
} else if (!connected()) {
|
||||
// readPacket has closed the connection
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PubSubClient::publish(const char* topic, const char* payload)
|
||||
{
|
||||
return publish(topic,(const uint8_t*)payload,strlen(payload),false);
|
||||
}
|
||||
|
||||
bool PubSubClient::publish(const char* topic, const char* payload, bool retained)
|
||||
{
|
||||
return publish(topic,(const uint8_t*)payload,strlen(payload),retained);
|
||||
}
|
||||
|
||||
bool PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength)
|
||||
{
|
||||
return publish(topic, payload, plength, false);
|
||||
}
|
||||
|
||||
bool PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength,
|
||||
bool retained)
|
||||
{
|
||||
if (connected()) {
|
||||
if (MQTT_MAX_PACKET_SIZE < MQTT_MAX_HEADER_SIZE + 2+strlen(topic) + plength) {
|
||||
// Too long
|
||||
return false;
|
||||
}
|
||||
// Leave room in the buffer for header and variable length field
|
||||
uint16_t length = MQTT_MAX_HEADER_SIZE;
|
||||
length = writeString(topic,buffer,length);
|
||||
uint16_t i;
|
||||
for (i=0; i<plength; i++) {
|
||||
buffer[length++] = payload[i];
|
||||
}
|
||||
uint8_t header = MQTTPUBLISH;
|
||||
if (retained) {
|
||||
header |= 1;
|
||||
}
|
||||
return write(header,buffer,length-MQTT_MAX_HEADER_SIZE);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PubSubClient::publish_P(const char* topic, const char* payload, bool retained)
|
||||
{
|
||||
return publish_P(topic, (const uint8_t*)payload, strlen(payload), retained);
|
||||
}
|
||||
|
||||
bool PubSubClient::publish_P(const char* topic, const uint8_t* payload, unsigned int plength,
|
||||
bool retained)
|
||||
{
|
||||
unsigned int rc = 0;
|
||||
uint16_t tlen;
|
||||
unsigned int pos = 0;
|
||||
unsigned int i;
|
||||
uint8_t header;
|
||||
unsigned int len;
|
||||
|
||||
if (!connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tlen = strlen(topic);
|
||||
|
||||
header = MQTTPUBLISH;
|
||||
if (retained) {
|
||||
header |= 1;
|
||||
}
|
||||
buffer[pos++] = header;
|
||||
len = plength + 2 + tlen;
|
||||
do {
|
||||
uint8_t digit;
|
||||
digit = len % 128;
|
||||
len = len / 128;
|
||||
if (len > 0) {
|
||||
digit |= 0x80;
|
||||
}
|
||||
buffer[pos++] = digit;
|
||||
} while(len>0);
|
||||
|
||||
pos = writeString(topic,buffer,pos);
|
||||
|
||||
rc += _client->write(buffer,pos);
|
||||
|
||||
for (i=0; i<plength; i++) {
|
||||
rc += _client->write((char)pgm_read_byte_near(payload + i));
|
||||
}
|
||||
|
||||
lastOutActivity = millis();
|
||||
|
||||
return rc == tlen + 4 + plength;
|
||||
}
|
||||
|
||||
bool PubSubClient::beginPublish(const char* topic, unsigned int plength, bool retained)
|
||||
{
|
||||
if (connected()) {
|
||||
// Send the header and variable length field
|
||||
uint16_t length = MQTT_MAX_HEADER_SIZE;
|
||||
length = writeString(topic,buffer,length);
|
||||
uint8_t header = MQTTPUBLISH;
|
||||
if (retained) {
|
||||
header |= 1;
|
||||
}
|
||||
size_t hlen = buildHeader(header, buffer, plength+length-MQTT_MAX_HEADER_SIZE);
|
||||
uint16_t rc = _client->write(buffer+(MQTT_MAX_HEADER_SIZE-hlen),length-(MQTT_MAX_HEADER_SIZE-hlen));
|
||||
lastOutActivity = millis();
|
||||
return (rc == (length-(MQTT_MAX_HEADER_SIZE-hlen)));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int PubSubClient::endPublish()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t PubSubClient::write(uint8_t data)
|
||||
{
|
||||
lastOutActivity = millis();
|
||||
return _client->write(data);
|
||||
}
|
||||
|
||||
size_t PubSubClient::write(const uint8_t *buffer, size_t size)
|
||||
{
|
||||
lastOutActivity = millis();
|
||||
return _client->write(buffer,size);
|
||||
}
|
||||
|
||||
size_t PubSubClient::buildHeader(uint8_t header, uint8_t* buf, uint16_t length)
|
||||
{
|
||||
uint8_t lenBuf[4];
|
||||
uint8_t llen = 0;
|
||||
uint8_t pos = 0;
|
||||
uint16_t len = length;
|
||||
do {
|
||||
uint8_t digit;
|
||||
digit = len % 128;
|
||||
len = len / 128;
|
||||
if (len > 0) {
|
||||
digit |= 0x80;
|
||||
}
|
||||
lenBuf[pos++] = digit;
|
||||
llen++;
|
||||
} while(len>0);
|
||||
|
||||
buf[4-llen] = header;
|
||||
for (int i=0; i<llen; i++) {
|
||||
buf[MQTT_MAX_HEADER_SIZE-llen+i] = lenBuf[i];
|
||||
}
|
||||
return llen+1; // Full header size is variable length bit plus the 1-byte fixed header
|
||||
}
|
||||
|
||||
bool PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length)
|
||||
{
|
||||
uint16_t rc;
|
||||
uint8_t hlen = buildHeader(header, buf, length);
|
||||
|
||||
#ifdef MQTT_MAX_TRANSFER_SIZE
|
||||
uint8_t* writeBuf = buf+(MQTT_MAX_HEADER_SIZE-hlen);
|
||||
uint16_t bytesRemaining = length+hlen; //Match the length type
|
||||
uint8_t bytesToWrite;
|
||||
bool result = true;
|
||||
while((bytesRemaining > 0) && result) {
|
||||
bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining;
|
||||
rc = _client->write(writeBuf,bytesToWrite);
|
||||
result = (rc == bytesToWrite);
|
||||
bytesRemaining -= rc;
|
||||
writeBuf += rc;
|
||||
}
|
||||
return result;
|
||||
#else
|
||||
rc = _client->write(buf+(MQTT_MAX_HEADER_SIZE-hlen),length+hlen);
|
||||
lastOutActivity = millis();
|
||||
return (rc == hlen+length);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool PubSubClient::subscribe(const char* topic)
|
||||
{
|
||||
return subscribe(topic, 0);
|
||||
}
|
||||
|
||||
bool PubSubClient::subscribe(const char* topic, uint8_t qos)
|
||||
{
|
||||
if (qos > 1) {
|
||||
return false;
|
||||
}
|
||||
if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
|
||||
// Too long
|
||||
return false;
|
||||
}
|
||||
if (connected()) {
|
||||
// Leave room in the buffer for header and variable length field
|
||||
uint16_t length = MQTT_MAX_HEADER_SIZE;
|
||||
nextMsgId++;
|
||||
if (nextMsgId == 0) {
|
||||
nextMsgId = 1;
|
||||
}
|
||||
buffer[length++] = (nextMsgId >> 8);
|
||||
buffer[length++] = (nextMsgId & 0xFF);
|
||||
length = writeString((char*)topic, buffer,length);
|
||||
buffer[length++] = qos;
|
||||
return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PubSubClient::unsubscribe(const char* topic)
|
||||
{
|
||||
if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
|
||||
// Too long
|
||||
return false;
|
||||
}
|
||||
if (connected()) {
|
||||
uint16_t length = MQTT_MAX_HEADER_SIZE;
|
||||
nextMsgId++;
|
||||
if (nextMsgId == 0) {
|
||||
nextMsgId = 1;
|
||||
}
|
||||
buffer[length++] = (nextMsgId >> 8);
|
||||
buffer[length++] = (nextMsgId & 0xFF);
|
||||
length = writeString(topic, buffer,length);
|
||||
return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PubSubClient::disconnect()
|
||||
{
|
||||
buffer[0] = MQTTDISCONNECT;
|
||||
buffer[1] = 0;
|
||||
_client->write(buffer,2);
|
||||
_state = MQTT_DISCONNECTED;
|
||||
_client->flush();
|
||||
_client->stop();
|
||||
lastInActivity = lastOutActivity = millis();
|
||||
}
|
||||
|
||||
uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, uint16_t pos)
|
||||
{
|
||||
const char* idp = string;
|
||||
uint16_t i = 0;
|
||||
pos += 2;
|
||||
while (*idp) {
|
||||
buf[pos++] = *idp++;
|
||||
i++;
|
||||
}
|
||||
buf[pos-i-2] = (i >> 8);
|
||||
buf[pos-i-1] = (i & 0xFF);
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
bool PubSubClient::connected()
|
||||
{
|
||||
bool rc;
|
||||
if (_client == NULL ) {
|
||||
rc = false;
|
||||
} else {
|
||||
rc = (int)_client->connected();
|
||||
if (!rc) {
|
||||
if (this->_state == MQTT_CONNECTED) {
|
||||
this->_state = MQTT_CONNECTION_LOST;
|
||||
_client->flush();
|
||||
_client->stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port)
|
||||
{
|
||||
IPAddress addr(ip[0],ip[1],ip[2],ip[3]);
|
||||
return setServer(addr,port);
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setServer(IPAddress ip, uint16_t port)
|
||||
{
|
||||
this->ip = ip;
|
||||
this->port = port;
|
||||
this->domain = NULL;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port)
|
||||
{
|
||||
this->domain = domain;
|
||||
this->port = port;
|
||||
return *this;
|
||||
}
|
||||
// cppcheck-suppress passedByValue
|
||||
PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE)
|
||||
{
|
||||
this->callback = callback;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setClient(Client& client)
|
||||
{
|
||||
this->_client = &client;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PubSubClient& PubSubClient::setStream(Stream& stream)
|
||||
{
|
||||
this->stream = &stream;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int PubSubClient::state()
|
||||
{
|
||||
return this->_state;
|
||||
}
|
||||
183
lib/MySensors/drivers/PubSubClient/PubSubClient.h
Normal file
183
lib/MySensors/drivers/PubSubClient/PubSubClient.h
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
PubSubClient.h - A simple client for MQTT.
|
||||
Nick O'Leary
|
||||
http://knolleary.net
|
||||
*/
|
||||
|
||||
#ifndef PubSubClient_h
|
||||
#define PubSubClient_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "IPAddress.h"
|
||||
#include "Client.h"
|
||||
#include "Stream.h"
|
||||
|
||||
#define MQTT_VERSION_3_1 3
|
||||
#define MQTT_VERSION_3_1_1 4
|
||||
|
||||
// MQTT_VERSION : Pick the version
|
||||
//#define MQTT_VERSION MQTT_VERSION_3_1
|
||||
#ifndef MQTT_VERSION
|
||||
#define MQTT_VERSION MQTT_VERSION_3_1_1
|
||||
#endif
|
||||
|
||||
// MQTT_MAX_PACKET_SIZE : Maximum packet size
|
||||
#ifndef MQTT_MAX_PACKET_SIZE
|
||||
#define MQTT_MAX_PACKET_SIZE 128
|
||||
#endif
|
||||
|
||||
// MQTT_KEEPALIVE : keepAlive interval in Seconds
|
||||
#ifndef MQTT_KEEPALIVE
|
||||
#define MQTT_KEEPALIVE 15
|
||||
#endif
|
||||
|
||||
// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds
|
||||
#ifndef MQTT_SOCKET_TIMEOUT
|
||||
#define MQTT_SOCKET_TIMEOUT 15
|
||||
#endif
|
||||
|
||||
// MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client
|
||||
// in each write call. Needed for the Arduino Wifi Shield. Leave undefined to
|
||||
// pass the entire MQTT packet in each write call.
|
||||
//#define MQTT_MAX_TRANSFER_SIZE 80
|
||||
|
||||
// Possible values for client.state()
|
||||
#define MQTT_CONNECTION_TIMEOUT -4
|
||||
#define MQTT_CONNECTION_LOST -3
|
||||
#define MQTT_CONNECT_FAILED -2
|
||||
#define MQTT_DISCONNECTED -1
|
||||
#define MQTT_CONNECTED 0
|
||||
#define MQTT_CONNECT_BAD_PROTOCOL 1
|
||||
#define MQTT_CONNECT_BAD_CLIENT_ID 2
|
||||
#define MQTT_CONNECT_UNAVAILABLE 3
|
||||
#define MQTT_CONNECT_BAD_CREDENTIALS 4
|
||||
#define MQTT_CONNECT_UNAUTHORIZED 5
|
||||
|
||||
#define MQTTCONNECT 1 << 4 // Client request to connect to Server
|
||||
#define MQTTCONNACK 2 << 4 // Connect Acknowledgment
|
||||
#define MQTTPUBLISH 3 << 4 // Publish message
|
||||
#define MQTTPUBACK 4 << 4 // Publish Acknowledgment
|
||||
#define MQTTPUBREC 5 << 4 // Publish Received (assured delivery part 1)
|
||||
#define MQTTPUBREL 6 << 4 // Publish Release (assured delivery part 2)
|
||||
#define MQTTPUBCOMP 7 << 4 // Publish Complete (assured delivery part 3)
|
||||
#define MQTTSUBSCRIBE 8 << 4 // Client Subscribe request
|
||||
#define MQTTSUBACK 9 << 4 // Subscribe Acknowledgment
|
||||
#define MQTTUNSUBSCRIBE 10 << 4 // Client Unsubscribe request
|
||||
#define MQTTUNSUBACK 11 << 4 // Unsubscribe Acknowledgment
|
||||
#define MQTTPINGREQ 12 << 4 // PING Request
|
||||
#define MQTTPINGRESP 13 << 4 // PING Response
|
||||
#define MQTTDISCONNECT 14 << 4 // Client is Disconnecting
|
||||
#define MQTTReserved 15 << 4 // Reserved
|
||||
|
||||
#define MQTTQOS0 (0 << 1)
|
||||
#define MQTTQOS1 (1 << 1)
|
||||
#define MQTTQOS2 (2 << 1)
|
||||
|
||||
// Maximum size of fixed header and variable length size header
|
||||
#define MQTT_MAX_HEADER_SIZE 5
|
||||
|
||||
#if defined(ESP8266) || defined(ESP32)
|
||||
#include <functional>
|
||||
#define MQTT_CALLBACK_SIGNATURE std::function<void(char*, uint8_t*, unsigned int)> callback
|
||||
#else
|
||||
#define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int)
|
||||
#endif
|
||||
|
||||
#define CHECK_STRING_LENGTH(l,s) if (l+2+strlen(s) > MQTT_MAX_PACKET_SIZE) {_client->stop();return false;}
|
||||
|
||||
/** PubSubClient class */
|
||||
class PubSubClient : public Print
|
||||
{
|
||||
private:
|
||||
Client* _client;
|
||||
uint8_t buffer[MQTT_MAX_PACKET_SIZE];
|
||||
uint16_t nextMsgId;
|
||||
unsigned long lastOutActivity;
|
||||
unsigned long lastInActivity;
|
||||
bool pingOutstanding;
|
||||
MQTT_CALLBACK_SIGNATURE;
|
||||
uint16_t readPacket(uint8_t*);
|
||||
bool readByte(uint8_t * result);
|
||||
bool readByte(uint8_t * result, uint16_t * index);
|
||||
bool write(uint8_t header, uint8_t* buf, uint16_t length);
|
||||
uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos);
|
||||
// Build up the header ready to send
|
||||
// Returns the size of the header
|
||||
// Note: the header is built at the end of the first MQTT_MAX_HEADER_SIZE bytes, so will start
|
||||
// (MQTT_MAX_HEADER_SIZE - <returned size>) bytes into the buffer
|
||||
size_t buildHeader(uint8_t header, uint8_t* buf, uint16_t length);
|
||||
IPAddress ip;
|
||||
const char* domain;
|
||||
uint16_t port;
|
||||
Stream* stream;
|
||||
int _state;
|
||||
public:
|
||||
PubSubClient(); //!< PubSubClient
|
||||
explicit PubSubClient(Client& client); //!< PubSubClient
|
||||
PubSubClient(IPAddress, uint16_t, Client& client); //!< PubSubClient
|
||||
PubSubClient(IPAddress, uint16_t, Client& client, Stream&); //!< PubSubClient
|
||||
PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client); //!< PubSubClient
|
||||
PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client,
|
||||
Stream&); //!< PubSubClient
|
||||
PubSubClient(uint8_t *, uint16_t, Client& client); //!< PubSubClient
|
||||
PubSubClient(uint8_t *, uint16_t, Client& client, Stream&); //!< PubSubClient
|
||||
PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client); //!< PubSubClient
|
||||
PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client,
|
||||
Stream&); //!< PubSubClient
|
||||
PubSubClient(const char*, uint16_t, Client& client); //!< PubSubClient
|
||||
PubSubClient(const char*, uint16_t, Client& client, Stream&); //!< PubSubClient
|
||||
PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client); //!< PubSubClient
|
||||
PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client,
|
||||
Stream&); //!< PubSubClient
|
||||
|
||||
PubSubClient& setServer(IPAddress ip, uint16_t port); //!< setServer
|
||||
PubSubClient& setServer(uint8_t * ip, uint16_t port); //!< setServer
|
||||
PubSubClient& setServer(const char * domain, uint16_t port); //!< setServer
|
||||
PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE); //!< setCallback
|
||||
PubSubClient& setClient(Client& client); //!< setClient
|
||||
PubSubClient& setStream(Stream& stream); //!< setStream
|
||||
|
||||
bool connect(const char* id); //!< connect
|
||||
bool connect(const char* id, const char* user, const char* pass); //!< connect
|
||||
bool connect(const char* id, const char* willTopic, uint8_t willQos, bool willRetain,
|
||||
const char* willMessage); //!< connect
|
||||
bool connect(const char* id, const char* user, const char* pass, const char* willTopic,
|
||||
uint8_t willQos, bool willRetain, const char* willMessage); //!< connect
|
||||
bool connect(const char* id, const char* user, const char* pass, const char* willTopic,
|
||||
uint8_t willQos, bool willRetain, const char* willMessage, bool cleanSession); //!< connect
|
||||
void disconnect(); //!< disconnect
|
||||
bool publish(const char* topic, const char* payload); //!< publish
|
||||
bool publish(const char* topic, const char* payload, bool retained); //!< publish
|
||||
bool publish(const char* topic, const uint8_t * payload, unsigned int plength); //!< publish
|
||||
bool publish(const char* topic, const uint8_t * payload, unsigned int plength,
|
||||
bool retained); //!< publish
|
||||
bool publish_P(const char* topic, const char* payload, bool retained); //!< publish
|
||||
bool publish_P(const char* topic, const uint8_t * payload, unsigned int plength,
|
||||
bool retained); //!< publish
|
||||
// Start to publish a message.
|
||||
// This API:
|
||||
// beginPublish(...)
|
||||
// one or more calls to write(...)
|
||||
// endPublish()
|
||||
// Allows for arbitrarily large payloads to be sent without them having to be copied into
|
||||
// a new buffer and held in memory at one time
|
||||
// Returns 1 if the message was started successfully, 0 if there was an error
|
||||
bool beginPublish(const char* topic, unsigned int plength, bool retained); //!< beginPublish
|
||||
// Finish off this publish message (started with beginPublish)
|
||||
// Returns 1 if the packet was sent successfully, 0 if there was an error
|
||||
int endPublish(); //!< endPublish
|
||||
// Write a single byte of payload (only to be used with beginPublish/endPublish)
|
||||
virtual size_t write(uint8_t); //!< write
|
||||
// Write size bytes from buffer into the payload (only to be used with beginPublish/endPublish)
|
||||
// Returns the number of bytes written
|
||||
virtual size_t write(const uint8_t *buffer, size_t size); //!< write
|
||||
bool subscribe(const char* topic); //!< subscribe
|
||||
bool subscribe(const char* topic, uint8_t qos); //!< subscribe
|
||||
bool unsubscribe(const char* topic); //!< unsubscribe
|
||||
bool loop(); //!< loop
|
||||
bool connected(); //!< connected
|
||||
int state(); //!< state
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
48
lib/MySensors/drivers/PubSubClient/README.md
Normal file
48
lib/MySensors/drivers/PubSubClient/README.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# Arduino Client for MQTT
|
||||
|
||||
This library provides a client for doing simple publish/subscribe messaging with
|
||||
a server that supports MQTT.
|
||||
|
||||
## Examples
|
||||
|
||||
The library comes with a number of example sketches. See File > Examples > PubSubClient
|
||||
within the Arduino application.
|
||||
|
||||
Full API documentation is available here: https://pubsubclient.knolleary.net
|
||||
|
||||
## Limitations
|
||||
|
||||
- It can only publish QoS 0 messages. It can subscribe at QoS 0 or QoS 1.
|
||||
- The maximum message size, including header, is **128 bytes** by default. This
|
||||
is configurable via `MQTT_MAX_PACKET_SIZE` in `PubSubClient.h`.
|
||||
- The keepalive interval is set to 15 seconds by default. This is configurable
|
||||
via `MQTT_KEEPALIVE` in `PubSubClient.h`.
|
||||
- The client uses MQTT 3.1.1 by default. It can be changed to use MQTT 3.1 by
|
||||
changing value of `MQTT_VERSION` in `PubSubClient.h`.
|
||||
|
||||
|
||||
## Compatible Hardware
|
||||
|
||||
The library uses the Arduino Ethernet Client api for interacting with the
|
||||
underlying network hardware. This means it Just Works with a growing number of
|
||||
boards and shields, including:
|
||||
|
||||
- Arduino Ethernet
|
||||
- Arduino Ethernet Shield
|
||||
- Arduino YUN – use the included `YunClient` in place of `EthernetClient`, and
|
||||
be sure to do a `Bridge.begin()` first
|
||||
- Arduino WiFi Shield - if you want to send packets > 90 bytes with this shield,
|
||||
enable the `MQTT_MAX_TRANSFER_SIZE` define in `PubSubClient.h`.
|
||||
- Sparkfun WiFly Shield – [library](https://github.com/dpslwk/WiFly)
|
||||
- TI CC3000 WiFi - [library](https://github.com/sparkfun/SFE_CC3000_Library)
|
||||
- Intel Galileo/Edison
|
||||
- ESP8266
|
||||
- ESP32
|
||||
|
||||
The library cannot currently be used with hardware based on the ENC28J60 chip –
|
||||
such as the Nanode or the Nuelectronics Ethernet Shield. For those, there is an
|
||||
[alternative library](https://github.com/njh/NanodeMQTT) available.
|
||||
|
||||
## License
|
||||
|
||||
This code is released under the MIT License.
|
||||
674
lib/MySensors/drivers/SPIFlash/License.txt
Normal file
674
lib/MySensors/drivers/SPIFlash/License.txt
Normal file
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
32
lib/MySensors/drivers/SPIFlash/README.md
Normal file
32
lib/MySensors/drivers/SPIFlash/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
SPIFlash
|
||||
========
|
||||
[](https://travis-ci.org/LowPowerLab/SPIFlash)
|
||||
[](https://github.com/LowPowerLab/SPIFlash)
|
||||
[](https://github.com/LowPowerLab/SPIFlash/issues)
|
||||
[](https://github.com/LowPowerLab/SPIFlash/pulls)
|
||||
[](https://github.com/LowPowerLab/SPIFlash/blob/master/LICENSE.txt)
|
||||
|
||||
Arduino/Moteino library for read/write access to SPI flash memory chips.
|
||||
This works with 256byte/page SPI flash memory such as the [4MBIT W25X40CLSNIG](https://lowpowerlab.com/shop/product/72) used on [Moteino](https://www.moteino.com) for data storage and wireless programming.
|
||||
<br/>
|
||||
For instance a 4MBit (512Kbyte) flash chip will have 2048 pages: 256*2048 = 524288 bytes (512Kbytes).
|
||||
<br/>Minimal modifications should allow chips that have different page size to work.
|
||||
<br/>DEPENDS ON: Arduino native *SPI library*.
|
||||
<br/>
|
||||
This library was primarily developed to enable **safe** wireless programming on Moteino nodes and Moteino based applications such as the [SwitchMote](https://lowpowerlab.com/guide/switchmote/). This has been documented at [lowpowerlab](https://lowpowerlab.com/guide/moteino/wireless-programming/). [Dualoptiboot](https://github.com/LowPowerLab/DualOptiboot) (all AVR based Moteinos come with it) and [RFM69_OTA WirelessProgramming library](https://github.com/LowPowerLab/RFM69) are required to be able to wirelessly re-flash a remote Moteino.
|
||||
|
||||
### Installation
|
||||
Copy the content of this library in the "Arduino/libraries/SPIFlash" folder.
|
||||
<br />
|
||||
To find your Arduino folder go to File>Preferences in the Arduino IDE.
|
||||
<br/>
|
||||
See [this tutorial](https://www.arduino.cc/en/Guide/Libraries) on installing Arduino libraries.
|
||||
|
||||
### License
|
||||
Copyright (c) 2013-2018 by Felix Rusu <felix@lowpowerlab.com>
|
||||
<br/><br/>
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
<br/><br/>
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
<br/><br/>
|
||||
You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
375
lib/MySensors/drivers/SPIFlash/SPIFlash.cpp
Normal file
375
lib/MySensors/drivers/SPIFlash/SPIFlash.cpp
Normal file
@@ -0,0 +1,375 @@
|
||||
// Copyright (c) 2013-2015 by Felix Rusu, LowPowerLab.com
|
||||
// SPI Flash memory library for arduino/moteino.
|
||||
// This works with 256byte/page SPI flash memory
|
||||
// For instance a 4MBit (512Kbyte) flash chip will have 2048 pages: 256*2048 = 524288 bytes (512Kbytes)
|
||||
// Minimal modifications should allow chips that have different page size but modifications
|
||||
// DEPENDS ON: Arduino SPI library
|
||||
// > Updated Jan. 5, 2015, TomWS1, modified writeBytes to allow blocks > 256 bytes and handle page misalignment.
|
||||
// > Updated Feb. 26, 2015 TomWS1, added support for SPI Transactions (Arduino 1.5.8 and above)
|
||||
// > Selective merge by Felix after testing in IDE 1.0.6, 1.6.4
|
||||
// > Updated May 19, 2016 D-H-R, added support for SST25/Microchip Flash which does not support Page programming with OPCode 0x02,
|
||||
// > use define MY_SPIFLASH_SST25TYPE for SST25 Type Flash Memory
|
||||
// > Updated Sep 07, 2018 tekka, sync with https://github.com/LowPowerLab/SPIFlash
|
||||
// **********************************************************************************
|
||||
// License
|
||||
// **********************************************************************************
|
||||
// This program is free software; you can redistribute it
|
||||
// and/or modify it under the terms of the GNU General
|
||||
// Public License as published by the Free Software
|
||||
// Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without even the
|
||||
// implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
// PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General
|
||||
// Public License along with this program.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Licence can be viewed at
|
||||
// http://www.gnu.org/licenses/gpl-3.0.txt
|
||||
//
|
||||
// Please maintain this license information along with authorship
|
||||
// and copyright notices in any redistribution of this code
|
||||
|
||||
#include "SPIFlash.h"
|
||||
|
||||
uint8_t SPIFlash::UNIQUEID[8];
|
||||
|
||||
/// IMPORTANT: NAND FLASH memory requires erase before write, because
|
||||
/// it can only transition from 1s to 0s and only the erase command can reset all 0s to 1s
|
||||
/// See http://en.wikipedia.org/wiki/Flash_memory
|
||||
/// The smallest range that can be erased is a sector (4K, 32K, 64K); there is also a chip erase command
|
||||
|
||||
/// Constructor. JedecID is optional but recommended, since this will ensure that the device is present and has a valid response
|
||||
/// get this from the datasheet of your flash chip
|
||||
/// Example for Atmel-Adesto 4Mbit AT25DF041A: 0x1F44 (page 27: http://www.adestotech.com/sites/default/files/datasheets/doc3668.pdf)
|
||||
/// Example for Winbond 4Mbit W25X40CL: 0xEF30 (page 14: http://www.winbond.com/NR/rdonlyres/6E25084C-0BFE-4B25-903D-AE10221A0929/0/W25X40CL.pdf)
|
||||
// Suppress uninitialized member variable in constructor because some memory can be saved with
|
||||
// on-demand initialization of these members
|
||||
// cppcheck-suppress uninitMemberVar
|
||||
SPIFlash::SPIFlash(uint8_t slaveSelectPin, uint16_t jedecID)
|
||||
{
|
||||
_slaveSelectPin = slaveSelectPin;
|
||||
_jedecID = jedecID;
|
||||
}
|
||||
|
||||
/// Select the flash chip
|
||||
void SPIFlash::select()
|
||||
{
|
||||
//save current SPI settings
|
||||
#ifndef SPI_HAS_TRANSACTION
|
||||
noInterrupts();
|
||||
#endif
|
||||
#if defined(SPCR) && defined(SPSR)
|
||||
_SPCR = SPCR;
|
||||
_SPSR = SPSR;
|
||||
#endif
|
||||
|
||||
#ifdef SPI_HAS_TRANSACTION
|
||||
SPI.beginTransaction(_settings);
|
||||
#else
|
||||
// set FLASH SPI settings
|
||||
SPI.setDataMode(SPI_MODE0);
|
||||
SPI.setBitOrder(MSBFIRST);
|
||||
SPI.setClockDivider(
|
||||
SPI_CLOCK_DIV4); //decided to slow down from DIV2 after SPI stalling in some instances, especially visible on mega1284p when RFM69 and FLASH chip both present
|
||||
#endif
|
||||
hwDigitalWrite(_slaveSelectPin, LOW);
|
||||
}
|
||||
|
||||
/// UNselect the flash chip
|
||||
void SPIFlash::unselect()
|
||||
{
|
||||
hwDigitalWrite(_slaveSelectPin, HIGH);
|
||||
//restore SPI settings to what they were before talking to the FLASH chip
|
||||
#ifdef SPI_HAS_TRANSACTION
|
||||
SPI.endTransaction();
|
||||
#else
|
||||
interrupts();
|
||||
#endif
|
||||
#if defined(SPCR) && defined(SPSR)
|
||||
SPCR = _SPCR;
|
||||
SPSR = _SPSR;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// setup SPI, read device ID etc...
|
||||
bool SPIFlash::initialize()
|
||||
{
|
||||
#if defined(SPCR) && defined(SPSR)
|
||||
_SPCR = SPCR;
|
||||
_SPSR = SPSR;
|
||||
#endif
|
||||
hwPinMode(_slaveSelectPin, OUTPUT);
|
||||
SPI.begin(); // see https://github.com/LowPowerLab/SPIFlash/issues/20
|
||||
#ifdef SPI_HAS_TRANSACTION
|
||||
_settings = SPISettings(4000000, MSBFIRST, SPI_MODE0);
|
||||
#endif
|
||||
|
||||
unselect();
|
||||
wakeup();
|
||||
|
||||
if (_jedecID == 0 || readDeviceId() == _jedecID) {
|
||||
command(SPIFLASH_STATUSWRITE, true); // Write Status Register
|
||||
SPI.transfer(0); // Global Unprotect
|
||||
unselect();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Get the manufacturer and device ID bytes (as a short word)
|
||||
uint16_t SPIFlash::readDeviceId()
|
||||
{
|
||||
#if defined(__AVR_ATmega32U4__) // Arduino Leonardo, MoteinoLeo
|
||||
command(SPIFLASH_IDREAD); // Read JEDEC ID
|
||||
#else
|
||||
select();
|
||||
SPI.transfer(SPIFLASH_IDREAD);
|
||||
#endif
|
||||
uint16_t jedecid = SPI.transfer(0) << 8;
|
||||
jedecid |= SPI.transfer(0);
|
||||
unselect();
|
||||
return jedecid;
|
||||
}
|
||||
|
||||
/// Get the 64 bit unique identifier, stores it in UNIQUEID[8]. Only needs to be called once, ie after initialize
|
||||
/// Returns the byte pointer to the UNIQUEID byte array
|
||||
/// Read UNIQUEID like this:
|
||||
/// flash.readUniqueId(); for (uint8_t i=0;i<8;i++) { Serial.print(flash.UNIQUEID[i], HEX); Serial.print(' '); }
|
||||
/// or like this:
|
||||
/// flash.readUniqueId(); uint8_t* MAC = flash.readUniqueId(); for (uint8_t i=0;i<8;i++) { Serial.print(MAC[i], HEX); Serial.print(' '); }
|
||||
uint8_t* SPIFlash::readUniqueId()
|
||||
{
|
||||
command(SPIFLASH_MACREAD);
|
||||
SPI.transfer(0);
|
||||
SPI.transfer(0);
|
||||
SPI.transfer(0);
|
||||
SPI.transfer(0);
|
||||
for (uint8_t i=0; i<8; i++) {
|
||||
UNIQUEID[i] = SPI.transfer(0);
|
||||
}
|
||||
unselect();
|
||||
return UNIQUEID;
|
||||
}
|
||||
|
||||
/// read 1 byte from flash memory
|
||||
uint8_t SPIFlash::readByte(uint32_t addr)
|
||||
{
|
||||
command(SPIFLASH_ARRAYREADLOWFREQ);
|
||||
SPI.transfer(addr >> 16);
|
||||
SPI.transfer(addr >> 8);
|
||||
SPI.transfer(addr);
|
||||
uint8_t result = SPI.transfer(0);
|
||||
unselect();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// read unlimited # of bytes
|
||||
void SPIFlash::readBytes(uint32_t addr, void* buf, uint16_t len)
|
||||
{
|
||||
command(SPIFLASH_ARRAYREAD);
|
||||
SPI.transfer(addr >> 16);
|
||||
SPI.transfer(addr >> 8);
|
||||
SPI.transfer(addr);
|
||||
SPI.transfer(0); //"dont care"
|
||||
for (uint16_t i = 0; i < len; ++i) {
|
||||
((uint8_t*) buf)[i] = SPI.transfer(0);
|
||||
}
|
||||
unselect();
|
||||
}
|
||||
|
||||
/// Send a command to the flash chip, pass TRUE for isWrite when its a write command
|
||||
void SPIFlash::command(uint8_t cmd, bool isWrite)
|
||||
{
|
||||
#if defined(__AVR_ATmega32U4__) // Arduino Leonardo, MoteinoLeo
|
||||
DDRB |= B00000001; // Make sure the SS pin (PB0 - used by RFM12B on MoteinoLeo R1) is set as output HIGH!
|
||||
PORTB |= B00000001;
|
||||
#endif
|
||||
if (isWrite) {
|
||||
command(SPIFLASH_WRITEENABLE); // Write Enable
|
||||
unselect();
|
||||
}
|
||||
//wait for any write/erase to complete
|
||||
// a time limit cannot really be added here without it being a very large safe limit
|
||||
// that is because some chips can take several seconds to carry out a chip erase or other similar multi block or entire-chip operations
|
||||
// a recommended alternative to such situations where chip can be or not be present is to add a 10k or similar weak pulldown on the
|
||||
// open drain MISO input which can read noise/static and hence return a non 0 status byte, causing the while() to hang when a flash chip is not present
|
||||
if (cmd != SPIFLASH_WAKE) while(busy());
|
||||
select();
|
||||
SPI.transfer(cmd);
|
||||
}
|
||||
|
||||
/// check if the chip is busy erasing/writing
|
||||
bool SPIFlash::busy()
|
||||
{
|
||||
/*
|
||||
select();
|
||||
SPI.transfer(SPIFLASH_STATUSREAD);
|
||||
uint8_t status = SPI.transfer(0);
|
||||
unselect();
|
||||
return status & 1;
|
||||
*/
|
||||
return readStatus() & 1;
|
||||
}
|
||||
|
||||
/// return the STATUS register
|
||||
uint8_t SPIFlash::readStatus()
|
||||
{
|
||||
select();
|
||||
SPI.transfer(SPIFLASH_STATUSREAD);
|
||||
uint8_t status = SPI.transfer(0);
|
||||
unselect();
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/// Write 1 byte to flash memory
|
||||
/// WARNING: you can only write to previously erased memory locations (see datasheet)
|
||||
/// use the block erase commands to first clear memory (write 0xFFs)
|
||||
void SPIFlash::writeByte(uint32_t addr, uint8_t byt)
|
||||
{
|
||||
command(SPIFLASH_BYTEPAGEPROGRAM, true); // Byte/Page Program
|
||||
SPI.transfer(addr >> 16);
|
||||
SPI.transfer(addr >> 8);
|
||||
SPI.transfer(addr);
|
||||
SPI.transfer(byt);
|
||||
unselect();
|
||||
}
|
||||
|
||||
/// write multiple bytes to flash memory (up to 64K)
|
||||
/// WARNING: you can only write to previously erased memory locations (see datasheet)
|
||||
/// use the block erase commands to first clear memory (write 0xFFs)
|
||||
/// This version handles both page alignment and data blocks larger than 256 bytes.
|
||||
/// See documentation of #MY_SPIFLASH_SST25TYPE define for more information
|
||||
void SPIFlash::writeBytes(uint32_t addr, const void* buf, uint16_t len)
|
||||
{
|
||||
#ifdef MY_SPIFLASH_SST25TYPE
|
||||
//SST25 Type of Flash does not support Page Programming but AAI Word Programming
|
||||
uint16_t i=0;
|
||||
uint8_t oddAdr=0;
|
||||
|
||||
command(SPIFLASH_AAIWORDPROGRAM, true); // Byte/Page Program
|
||||
SPI.transfer(addr >> 16);
|
||||
SPI.transfer(addr >> 8);
|
||||
SPI.transfer(addr);
|
||||
|
||||
if (addr%2) {
|
||||
//start address is not even, i.e. first byte of word must be 0xff
|
||||
SPI.transfer(0xff);
|
||||
SPI.transfer(((uint8_t*) buf)[0]);
|
||||
unselect();
|
||||
oddAdr=1; //following addresses must all be shifted one off
|
||||
len--;
|
||||
if (len > 0) {
|
||||
command(SPIFLASH_AAIWORDPROGRAM); //If for loop will run issue Wordprogram command
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i<(len/2); i++) {
|
||||
//AAI command must be set before every new word
|
||||
if (i>0) {
|
||||
command(SPIFLASH_AAIWORDPROGRAM); //Wordprogram command for first write has been issued before
|
||||
}
|
||||
SPI.transfer(((uint8_t*) buf)[i*2+oddAdr]);
|
||||
SPI.transfer(((uint8_t*) buf)[i*2+1+oddAdr]);
|
||||
unselect();
|
||||
}
|
||||
|
||||
if (len-i*2 == 1) {
|
||||
//There is one byte (i.e. half word) left. This happens if len was odd or (len was even and addr odd)
|
||||
if (i>0) {
|
||||
command(SPIFLASH_AAIWORDPROGRAM); //if for loop had not run wordprogram command from before is still valid
|
||||
}
|
||||
SPI.transfer(((uint8_t*) buf)[i*2+oddAdr]);
|
||||
SPI.transfer(0xff);
|
||||
unselect();
|
||||
}
|
||||
|
||||
command(SPIFLASH_WRITEDISABLE); //end AAI programming
|
||||
unselect();
|
||||
#else
|
||||
uint16_t maxBytes = 256-(addr%256); // force the first set of bytes to stay within the first page
|
||||
uint16_t offset = 0;
|
||||
while (len>0) {
|
||||
uint16_t n = (len<=maxBytes) ? len : maxBytes;
|
||||
command(SPIFLASH_BYTEPAGEPROGRAM, true); // Byte/Page Program
|
||||
SPI.transfer(addr >> 16);
|
||||
SPI.transfer(addr >> 8);
|
||||
SPI.transfer(addr);
|
||||
|
||||
for (uint16_t i = 0; i < n; i++) {
|
||||
SPI.transfer(((uint8_t*) buf)[offset + i]);
|
||||
}
|
||||
unselect();
|
||||
|
||||
addr+=n; // adjust the addresses and remaining bytes by what we've just transferred.
|
||||
offset +=n;
|
||||
len -= n;
|
||||
maxBytes = 256; // now we can do up to 256 bytes per loop
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// erase entire flash memory array
|
||||
/// may take several seconds depending on size, but is non blocking
|
||||
/// so you may wait for this to complete using busy() or continue doing
|
||||
/// other things and later check if the chip is done with busy()
|
||||
/// note that any command will first wait for chip to become available using busy()
|
||||
/// so no need to do that twice
|
||||
void SPIFlash::chipErase()
|
||||
{
|
||||
command(SPIFLASH_CHIPERASE, true);
|
||||
unselect();
|
||||
}
|
||||
|
||||
/// erase a 4Kbyte block
|
||||
void SPIFlash::blockErase4K(uint32_t addr)
|
||||
{
|
||||
command(SPIFLASH_BLOCKERASE_4K, true); // Block Erase
|
||||
SPI.transfer(addr >> 16);
|
||||
SPI.transfer(addr >> 8);
|
||||
SPI.transfer(addr);
|
||||
unselect();
|
||||
}
|
||||
|
||||
/// erase a 32Kbyte block
|
||||
void SPIFlash::blockErase32K(uint32_t addr)
|
||||
{
|
||||
command(SPIFLASH_BLOCKERASE_32K, true); // Block Erase
|
||||
SPI.transfer(addr >> 16);
|
||||
SPI.transfer(addr >> 8);
|
||||
SPI.transfer(addr);
|
||||
unselect();
|
||||
}
|
||||
/// erase a 64Kbyte block
|
||||
void SPIFlash::blockErase64K(uint32_t addr)
|
||||
{
|
||||
command(SPIFLASH_BLOCKERASE_64K, true); // Block Erase
|
||||
SPI.transfer(addr >> 16);
|
||||
SPI.transfer(addr >> 8);
|
||||
SPI.transfer(addr);
|
||||
unselect();
|
||||
}
|
||||
|
||||
void SPIFlash::sleep()
|
||||
{
|
||||
command(SPIFLASH_SLEEP);
|
||||
unselect();
|
||||
}
|
||||
|
||||
void SPIFlash::wakeup()
|
||||
{
|
||||
command(SPIFLASH_WAKE);
|
||||
unselect();
|
||||
}
|
||||
|
||||
/// cleanup
|
||||
void SPIFlash::end()
|
||||
{
|
||||
SPI.end();
|
||||
}
|
||||
198
lib/MySensors/drivers/SPIFlash/SPIFlash.h
Normal file
198
lib/MySensors/drivers/SPIFlash/SPIFlash.h
Normal file
@@ -0,0 +1,198 @@
|
||||
// Copyright (c) 2013-2015 by Felix Rusu, LowPowerLab.com
|
||||
// SPI Flash memory library for arduino/moteino.
|
||||
// This works with 256byte/page SPI flash memory
|
||||
// For instance a 4MBit (512Kbyte) flash chip will have 2048 pages: 256*2048 = 524288 bytes (512Kbytes)
|
||||
// Minimal modifications should allow chips that have different page size but modifications
|
||||
// DEPENDS ON: Arduino SPI library
|
||||
// > Updated Jan. 5, 2015, TomWS1, modified writeBytes to allow blocks > 256 bytes and handle page misalignment.
|
||||
// > Updated Feb. 26, 2015 TomWS1, added support for SPI Transactions (Arduino 1.5.8 and above)
|
||||
// > Selective merge by Felix after testing in IDE 1.0.6, 1.6.4
|
||||
// > Updated May 19, 2016 D-H-R, added support for SST25/Microchip Flash which does not support Page programming with OPCode 0x02,
|
||||
// > use define MY_SPIFLASH_SST25TYPE for SST25 Type Flash Memory. Added / changed comments to better suit doxygen
|
||||
// > Updated Sep 07, 2018 tekka, sync with https://github.com/LowPowerLab/SPIFlash
|
||||
// **********************************************************************************
|
||||
// License
|
||||
// **********************************************************************************
|
||||
// This program is free software; you can redistribute it
|
||||
// and/or modify it under the terms of the GNU General
|
||||
// Public License as published by the Free Software
|
||||
// Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without even the
|
||||
// implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
// PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General
|
||||
// Public License along with this program.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Licence can be viewed at
|
||||
// http://www.gnu.org/licenses/gpl-3.0.txt
|
||||
//
|
||||
// Please maintain this license information along with authorship
|
||||
// and copyright notices in any redistribution of this code
|
||||
|
||||
///
|
||||
/// @file SPIFlash.h
|
||||
///
|
||||
/// @brief SPIFlash provides access to a SPI Flash IC for OTA update or storing data
|
||||
///
|
||||
/// IMPORTANT: NAND FLASH memory requires erase before write, because
|
||||
/// it can only transition from 1s to 0s and only the erase command can reset all 0s to 1s
|
||||
/// See http://en.wikipedia.org/wiki/Flash_memory
|
||||
/// The smallest range that can be erased is a sector (4K, 32K, 64K); there is also a chip erase command
|
||||
///
|
||||
/// Standard SPI flash commands <BR>
|
||||
/// Assuming the WP pin is pulled up (to disable hardware write protection).<BR>
|
||||
/// To use any write commands the WEL bit in the status register must be set to 1.<BR>
|
||||
/// This is accomplished by sending a 0x06 command before any such write/erase command.<BR>
|
||||
/// The WEL bit in the status register resets to the logical ?0? state after a device power-up or reset.
|
||||
/// In addition, the WEL bit will be reset to the logical ?0? state automatically under the following conditions:<BR>
|
||||
/// - Write Disable operation completes successfully<BR>
|
||||
/// - Write Status Register operation completes successfully or aborts<BR>
|
||||
/// - Protect Sector operation completes successfully or aborts<BR>
|
||||
/// - Unprotect Sector operation completes successfully or aborts<BR>
|
||||
/// - Byte/Page Program operation completes successfully or aborts<BR>
|
||||
/// - Sequential Program Mode reaches highest unprotected memory location<BR>
|
||||
/// - Sequential Program Mode reaches the end of the memory array<BR>
|
||||
/// - Sequential Program Mode aborts<BR>
|
||||
/// - Block Erase operation completes successfully or aborts<BR>
|
||||
/// - Chip Erase operation completes successfully or aborts<BR>
|
||||
/// - Hold condition aborts
|
||||
///
|
||||
|
||||
#ifndef _SPIFLASH_H_
|
||||
#define _SPIFLASH_H_
|
||||
|
||||
#if ARDUINO >= 100
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <wiring.h>
|
||||
#include "pins_arduino.h"
|
||||
#endif
|
||||
|
||||
#include <SPI.h>
|
||||
|
||||
#ifndef SPIFLASH_WRITEENABLE
|
||||
#define SPIFLASH_WRITEENABLE 0x06 //!< write enable
|
||||
#endif
|
||||
|
||||
#ifndef SPIFLASH_WRITEDISABLE
|
||||
#define SPIFLASH_WRITEDISABLE 0x04 //!< write disable
|
||||
#endif
|
||||
|
||||
#ifndef SPIFLASH_BLOCKERASE_4K
|
||||
#define SPIFLASH_BLOCKERASE_4K 0x20 //!< erase one 4K block of flash memory
|
||||
#endif
|
||||
|
||||
#ifndef SPIFLASH_BLOCKERASE_32K
|
||||
#define SPIFLASH_BLOCKERASE_32K 0x52 //!< erase one 32K block of flash memory
|
||||
#endif
|
||||
|
||||
#ifndef SPIFLASH_BLOCKERASE_64K
|
||||
#define SPIFLASH_BLOCKERASE_64K 0xD8 //!< erase one 64K block of flash memory
|
||||
#endif
|
||||
|
||||
#ifndef SPIFLASH_CHIPERASE
|
||||
#define SPIFLASH_CHIPERASE 0x60 //!< @brief chip erase (may take several seconds depending on size)
|
||||
#endif
|
||||
//!< Chip is erased but not actually waited for completion (instead need to check the status register BUSY bit)
|
||||
|
||||
#ifndef SPIFLASH_STATUSREAD
|
||||
#define SPIFLASH_STATUSREAD 0x05 //!< read status register
|
||||
#endif
|
||||
|
||||
#ifndef SPIFLASH_STATUSWRITE
|
||||
#define SPIFLASH_STATUSWRITE 0x01 //!< write status register
|
||||
#endif
|
||||
|
||||
#ifndef SPIFLASH_ARRAYREAD
|
||||
#define SPIFLASH_ARRAYREAD 0x0B //!< read array (fast, need to add 1 dummy byte after 3 address bytes)
|
||||
#endif
|
||||
|
||||
#ifndef SPIFLASH_ARRAYREADLOWFREQ
|
||||
#define SPIFLASH_ARRAYREADLOWFREQ 0x03 //!< read array (low frequency)
|
||||
#endif
|
||||
|
||||
#ifndef SPIFLASH_SLEEP
|
||||
#define SPIFLASH_SLEEP 0xB9 //!< deep power down
|
||||
#endif
|
||||
|
||||
#ifndef SPIFLASH_WAKE
|
||||
#define SPIFLASH_WAKE 0xAB //!< deep power wake up
|
||||
#endif
|
||||
|
||||
#ifndef SPIFLASH_BYTEPAGEPROGRAM
|
||||
#define SPIFLASH_BYTEPAGEPROGRAM 0x02 //!< write (1 to 256bytes). Writing more than one Byte is not supported on all devices (e.g. SST25 Series)
|
||||
#endif
|
||||
|
||||
#ifndef SPIFLASH_AAIWORDPROGRAM
|
||||
#define SPIFLASH_AAIWORDPROGRAM 0xAD //!< @brief Auto Address Increment Programming on Microchip SST Family Devices which do not support page program. <BR>
|
||||
#endif
|
||||
//!< Use define #MY_SPIFLASH_SST25TYPE to use AAI prog instead of Bytepageprogram which does not work on SST Family Chips
|
||||
//!< tested with SST25PF020B80 http://ww1.microchip.com/downloads/en/DeviceDoc/20005135B.pdf
|
||||
|
||||
#ifndef SPIFLASH_IDREAD
|
||||
#define SPIFLASH_IDREAD 0x9F //!< @brief read JEDEC manufacturer and device ID (2 bytes, specific bytes for each manufacturer and device)
|
||||
#endif
|
||||
//!< Example for Atmel-Adesto 4Mbit AT25DF041A: 0x1F44 (page 27: http://www.adestotech.com/sites/default/files/datasheets/doc3668.pdf)
|
||||
//!< Example for Winbond 4Mbit W25X40CL: 0xEF30 (page 14: http://www.winbond.com/NR/rdonlyres/6E25084C-0BFE-4B25-903D-AE10221A0929/0/W25X40CL.pdf)
|
||||
|
||||
#ifndef SPIFLASH_MACREAD
|
||||
#define SPIFLASH_MACREAD 0x4B //!< read unique ID number (MAC)
|
||||
#endif
|
||||
|
||||
///
|
||||
/// @def MY_SPIFLASH_SST25TYPE
|
||||
/// @brief If set AAI Word Programming is used to support SST25 Family SPI Flash.
|
||||
///
|
||||
/// SST25 Family Flash does not support programming multiple Bytes with opcode 0x02 #SPIFLASH_BYTEPAGEPROGRAM. <BR>
|
||||
/// If SPIFLASH_SST25TYPE is set and writeBytes is called, it will use opcode 0xAD #SPIFLASH_AAIWORDPROGRAM and care for Byte alignment.<BR>
|
||||
/// Note: AAI Wordprogramming is independent of Pages, so pagebreaking is not an issue when using AAI Wordprogramming.
|
||||
///
|
||||
#ifdef DOXYGEN //needed to tell doxygen not to ignore the define which is actually made somewhere else
|
||||
#define MY_SPIFLASH_SST25TYPE
|
||||
#endif
|
||||
|
||||
/** SPIFlash class */
|
||||
class SPIFlash
|
||||
{
|
||||
public:
|
||||
static uint8_t UNIQUEID[8]; //!< Storage for unique identifier
|
||||
explicit SPIFlash(uint8_t slaveSelectPin, uint16_t jedecID=0); //!< Constructor
|
||||
bool initialize(); //!< setup SPI, read device ID etc...
|
||||
void command(uint8_t cmd, bool isWrite=
|
||||
false); //!< Send a command to the flash chip, pass TRUE for isWrite when its a write command
|
||||
uint8_t readStatus(); //!< return the STATUS register
|
||||
uint8_t readByte(uint32_t addr); //!< read 1 byte from flash memory
|
||||
void readBytes(uint32_t addr, void* buf, uint16_t len); //!< read unlimited # of bytes
|
||||
void writeByte(uint32_t addr, uint8_t byt); //!< Write 1 byte to flash memory
|
||||
void writeBytes(uint32_t addr, const void* buf,
|
||||
uint16_t len); //!< write multiple bytes to flash memory (up to 64K), if define SPIFLASH_SST25TYPE is set AAI Word Programming will be used
|
||||
bool busy(); //!< check if the chip is busy erasing/writing
|
||||
void chipErase(); //!< erase entire flash memory array
|
||||
void blockErase4K(uint32_t address); //!< erase a 4Kbyte block
|
||||
void blockErase32K(uint32_t address); //!< erase a 32Kbyte block
|
||||
void blockErase64K(uint32_t addr); //!< erase a 64Kbyte block
|
||||
uint16_t readDeviceId(); //!< Get the manufacturer and device ID bytes (as a short word)
|
||||
uint8_t* readUniqueId(); //!< Get the 64 bit unique identifier, stores it in @ref UNIQUEID[8]
|
||||
|
||||
void sleep(); //!< Put device to sleep
|
||||
void wakeup(); //!< Wake device
|
||||
void end(); //!< end
|
||||
protected:
|
||||
void select(); //!< select
|
||||
void unselect(); //!< unselect
|
||||
uint8_t _slaveSelectPin; //!< Slave select pin
|
||||
uint16_t _jedecID; //!< JEDEC ID
|
||||
uint8_t _SPCR; //!< SPCR
|
||||
uint8_t _SPSR; //!< SPSR
|
||||
#ifdef SPI_HAS_TRANSACTION
|
||||
SPISettings _settings;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,127 @@
|
||||
// **********************************************************************************
|
||||
// This sketch is an example of using the SPIFlash library with a Moteino
|
||||
// that has an onboard SPI Flash chip. This sketch listens to a few serial commands
|
||||
// Hence type the following commands to interact with the SPI flash memory array:
|
||||
// - 'd' dumps the first 256bytes of the flash chip to screen
|
||||
// - 'e' erases the entire memory chip
|
||||
// - 'i' print manufacturer/device ID
|
||||
// - [0-9] writes a random byte to addresses [0-9] (either 0xAA or 0xBB)
|
||||
// Get the SPIFlash library from here: https://github.com/LowPowerLab/SPIFlash
|
||||
// **********************************************************************************
|
||||
// Copyright Felix Rusu, LowPowerLab.com
|
||||
// Library and code by Felix Rusu - felix@lowpowerlab.com
|
||||
// **********************************************************************************
|
||||
// License
|
||||
// **********************************************************************************
|
||||
// This program is free software; you can redistribute it
|
||||
// and/or modify it under the terms of the GNU General
|
||||
// Public License as published by the Free Software
|
||||
// Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will
|
||||
// be useful, but WITHOUT ANY WARRANTY; without even the
|
||||
// implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
// PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General
|
||||
// Public License along with this program.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Licence can be viewed at
|
||||
// http://www.gnu.org/licenses/gpl-3.0.txt
|
||||
//
|
||||
// Please maintain this license information along with authorship
|
||||
// and copyright notices in any redistribution of this code
|
||||
// **********************************************************************************
|
||||
|
||||
|
||||
#include <SPIFlash.h> //get it here: https://github.com/LowPowerLab/SPIFlash
|
||||
#include <SPI.h>
|
||||
|
||||
#define SERIAL_BAUD 115200
|
||||
char input = 0;
|
||||
long lastPeriod = -1;
|
||||
|
||||
#ifdef __AVR_ATmega1284P__
|
||||
#define LED 15 // Moteino MEGAs have LEDs on D15
|
||||
#define FLASH_SS 23 // and FLASH SS on D23
|
||||
#else
|
||||
#define LED 9 // Moteinos have LEDs on D9
|
||||
#define FLASH_SS 8 // and FLASH SS on D8
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////
|
||||
// flash(SPI_CS, MANUFACTURER_ID)
|
||||
// SPI_CS - CS pin attached to SPI flash chip (8 in case of Moteino)
|
||||
// MANUFACTURER_ID - OPTIONAL, 0x1F44 for adesto(ex atmel) 4mbit flash
|
||||
// 0xEF30 for windbond 4mbit flash
|
||||
//////////////////////////////////////////
|
||||
SPIFlash flash(FLASH_SS, 0xEF30);
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(SERIAL_BAUD);
|
||||
Serial.print("Start...");
|
||||
|
||||
if (flash.initialize()) {
|
||||
Serial.println("Init OK!");
|
||||
Blink(LED, 20, 10);
|
||||
} else {
|
||||
Serial.println("Init FAIL!");
|
||||
}
|
||||
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Handle serial input (to allow basic DEBUGGING of FLASH chip)
|
||||
// ie: display first 256 bytes in FLASH, erase chip, write bytes at first 10 positions, etc
|
||||
if (Serial.available() > 0) {
|
||||
input = Serial.read();
|
||||
if (input == 'd') { //d=dump flash area
|
||||
Serial.println("Flash content:");
|
||||
int counter = 0;
|
||||
|
||||
while(counter<=256) {
|
||||
Serial.print(flash.readByte(counter++), HEX);
|
||||
Serial.print('.');
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
} else if (input == 'e') {
|
||||
Serial.print("Erasing Flash chip ... ");
|
||||
flash.chipErase();
|
||||
while(flash.busy());
|
||||
Serial.println("DONE");
|
||||
} else if (input == 'i') {
|
||||
Serial.print("DeviceID: ");
|
||||
Serial.println(flash.readDeviceId(), HEX);
|
||||
} else if (input >= 48 && input <= 57) { //0-9
|
||||
Serial.print("\nWriteByte(");
|
||||
Serial.print(input);
|
||||
Serial.print(")");
|
||||
flash.writeByte(input-48, (millis()%2) ? 0xaa : 0xbb);
|
||||
}
|
||||
}
|
||||
|
||||
// Periodically blink the onboard LED while listening for serial commands
|
||||
if ((int)(millis()/500) > lastPeriod) {
|
||||
lastPeriod++;
|
||||
pinMode(LED, OUTPUT);
|
||||
digitalWrite(LED, lastPeriod%2);
|
||||
}
|
||||
}
|
||||
|
||||
void Blink(byte PIN, int DELAY_MS, byte loops)
|
||||
{
|
||||
pinMode(PIN, OUTPUT);
|
||||
while (loops--) {
|
||||
digitalWrite(PIN,HIGH);
|
||||
delay(DELAY_MS);
|
||||
digitalWrite(PIN,LOW);
|
||||
delay(DELAY_MS);
|
||||
}
|
||||
}
|
||||
18
lib/MySensors/drivers/SPIFlash/keywords.txt
Normal file
18
lib/MySensors/drivers/SPIFlash/keywords.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
SPIFlash KEYWORD1
|
||||
initialize KEYWORD2
|
||||
command KEYWORD2
|
||||
readStatus KEYWORD2
|
||||
readByte KEYWORD2
|
||||
readBytes KEYWORD2
|
||||
writeByte KEYWORD2
|
||||
writeBytes KEYWORD2
|
||||
flashBusy KEYWORD2
|
||||
chipErase KEYWORD2
|
||||
blockErase4K KEYWORD2
|
||||
blockErase32K KEYWORD2
|
||||
readDeviceId KEYWORD2
|
||||
readUniqueId KEYWORD2
|
||||
UNIQUEID KEYWORD2
|
||||
sleep KEYWORD2
|
||||
wakeup KEYWORD2
|
||||
end KEYWORD2
|
||||
165
lib/MySensors/drivers/TinyGSM/LICENSE
Normal file
165
lib/MySensors/drivers/TinyGSM/LICENSE
Normal file
@@ -0,0 +1,165 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
67
lib/MySensors/drivers/TinyGSM/TinyGsmClient.h
Normal file
67
lib/MySensors/drivers/TinyGSM/TinyGsmClient.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* file TinyGsmClient.h
|
||||
* author Volodymyr Shymanskyy
|
||||
* license LGPL-3.0
|
||||
* copyright Copyright (c) 2016 Volodymyr Shymanskyy
|
||||
* date Nov 2016
|
||||
*/
|
||||
|
||||
#ifndef TinyGsmClient_h
|
||||
#define TinyGsmClient_h
|
||||
|
||||
#if defined(TINY_GSM_MODEM_SIM800) || defined(TINY_GSM_MODEM_SIM868) || defined(TINY_GSM_MODEM_U201) || defined(TINY_GSM_MODEM_ESP8266)
|
||||
#define TINY_GSM_MODEM_HAS_SSL
|
||||
#endif
|
||||
|
||||
#if defined(TINY_GSM_MODEM_SIM808) || defined(TINY_GSM_MODEM_SIM868) || defined(TINY_GSM_MODEM_A7)
|
||||
#define TINY_GSM_MODEM_HAS_GPS
|
||||
#endif
|
||||
|
||||
#if defined(TINY_GSM_MODEM_SIM800) || defined(TINY_GSM_MODEM_SIM900)
|
||||
#define TINY_GSM_MODEM_HAS_GPRS
|
||||
#include "TinyGsmClientSIM800.h"
|
||||
typedef TinyGsmSim800 TinyGsm;
|
||||
typedef TinyGsmSim800::GsmClient TinyGsmClient;
|
||||
typedef TinyGsmSim800::GsmClientSecure TinyGsmClientSecure;
|
||||
|
||||
#elif defined(TINY_GSM_MODEM_SIM808) || defined(TINY_GSM_MODEM_SIM868)
|
||||
#define TINY_GSM_MODEM_HAS_GPRS
|
||||
#include "TinyGsmClientSIM808.h"
|
||||
typedef TinyGsmSim808 TinyGsm;
|
||||
typedef TinyGsmSim808::GsmClient TinyGsmClient;
|
||||
typedef TinyGsmSim808::GsmClientSecure TinyGsmClientSecure;
|
||||
|
||||
#elif defined(TINY_GSM_MODEM_A6) || defined(TINY_GSM_MODEM_A7)
|
||||
#define TINY_GSM_MODEM_HAS_GPRS
|
||||
#include "TinyGsmClientA6.h"
|
||||
typedef TinyGsm::GsmClient TinyGsmClient;
|
||||
|
||||
#elif defined(TINY_GSM_MODEM_M590)
|
||||
#define TINY_GSM_MODEM_HAS_GPRS
|
||||
#include "TinyGsmClientM590.h"
|
||||
typedef TinyGsm::GsmClient TinyGsmClient;
|
||||
|
||||
#elif defined(TINY_GSM_MODEM_U201)
|
||||
#define TINY_GSM_MODEM_HAS_GPRS
|
||||
#include "TinyGsmClientU201.h"
|
||||
typedef TinyGsmU201 TinyGsm;
|
||||
typedef TinyGsmU201::GsmClient TinyGsmClient;
|
||||
typedef TinyGsmU201::GsmClientSecure TinyGsmClientSecure;
|
||||
|
||||
#elif defined(TINY_GSM_MODEM_ESP8266)
|
||||
#define TINY_GSM_MODEM_HAS_WIFI
|
||||
#include "TinyGsmClientESP8266.h"
|
||||
typedef TinyGsm::GsmClient TinyGsmClient;
|
||||
typedef TinyGsm::GsmClientSecure TinyGsmClientSecure;
|
||||
|
||||
#elif defined(TINY_GSM_MODEM_XBEE)
|
||||
#define TINY_GSM_MODEM_HAS_GPRS
|
||||
#define TINY_GSM_MODEM_HAS_WIFI
|
||||
#include "TinyGsmClientXBee.h"
|
||||
typedef TinyGsm::GsmClient TinyGsmClient;
|
||||
|
||||
#else
|
||||
#error "Please define GSM modem model"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
843
lib/MySensors/drivers/TinyGSM/TinyGsmClientA6.h
Normal file
843
lib/MySensors/drivers/TinyGSM/TinyGsmClientA6.h
Normal file
@@ -0,0 +1,843 @@
|
||||
/**
|
||||
* file TinyGsmClientA6.h
|
||||
* author Volodymyr Shymanskyy
|
||||
* license LGPL-3.0
|
||||
* copyright Copyright (c) 2016 Volodymyr Shymanskyy
|
||||
* date Nov 2016
|
||||
*/
|
||||
|
||||
#ifndef TinyGsmClientA6_h
|
||||
#define TinyGsmClientA6_h
|
||||
|
||||
//#define TINY_GSM_DEBUG Serial
|
||||
|
||||
#if !defined(TINY_GSM_RX_BUFFER)
|
||||
#define TINY_GSM_RX_BUFFER 256
|
||||
#endif
|
||||
|
||||
#define TINY_GSM_MUX_COUNT 8
|
||||
|
||||
#include "TinyGsmCommon.h"
|
||||
|
||||
#define GSM_NL "\r\n"
|
||||
static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
|
||||
static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
|
||||
|
||||
enum SimStatus {
|
||||
SIM_ERROR = 0,
|
||||
SIM_READY = 1,
|
||||
SIM_LOCKED = 2,
|
||||
};
|
||||
|
||||
enum RegStatus {
|
||||
REG_UNREGISTERED = 0,
|
||||
REG_SEARCHING = 2,
|
||||
REG_DENIED = 3,
|
||||
REG_OK_HOME = 1,
|
||||
REG_OK_ROAMING = 5,
|
||||
REG_UNKNOWN = 4,
|
||||
};
|
||||
|
||||
|
||||
class TinyGsm
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
class GsmClient : public Client
|
||||
{
|
||||
friend class TinyGsm;
|
||||
typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
|
||||
|
||||
public:
|
||||
GsmClient() {}
|
||||
|
||||
GsmClient(TinyGsm& modem)
|
||||
{
|
||||
init(&modem);
|
||||
}
|
||||
|
||||
bool init(TinyGsm* modem)
|
||||
{
|
||||
this->at = modem;
|
||||
this->mux = -1;
|
||||
sock_connected = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual int connect(const char *host, uint16_t port)
|
||||
{
|
||||
stop();
|
||||
TINY_GSM_YIELD();
|
||||
rx.clear();
|
||||
uint8_t newMux = -1;
|
||||
sock_connected = at->modemConnect(host, port, &newMux);
|
||||
if (sock_connected) {
|
||||
mux = newMux;
|
||||
at->sockets[mux] = this;
|
||||
}
|
||||
return sock_connected;
|
||||
}
|
||||
|
||||
virtual int connect(IPAddress ip, uint16_t port)
|
||||
{
|
||||
String host;
|
||||
host.reserve(16);
|
||||
host += ip[0];
|
||||
host += ".";
|
||||
host += ip[1];
|
||||
host += ".";
|
||||
host += ip[2];
|
||||
host += ".";
|
||||
host += ip[3];
|
||||
return connect(host.c_str(), port);
|
||||
}
|
||||
|
||||
virtual void stop()
|
||||
{
|
||||
TINY_GSM_YIELD();
|
||||
at->sendAT(GF("+CIPCLOSE="), mux);
|
||||
sock_connected = false;
|
||||
at->waitResponse();
|
||||
rx.clear();
|
||||
}
|
||||
|
||||
virtual size_t write(const uint8_t *buf, size_t size)
|
||||
{
|
||||
TINY_GSM_YIELD();
|
||||
//at->maintain();
|
||||
return at->modemSend(buf, size, mux);
|
||||
}
|
||||
|
||||
virtual size_t write(uint8_t c)
|
||||
{
|
||||
return write(&c, 1);
|
||||
}
|
||||
|
||||
virtual int available()
|
||||
{
|
||||
TINY_GSM_YIELD();
|
||||
if (!rx.size() && sock_connected) {
|
||||
at->maintain();
|
||||
}
|
||||
return rx.size();
|
||||
}
|
||||
|
||||
virtual int read(uint8_t *buf, size_t size)
|
||||
{
|
||||
TINY_GSM_YIELD();
|
||||
size_t cnt = 0;
|
||||
while (cnt < size) {
|
||||
size_t chunk = TinyGsmMin(size-cnt, rx.size());
|
||||
if (chunk > 0) {
|
||||
rx.get(buf, chunk);
|
||||
buf += chunk;
|
||||
cnt += chunk;
|
||||
continue;
|
||||
}
|
||||
// TODO: Read directly into user buffer?
|
||||
if (!rx.size() && sock_connected) {
|
||||
at->maintain();
|
||||
//break;
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
virtual int read()
|
||||
{
|
||||
uint8_t c;
|
||||
if (read(&c, 1) == 1) {
|
||||
return c;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
virtual int peek()
|
||||
{
|
||||
return -1; //TODO
|
||||
}
|
||||
virtual void flush()
|
||||
{
|
||||
at->stream.flush();
|
||||
}
|
||||
|
||||
virtual uint8_t connected()
|
||||
{
|
||||
if (available()) {
|
||||
return true;
|
||||
}
|
||||
return sock_connected;
|
||||
}
|
||||
virtual operator bool()
|
||||
{
|
||||
return connected();
|
||||
}
|
||||
|
||||
/*
|
||||
* Extended API
|
||||
*/
|
||||
|
||||
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
|
||||
private:
|
||||
TinyGsm* at;
|
||||
uint8_t mux;
|
||||
bool sock_connected;
|
||||
RxFifo rx;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
explicit TinyGsm(Stream& stream)
|
||||
: stream(stream)
|
||||
{
|
||||
memset(sockets, 0, sizeof(sockets));
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic functions
|
||||
*/
|
||||
bool begin()
|
||||
{
|
||||
return init();
|
||||
}
|
||||
|
||||
bool init()
|
||||
{
|
||||
if (!testAT()) {
|
||||
return false;
|
||||
}
|
||||
sendAT(GF("&FZE0")); // Factory + Reset + Echo Off
|
||||
if (waitResponse() != 1) {
|
||||
return false;
|
||||
}
|
||||
sendAT(GF("+CMEE=0"));
|
||||
waitResponse();
|
||||
|
||||
sendAT(GF("+CMER=3,0,0,2"));
|
||||
waitResponse();
|
||||
|
||||
getSimStatus();
|
||||
return true;
|
||||
}
|
||||
|
||||
void setBaud(unsigned long baud)
|
||||
{
|
||||
sendAT(GF("+IPR="), baud);
|
||||
}
|
||||
|
||||
bool testAT(unsigned long timeout = 10000L)
|
||||
{
|
||||
for (unsigned long start = millis(); millis() - start < timeout; ) {
|
||||
sendAT(GF(""));
|
||||
if (waitResponse(200) == 1) {
|
||||
delay(100);
|
||||
return true;
|
||||
}
|
||||
delay(100);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void maintain()
|
||||
{
|
||||
waitResponse(10, NULL, NULL);
|
||||
}
|
||||
|
||||
bool factoryDefault()
|
||||
{
|
||||
sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
|
||||
waitResponse();
|
||||
sendAT(GF("&W")); // Write configuration
|
||||
return waitResponse() == 1;
|
||||
}
|
||||
|
||||
String getModemInfo()
|
||||
{
|
||||
sendAT(GF("I"));
|
||||
String res;
|
||||
if (waitResponse(1000L, res) != 1) {
|
||||
return "";
|
||||
}
|
||||
res.replace(GSM_NL "OK" GSM_NL, "");
|
||||
res.replace(GSM_NL, " ");
|
||||
res.trim();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool hasSSL()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Power functions
|
||||
*/
|
||||
|
||||
bool restart()
|
||||
{
|
||||
if (!testAT()) {
|
||||
return false;
|
||||
}
|
||||
sendAT(GF("+RST=1"));
|
||||
delay(3000);
|
||||
return init();
|
||||
}
|
||||
|
||||
bool poweroff()
|
||||
{
|
||||
sendAT(GF("+CPOF"));
|
||||
return waitResponse() == 1;
|
||||
}
|
||||
|
||||
bool radioOff() TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
|
||||
bool sleepEnable(bool enable = true) TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
|
||||
/*
|
||||
* SIM card functions
|
||||
*/
|
||||
|
||||
bool simUnlock(const char *pin)
|
||||
{
|
||||
sendAT(GF("+CPIN=\""), pin, GF("\""));
|
||||
return waitResponse() == 1;
|
||||
}
|
||||
|
||||
String getSimCCID()
|
||||
{
|
||||
sendAT(GF("+CCID"));
|
||||
if (waitResponse(GF(GSM_NL "+SCID: SIM Card ID:")) != 1) {
|
||||
return "";
|
||||
}
|
||||
String res = stream.readStringUntil('\n');
|
||||
waitResponse();
|
||||
res.trim();
|
||||
return res;
|
||||
}
|
||||
|
||||
String getIMEI()
|
||||
{
|
||||
sendAT(GF("+GSN"));
|
||||
if (waitResponse(GF(GSM_NL)) != 1) {
|
||||
return "";
|
||||
}
|
||||
String res = stream.readStringUntil('\n');
|
||||
waitResponse();
|
||||
res.trim();
|
||||
return res;
|
||||
}
|
||||
|
||||
SimStatus getSimStatus(unsigned long timeout = 10000L)
|
||||
{
|
||||
for (unsigned long start = millis(); millis() - start < timeout; ) {
|
||||
sendAT(GF("+CPIN?"));
|
||||
if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
|
||||
delay(1000);
|
||||
continue;
|
||||
}
|
||||
int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"));
|
||||
waitResponse();
|
||||
switch (status) {
|
||||
case 2:
|
||||
case 3:
|
||||
return SIM_LOCKED;
|
||||
case 1:
|
||||
return SIM_READY;
|
||||
default:
|
||||
return SIM_ERROR;
|
||||
}
|
||||
}
|
||||
return SIM_ERROR;
|
||||
}
|
||||
|
||||
RegStatus getRegistrationStatus()
|
||||
{
|
||||
sendAT(GF("+CREG?"));
|
||||
if (waitResponse(GF(GSM_NL "+CREG:")) != 1) {
|
||||
return REG_UNKNOWN;
|
||||
}
|
||||
streamSkipUntil(','); // Skip format (0)
|
||||
int status = stream.readStringUntil('\n').toInt();
|
||||
waitResponse();
|
||||
return (RegStatus)status;
|
||||
}
|
||||
|
||||
String getOperator()
|
||||
{
|
||||
sendAT(GF("+COPS=3,0")); // Set format
|
||||
waitResponse();
|
||||
|
||||
sendAT(GF("+COPS?"));
|
||||
if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
|
||||
return "";
|
||||
}
|
||||
streamSkipUntil('"'); // Skip mode and format
|
||||
String res = stream.readStringUntil('"');
|
||||
waitResponse();
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic network functions
|
||||
*/
|
||||
|
||||
int getSignalQuality()
|
||||
{
|
||||
sendAT(GF("+CSQ"));
|
||||
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
|
||||
return 99;
|
||||
}
|
||||
int res = stream.readStringUntil(',').toInt();
|
||||
waitResponse();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool isNetworkConnected()
|
||||
{
|
||||
RegStatus s = getRegistrationStatus();
|
||||
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
|
||||
}
|
||||
|
||||
bool waitForNetwork(unsigned long timeout = 60000L)
|
||||
{
|
||||
for (unsigned long start = millis(); millis() - start < timeout; ) {
|
||||
if (isNetworkConnected()) {
|
||||
return true;
|
||||
}
|
||||
delay(250);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* GPRS functions
|
||||
*/
|
||||
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL)
|
||||
{
|
||||
gprsDisconnect();
|
||||
|
||||
sendAT(GF("+CGATT=1"));
|
||||
if (waitResponse(60000L) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: wait AT+CGATT?
|
||||
|
||||
sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"');
|
||||
waitResponse();
|
||||
|
||||
if (!user) {
|
||||
user = "";
|
||||
}
|
||||
if (!pwd) {
|
||||
pwd = "";
|
||||
}
|
||||
sendAT(GF("+CSTT=\""), apn, GF("\",\""), user, GF("\",\""), pwd, GF("\""));
|
||||
if (waitResponse(60000L) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sendAT(GF("+CGACT=1,1"));
|
||||
waitResponse(60000L);
|
||||
|
||||
sendAT(GF("+CIPMUX=1"));
|
||||
if (waitResponse() != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gprsDisconnect()
|
||||
{
|
||||
// Shut the TCP/IP connection
|
||||
sendAT(GF("+CIPSHUT"));
|
||||
if (waitResponse(60000L) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i<3; i++) {
|
||||
sendAT(GF("+CGATT=0"));
|
||||
if (waitResponse(5000L) == 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isGprsConnected()
|
||||
{
|
||||
sendAT(GF("+CGATT?"));
|
||||
if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) {
|
||||
return false;
|
||||
}
|
||||
int res = stream.readStringUntil('\n').toInt();
|
||||
waitResponse();
|
||||
return (res == 1);
|
||||
}
|
||||
|
||||
String getLocalIP()
|
||||
{
|
||||
sendAT(GF("+CIFSR"));
|
||||
String res;
|
||||
if (waitResponse(10000L, res) != 1) {
|
||||
return "";
|
||||
}
|
||||
res.replace(GSM_NL "OK" GSM_NL, "");
|
||||
res.replace(GSM_NL, "");
|
||||
res.trim();
|
||||
return res;
|
||||
}
|
||||
|
||||
IPAddress localIP()
|
||||
{
|
||||
return TinyGsmIpFromString(getLocalIP());
|
||||
}
|
||||
|
||||
/*
|
||||
* Phone Call functions
|
||||
*/
|
||||
|
||||
bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE;
|
||||
|
||||
bool callAnswer()
|
||||
{
|
||||
sendAT(GF("A"));
|
||||
return waitResponse() == 1;
|
||||
}
|
||||
|
||||
// Returns true on pick-up, false on error/busy
|
||||
bool callNumber(const String& number)
|
||||
{
|
||||
if (number == GF("last")) {
|
||||
sendAT(GF("DLST"));
|
||||
} else {
|
||||
sendAT(GF("D\""), number, "\";");
|
||||
}
|
||||
|
||||
if (waitResponse(5000L) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (waitResponse(60000L,
|
||||
GF(GSM_NL "+CIEV: \"CALL\",1"),
|
||||
GF(GSM_NL "+CIEV: \"CALL\",0"),
|
||||
GFP(GSM_ERROR)) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int rsp = waitResponse(60000L,
|
||||
GF(GSM_NL "+CIEV: \"SOUNDER\",0"),
|
||||
GF(GSM_NL "+CIEV: \"CALL\",0"));
|
||||
|
||||
int rsp2 = waitResponse(300L, GF(GSM_NL "BUSY" GSM_NL), GF(GSM_NL "NO ANSWER" GSM_NL));
|
||||
|
||||
return rsp == 1 && rsp2 == 0;
|
||||
}
|
||||
|
||||
bool callHangup()
|
||||
{
|
||||
sendAT(GF("H"));
|
||||
return waitResponse() == 1;
|
||||
}
|
||||
|
||||
// 0-9,*,#,A,B,C,D
|
||||
bool dtmfSend(char cmd, unsigned duration_ms = 100)
|
||||
{
|
||||
duration_ms = constrain(duration_ms, 100, 1000);
|
||||
|
||||
// The duration parameter is not working, so we simulate it using delay..
|
||||
// TODO: Maybe there's another way...
|
||||
|
||||
//sendAT(GF("+VTD="), duration_ms / 100);
|
||||
//waitResponse();
|
||||
|
||||
sendAT(GF("+VTS="), cmd);
|
||||
if (waitResponse(10000L) == 1) {
|
||||
delay(duration_ms);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Audio functions
|
||||
*/
|
||||
|
||||
bool audioSetHeadphones()
|
||||
{
|
||||
sendAT(GF("+SNFS=0"));
|
||||
return waitResponse() == 1;
|
||||
}
|
||||
|
||||
bool audioSetSpeaker()
|
||||
{
|
||||
sendAT(GF("+SNFS=1"));
|
||||
return waitResponse() == 1;
|
||||
}
|
||||
|
||||
bool audioMuteMic(bool mute)
|
||||
{
|
||||
sendAT(GF("+CMUT="), mute);
|
||||
return waitResponse() == 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Messaging functions
|
||||
*/
|
||||
|
||||
String sendUSSD(const String& code)
|
||||
{
|
||||
sendAT(GF("+CMGF=1"));
|
||||
waitResponse();
|
||||
sendAT(GF("+CSCS=\"HEX\""));
|
||||
waitResponse();
|
||||
sendAT(GF("+CUSD=1,\""), code, GF("\",15"));
|
||||
if (waitResponse(10000L) != 1) {
|
||||
return "";
|
||||
}
|
||||
if (waitResponse(GF(GSM_NL "+CUSD:")) != 1) {
|
||||
return "";
|
||||
}
|
||||
stream.readStringUntil('"');
|
||||
String hex = stream.readStringUntil('"');
|
||||
stream.readStringUntil(',');
|
||||
int dcs = stream.readStringUntil('\n').toInt();
|
||||
|
||||
if (dcs == 15) {
|
||||
return TinyGsmDecodeHex7bit(hex);
|
||||
} else if (dcs == 72) {
|
||||
return TinyGsmDecodeHex16bit(hex);
|
||||
} else {
|
||||
return hex;
|
||||
}
|
||||
}
|
||||
|
||||
bool sendSMS(const String& number, const String& text)
|
||||
{
|
||||
sendAT(GF("+CMGF=1"));
|
||||
waitResponse();
|
||||
sendAT(GF("+CMGS=\""), number, GF("\""));
|
||||
if (waitResponse(GF(">")) != 1) {
|
||||
return false;
|
||||
}
|
||||
stream.print(text);
|
||||
stream.write((char)0x1A);
|
||||
stream.flush();
|
||||
return waitResponse(60000L) == 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Location functions
|
||||
*/
|
||||
|
||||
String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE;
|
||||
|
||||
/*
|
||||
* Battery functions
|
||||
*/
|
||||
|
||||
uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE;
|
||||
|
||||
int getBattPercent()
|
||||
{
|
||||
sendAT(GF("+CBC?"));
|
||||
if (waitResponse(GF(GSM_NL "+CBC:")) != 1) {
|
||||
return false;
|
||||
}
|
||||
stream.readStringUntil(',');
|
||||
int res = stream.readStringUntil('\n').toInt();
|
||||
waitResponse();
|
||||
return res;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
bool modemConnect(const char* host, uint16_t port, uint8_t* mux)
|
||||
{
|
||||
sendAT(GF("+CIPSTART="), GF("\"TCP"), GF("\",\""), host, GF("\","), port);
|
||||
|
||||
if (waitResponse(75000L, GF(GSM_NL "+CIPNUM:")) != 1) {
|
||||
return false;
|
||||
}
|
||||
int newMux = stream.readStringUntil('\n').toInt();
|
||||
|
||||
int rsp = waitResponse(75000L,
|
||||
GF("CONNECT OK" GSM_NL),
|
||||
GF("CONNECT FAIL" GSM_NL),
|
||||
GF("ALREADY CONNECT" GSM_NL));
|
||||
if (waitResponse() != 1) {
|
||||
return false;
|
||||
}
|
||||
*mux = newMux;
|
||||
|
||||
return (1 == rsp);
|
||||
}
|
||||
|
||||
int modemSend(const void* buff, size_t len, uint8_t mux)
|
||||
{
|
||||
sendAT(GF("+CIPSEND="), mux, ',', len);
|
||||
if (waitResponse(2000L, GF(GSM_NL ">")) != 1) {
|
||||
return 0;
|
||||
}
|
||||
stream.write((uint8_t*)buff, len);
|
||||
stream.flush();
|
||||
if (waitResponse(10000L, GFP(GSM_OK), GF(GSM_NL "FAIL")) != 1) {
|
||||
return 0;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
bool modemGetConnected(uint8_t mux)
|
||||
{
|
||||
sendAT(GF("+CIPSTATUS")); //TODO mux?
|
||||
int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), GF(",\"CLOSING\""),
|
||||
GF(",\"INITIAL\""));
|
||||
waitResponse();
|
||||
return 1 == res;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/* Utilities */
|
||||
|
||||
template<typename T>
|
||||
void streamWrite(T last)
|
||||
{
|
||||
stream.print(last);
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
void streamWrite(T head, Args... tail)
|
||||
{
|
||||
stream.print(head);
|
||||
streamWrite(tail...);
|
||||
}
|
||||
|
||||
bool streamSkipUntil(char c) //TODO: timeout
|
||||
{
|
||||
while (true) {
|
||||
while (!stream.available()) {
|
||||
TINY_GSM_YIELD();
|
||||
}
|
||||
if (stream.read() == c) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void sendAT(Args... cmd)
|
||||
{
|
||||
streamWrite("AT", cmd..., GSM_NL);
|
||||
stream.flush();
|
||||
TINY_GSM_YIELD();
|
||||
//DBG("### AT:", cmd...);
|
||||
}
|
||||
|
||||
// TODO: Optimize this!
|
||||
uint8_t waitResponse(uint32_t timeout, String& data,
|
||||
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
|
||||
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
|
||||
{
|
||||
/*String r1s(r1); r1s.trim();
|
||||
String r2s(r2); r2s.trim();
|
||||
String r3s(r3); r3s.trim();
|
||||
String r4s(r4); r4s.trim();
|
||||
String r5s(r5); r5s.trim();
|
||||
DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
|
||||
data.reserve(64);
|
||||
int index = 0;
|
||||
unsigned long startMillis = millis();
|
||||
do {
|
||||
TINY_GSM_YIELD();
|
||||
while (stream.available() > 0) {
|
||||
int a = stream.read();
|
||||
if (a <= 0) {
|
||||
continue; // Skip 0x00 bytes, just in case
|
||||
}
|
||||
data += (char)a;
|
||||
if (r1 && data.endsWith(r1)) {
|
||||
index = 1;
|
||||
goto finish;
|
||||
} else if (r2 && data.endsWith(r2)) {
|
||||
index = 2;
|
||||
goto finish;
|
||||
} else if (r3 && data.endsWith(r3)) {
|
||||
index = 3;
|
||||
goto finish;
|
||||
} else if (r4 && data.endsWith(r4)) {
|
||||
index = 4;
|
||||
goto finish;
|
||||
} else if (r5 && data.endsWith(r5)) {
|
||||
index = 5;
|
||||
goto finish;
|
||||
} else if (data.endsWith(GF("+CIPRCV:"))) {
|
||||
int mux = stream.readStringUntil(',').toInt();
|
||||
int len = stream.readStringUntil(',').toInt();
|
||||
int len_orig = len;
|
||||
if (len > sockets[mux]->rx.free()) {
|
||||
DBG("### Buffer overflow: ", len, "->", sockets[mux]->rx.free());
|
||||
} else {
|
||||
DBG("### Got: ", len, "->", sockets[mux]->rx.free());
|
||||
}
|
||||
while (len--) {
|
||||
while (!stream.available()) {
|
||||
TINY_GSM_YIELD();
|
||||
}
|
||||
sockets[mux]->rx.put(stream.read());
|
||||
}
|
||||
if (len_orig > sockets[mux]->available()) { // TODO
|
||||
DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig);
|
||||
}
|
||||
data = "";
|
||||
} else if (data.endsWith(GF("+TCPCLOSED:"))) {
|
||||
int mux = stream.readStringUntil('\n').toInt();
|
||||
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT) {
|
||||
sockets[mux]->sock_connected = false;
|
||||
}
|
||||
data = "";
|
||||
DBG("### Closed: ", mux);
|
||||
}
|
||||
}
|
||||
} while (millis() - startMillis < timeout);
|
||||
finish:
|
||||
if (!index) {
|
||||
data.trim();
|
||||
if (data.length()) {
|
||||
DBG("### Unhandled:", data);
|
||||
}
|
||||
data = "";
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
uint8_t waitResponse(uint32_t timeout,
|
||||
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
|
||||
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
|
||||
{
|
||||
String data;
|
||||
return waitResponse(timeout, data, r1, r2, r3, r4, r5);
|
||||
}
|
||||
|
||||
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
|
||||
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
|
||||
{
|
||||
return waitResponse(1000, r1, r2, r3, r4, r5);
|
||||
}
|
||||
|
||||
public:
|
||||
Stream& stream;
|
||||
|
||||
protected:
|
||||
GsmClient* sockets[TINY_GSM_MUX_COUNT];
|
||||
};
|
||||
|
||||
#endif
|
||||
591
lib/MySensors/drivers/TinyGSM/TinyGsmClientESP8266.h
Normal file
591
lib/MySensors/drivers/TinyGSM/TinyGsmClientESP8266.h
Normal file
@@ -0,0 +1,591 @@
|
||||
/**
|
||||
* file TinyGsmClientESP8266.h
|
||||
* author Volodymyr Shymanskyy
|
||||
* license LGPL-3.0
|
||||
* copyright Copyright (c) 2016 Volodymyr Shymanskyy
|
||||
* date Nov 2016
|
||||
*/
|
||||
|
||||
#ifndef TinyGsmClientESP8266_h
|
||||
#define TinyGsmClientESP8266_h
|
||||
|
||||
//#define TINY_GSM_DEBUG Serial
|
||||
|
||||
#if !defined(TINY_GSM_RX_BUFFER)
|
||||
#define TINY_GSM_RX_BUFFER 512
|
||||
#endif
|
||||
|
||||
#define TINY_GSM_MUX_COUNT 5
|
||||
|
||||
#include "TinyGsmCommon.h"
|
||||
|
||||
#define GSM_NL "\r\n"
|
||||
static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
|
||||
static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
|
||||
static unsigned TINY_GSM_TCP_KEEP_ALIVE = 120;
|
||||
|
||||
class TinyGsm
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
class GsmClient : public Client
|
||||
{
|
||||
friend class TinyGsm;
|
||||
typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
|
||||
|
||||
public:
|
||||
GsmClient() {}
|
||||
|
||||
GsmClient(TinyGsm& modem, uint8_t mux = 1)
|
||||
{
|
||||
init(&modem, mux);
|
||||
}
|
||||
|
||||
bool init(TinyGsm* modem, uint8_t mux = 1)
|
||||
{
|
||||
this->at = modem;
|
||||
this->mux = mux;
|
||||
sock_connected = false;
|
||||
|
||||
at->sockets[mux] = this;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual int connect(const char *host, uint16_t port)
|
||||
{
|
||||
stop();
|
||||
TINY_GSM_YIELD();
|
||||
rx.clear();
|
||||
sock_connected = at->modemConnect(host, port, mux);
|
||||
return sock_connected;
|
||||
}
|
||||
|
||||
virtual int connect(IPAddress ip, uint16_t port)
|
||||
{
|
||||
String host;
|
||||
host.reserve(16);
|
||||
host += ip[0];
|
||||
host += ".";
|
||||
host += ip[1];
|
||||
host += ".";
|
||||
host += ip[2];
|
||||
host += ".";
|
||||
host += ip[3];
|
||||
return connect(host.c_str(), port);
|
||||
}
|
||||
|
||||
virtual void stop()
|
||||
{
|
||||
TINY_GSM_YIELD();
|
||||
at->sendAT(GF("+CIPCLOSE="), mux);
|
||||
sock_connected = false;
|
||||
at->waitResponse();
|
||||
rx.clear();
|
||||
}
|
||||
|
||||
virtual size_t write(const uint8_t *buf, size_t size)
|
||||
{
|
||||
TINY_GSM_YIELD();
|
||||
//at->maintain();
|
||||
return at->modemSend(buf, size, mux);
|
||||
}
|
||||
|
||||
virtual size_t write(uint8_t c)
|
||||
{
|
||||
return write(&c, 1);
|
||||
}
|
||||
|
||||
virtual int available()
|
||||
{
|
||||
TINY_GSM_YIELD();
|
||||
if (!rx.size() && sock_connected) {
|
||||
at->maintain();
|
||||
}
|
||||
return rx.size();
|
||||
}
|
||||
|
||||
virtual int read(uint8_t *buf, size_t size)
|
||||
{
|
||||
TINY_GSM_YIELD();
|
||||
size_t cnt = 0;
|
||||
while (cnt < size) {
|
||||
size_t chunk = TinyGsmMin(size-cnt, rx.size());
|
||||
if (chunk > 0) {
|
||||
rx.get(buf, chunk);
|
||||
buf += chunk;
|
||||
cnt += chunk;
|
||||
continue;
|
||||
}
|
||||
// TODO: Read directly into user buffer?
|
||||
if (!rx.size() && sock_connected) {
|
||||
at->maintain();
|
||||
//break;
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
virtual int read()
|
||||
{
|
||||
uint8_t c;
|
||||
if (read(&c, 1) == 1) {
|
||||
return c;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
virtual int peek()
|
||||
{
|
||||
return -1; //TODO
|
||||
}
|
||||
virtual void flush()
|
||||
{
|
||||
at->stream.flush();
|
||||
}
|
||||
|
||||
virtual uint8_t connected()
|
||||
{
|
||||
if (available()) {
|
||||
return true;
|
||||
}
|
||||
return sock_connected;
|
||||
}
|
||||
virtual operator bool()
|
||||
{
|
||||
return connected();
|
||||
}
|
||||
|
||||
/*
|
||||
* Extended API
|
||||
*/
|
||||
|
||||
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
|
||||
private:
|
||||
TinyGsm* at;
|
||||
uint8_t mux;
|
||||
bool sock_connected;
|
||||
RxFifo rx;
|
||||
};
|
||||
|
||||
class GsmClientSecure : public GsmClient
|
||||
{
|
||||
public:
|
||||
GsmClientSecure() {}
|
||||
|
||||
GsmClientSecure(TinyGsm& modem, uint8_t mux = 1)
|
||||
: GsmClient(modem, mux)
|
||||
{}
|
||||
|
||||
public:
|
||||
virtual int connect(const char *host, uint16_t port)
|
||||
{
|
||||
stop();
|
||||
TINY_GSM_YIELD();
|
||||
rx.clear();
|
||||
sock_connected = at->modemConnect(host, port, mux, true);
|
||||
return sock_connected;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
explicit TinyGsm(Stream& stream)
|
||||
: stream(stream)
|
||||
{
|
||||
memset(sockets, 0, sizeof(sockets));
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic functions
|
||||
*/
|
||||
bool begin()
|
||||
{
|
||||
return init();
|
||||
}
|
||||
|
||||
bool init()
|
||||
{
|
||||
if (!testAT()) {
|
||||
return false;
|
||||
}
|
||||
sendAT(GF("E0")); // Echo Off
|
||||
if (waitResponse() != 1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void setBaud(unsigned long baud)
|
||||
{
|
||||
sendAT(GF("+IPR="), baud);
|
||||
}
|
||||
|
||||
bool testAT(unsigned long timeout = 10000L)
|
||||
{
|
||||
for (unsigned long start = millis(); millis() - start < timeout; ) {
|
||||
sendAT(GF(""));
|
||||
if (waitResponse(200) == 1) {
|
||||
delay(100);
|
||||
return true;
|
||||
}
|
||||
delay(100);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void maintain()
|
||||
{
|
||||
waitResponse(10, NULL, NULL);
|
||||
}
|
||||
|
||||
bool factoryDefault()
|
||||
{
|
||||
sendAT(GF("+RESTORE"));
|
||||
return waitResponse() == 1;
|
||||
}
|
||||
|
||||
String getModemInfo()
|
||||
{
|
||||
sendAT(GF("+GMR"));
|
||||
String res;
|
||||
if (waitResponse(1000L, res) != 1) {
|
||||
return "";
|
||||
}
|
||||
res.replace(GSM_NL "OK" GSM_NL, "");
|
||||
res.replace(GSM_NL, " ");
|
||||
res.trim();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool hasSSL()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Power functions
|
||||
*/
|
||||
|
||||
bool restart()
|
||||
{
|
||||
if (!testAT()) {
|
||||
return false;
|
||||
}
|
||||
sendAT(GF("+RST"));
|
||||
if (waitResponse(10000L) != 1) {
|
||||
return false;
|
||||
}
|
||||
if (waitResponse(10000L, GF(GSM_NL "ready" GSM_NL)) != 1) {
|
||||
return false;
|
||||
}
|
||||
delay(500);
|
||||
return init();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Generic network functions
|
||||
*/
|
||||
|
||||
int getSignalQuality()
|
||||
{
|
||||
sendAT(GF("+CWJAP_CUR?"));
|
||||
int res1 = waitResponse(GF("No AP"), GF("+CWJAP_CUR:"));
|
||||
if (res1 != 2) {
|
||||
waitResponse();
|
||||
return 0;
|
||||
}
|
||||
streamSkipUntil(','); // Skip SSID
|
||||
streamSkipUntil(','); // Skip BSSID/MAC address
|
||||
streamSkipUntil(','); // Skip Chanel number
|
||||
int res2 = stream.parseInt(); // Read RSSI
|
||||
waitResponse(); // Returns an OK after the value
|
||||
return res2;
|
||||
}
|
||||
|
||||
bool isNetworkConnected()
|
||||
{
|
||||
sendAT(GF("+CIPSTATUS"));
|
||||
int res1 = waitResponse(3000, GF("STATUS:"));
|
||||
int res2 = 0;
|
||||
if (res1 == 1) {
|
||||
res2 = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5"));
|
||||
}
|
||||
// <stat> status of ESP8266 station interface
|
||||
// 2 : ESP8266 station connected to an AP and has obtained IP
|
||||
// 3 : ESP8266 station created a TCP or UDP transmission
|
||||
// 4 : the TCP or UDP transmission of ESP8266 station disconnected (but AP is connected)
|
||||
// 5 : ESP8266 station did NOT connect to an AP
|
||||
waitResponse(); // Returns an OK after the status
|
||||
if (res2 == 2 || res2 == 3 || res2 == 4) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool waitForNetwork(unsigned long timeout = 60000L)
|
||||
{
|
||||
for (unsigned long start = millis(); millis() - start < timeout; ) {
|
||||
sendAT(GF("+CIPSTATUS"));
|
||||
int res1 = waitResponse(3000, GF("busy p..."), GF("STATUS:"));
|
||||
if (res1 == 2) {
|
||||
int res2 = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5"));
|
||||
if (res2 == 2 || res2 == 3 || res2 == 4) {
|
||||
waitResponse();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
delay(250);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* WiFi functions
|
||||
*/
|
||||
bool networkConnect(const char* ssid, const char* pwd)
|
||||
{
|
||||
|
||||
sendAT(GF("+CIPMUX=1"));
|
||||
if (waitResponse() != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sendAT(GF("+CWMODE_CUR=1"));
|
||||
if (waitResponse() != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sendAT(GF("+CWJAP_CUR=\""), ssid, GF("\",\""), pwd, GF("\""));
|
||||
if (waitResponse(30000L, GFP(GSM_OK), GF(GSM_NL "FAIL" GSM_NL)) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool networkDisconnect()
|
||||
{
|
||||
sendAT(GF("+CWQAP"));
|
||||
bool retVal = waitResponse(10000L) == 1;
|
||||
waitResponse(GF("WIFI DISCONNECT"));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
String getLocalIP()
|
||||
{
|
||||
sendAT(GF("+CIPSTA_CUR??"));
|
||||
int res1 = waitResponse(GF("ERROR"), GF("+CWJAP_CUR:"));
|
||||
if (res1 != 2) {
|
||||
return "";
|
||||
}
|
||||
String res2 = stream.readStringUntil('"');
|
||||
waitResponse();
|
||||
return res2;
|
||||
}
|
||||
|
||||
IPAddress localIP()
|
||||
{
|
||||
return TinyGsmIpFromString(getLocalIP());
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
bool modemConnect(const char* host, uint16_t port, uint8_t mux, bool ssl = false)
|
||||
{
|
||||
if (ssl) {
|
||||
sendAT(GF("+CIPSSLSIZE=4096"));
|
||||
waitResponse();
|
||||
}
|
||||
sendAT(GF("+CIPSTART="), mux, ',', ssl ? GF("\"SSL") : GF("\"TCP"), GF("\",\""), host, GF("\","),
|
||||
port, GF(","), TINY_GSM_TCP_KEEP_ALIVE);
|
||||
// TODO: Check mux
|
||||
int rsp = waitResponse(75000L,
|
||||
GFP(GSM_OK),
|
||||
GFP(GSM_ERROR),
|
||||
GF("ALREADY CONNECT"));
|
||||
// if (rsp == 3) waitResponse(); // May return "ERROR" after the "ALREADY CONNECT"
|
||||
return (1 == rsp);
|
||||
}
|
||||
|
||||
int modemSend(const void* buff, size_t len, uint8_t mux)
|
||||
{
|
||||
sendAT(GF("+CIPSEND="), mux, ',', len);
|
||||
if (waitResponse(GF(">")) != 1) {
|
||||
return 0;
|
||||
}
|
||||
stream.write((uint8_t*)buff, len);
|
||||
stream.flush();
|
||||
if (waitResponse(10000L, GF(GSM_NL "SEND OK" GSM_NL)) != 1) {
|
||||
return 0;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
bool modemGetConnected(uint8_t mux)
|
||||
{
|
||||
// TODO: re-check this
|
||||
sendAT(GF("+CIPSTATUS="), mux);
|
||||
int res1 = waitResponse(3000, GF("STATUS:"));
|
||||
int res2;
|
||||
if (res1 == 1) {
|
||||
res2 = waitResponse(GFP(GSM_ERROR), GF("2"), GF("3"), GF("4"), GF("5"));
|
||||
}
|
||||
// <stat> status of ESP8266 station interface
|
||||
// 2 : ESP8266 station connected to an AP and has obtained IP
|
||||
// 3 : ESP8266 station created a TCP or UDP transmission
|
||||
// 4 : the TCP or UDP transmission of ESP8266 station disconnected (but AP is connected)
|
||||
// 5 : ESP8266 station did NOT connect to an AP
|
||||
waitResponse(); // Returns an OK after the status
|
||||
if (res2 == 2 || res2 == 3 || res2 == 4) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/* Utilities */
|
||||
|
||||
template<typename T>
|
||||
void streamWrite(T last)
|
||||
{
|
||||
stream.print(last);
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
void streamWrite(T head, Args... tail)
|
||||
{
|
||||
stream.print(head);
|
||||
streamWrite(tail...);
|
||||
}
|
||||
|
||||
bool streamSkipUntil(char c) //TODO: timeout
|
||||
{
|
||||
while (true) {
|
||||
while (!stream.available()) {
|
||||
TINY_GSM_YIELD();
|
||||
}
|
||||
if (stream.read() == c) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void sendAT(Args... cmd)
|
||||
{
|
||||
streamWrite("AT", cmd..., GSM_NL);
|
||||
stream.flush();
|
||||
TINY_GSM_YIELD();
|
||||
//DBG("### AT:", cmd...);
|
||||
}
|
||||
|
||||
// TODO: Optimize this!
|
||||
uint8_t waitResponse(uint32_t timeout, String& data,
|
||||
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
|
||||
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
|
||||
{
|
||||
/*String r1s(r1); r1s.trim();
|
||||
String r2s(r2); r2s.trim();
|
||||
String r3s(r3); r3s.trim();
|
||||
String r4s(r4); r4s.trim();
|
||||
String r5s(r5); r5s.trim();
|
||||
DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
|
||||
data.reserve(64);
|
||||
int index = 0;
|
||||
unsigned long startMillis = millis();
|
||||
do {
|
||||
TINY_GSM_YIELD();
|
||||
while (stream.available() > 0) {
|
||||
int a = stream.read();
|
||||
if (a <= 0) {
|
||||
continue; // Skip 0x00 bytes, just in case
|
||||
}
|
||||
data += (char)a;
|
||||
if (r1 && data.endsWith(r1)) {
|
||||
index = 1;
|
||||
goto finish;
|
||||
} else if (r2 && data.endsWith(r2)) {
|
||||
index = 2;
|
||||
goto finish;
|
||||
} else if (r3 && data.endsWith(r3)) {
|
||||
index = 3;
|
||||
goto finish;
|
||||
} else if (r4 && data.endsWith(r4)) {
|
||||
index = 4;
|
||||
goto finish;
|
||||
} else if (r5 && data.endsWith(r5)) {
|
||||
index = 5;
|
||||
goto finish;
|
||||
} else if (data.endsWith(GF(GSM_NL "+IPD,"))) {
|
||||
int mux = stream.readStringUntil(',').toInt();
|
||||
int len = stream.readStringUntil(':').toInt();
|
||||
int len_orig = len;
|
||||
if (len > sockets[mux]->rx.free()) {
|
||||
DBG("### Buffer overflow: ", len, "->", sockets[mux]->rx.free());
|
||||
} else {
|
||||
DBG("### Got: ", len, "->", sockets[mux]->rx.free());
|
||||
}
|
||||
while (len--) {
|
||||
while (!stream.available()) {
|
||||
TINY_GSM_YIELD();
|
||||
}
|
||||
sockets[mux]->rx.put(stream.read());
|
||||
}
|
||||
if (len_orig > sockets[mux]->available()) { // TODO
|
||||
DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig);
|
||||
}
|
||||
data = "";
|
||||
} else if (data.endsWith(GF("CLOSED"))) {
|
||||
int muxStart = max(0,data.lastIndexOf(GSM_NL, data.length()-8));
|
||||
int coma = data.indexOf(',', muxStart);
|
||||
int mux = data.substring(muxStart, coma).toInt();
|
||||
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
|
||||
sockets[mux]->sock_connected = false;
|
||||
}
|
||||
data = "";
|
||||
DBG("### Closed: ", mux);
|
||||
}
|
||||
}
|
||||
} while (millis() - startMillis < timeout);
|
||||
finish:
|
||||
if (!index) {
|
||||
data.trim();
|
||||
if (data.length()) {
|
||||
DBG("### Unhandled:", data);
|
||||
}
|
||||
data = "";
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
uint8_t waitResponse(uint32_t timeout,
|
||||
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
|
||||
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
|
||||
{
|
||||
String data;
|
||||
return waitResponse(timeout, data, r1, r2, r3, r4, r5);
|
||||
}
|
||||
|
||||
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
|
||||
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
|
||||
{
|
||||
return waitResponse(1000, r1, r2, r3, r4, r5);
|
||||
}
|
||||
|
||||
public:
|
||||
Stream& stream;
|
||||
|
||||
protected:
|
||||
GsmClient* sockets[TINY_GSM_MUX_COUNT];
|
||||
};
|
||||
|
||||
#endif
|
||||
781
lib/MySensors/drivers/TinyGSM/TinyGsmClientM590.h
Normal file
781
lib/MySensors/drivers/TinyGSM/TinyGsmClientM590.h
Normal file
@@ -0,0 +1,781 @@
|
||||
/**
|
||||
* file TinyGsmClientM590.h
|
||||
* author Volodymyr Shymanskyy
|
||||
* license LGPL-3.0
|
||||
* copyright Copyright (c) 2016 Volodymyr Shymanskyy
|
||||
* date Nov 2016
|
||||
*/
|
||||
|
||||
#ifndef TinyGsmClientM590_h
|
||||
#define TinyGsmClientM590_h
|
||||
|
||||
//#define TINY_GSM_DEBUG Serial
|
||||
|
||||
#if !defined(TINY_GSM_RX_BUFFER)
|
||||
#define TINY_GSM_RX_BUFFER 256
|
||||
#endif
|
||||
|
||||
#define TINY_GSM_MUX_COUNT 2
|
||||
|
||||
#include "TinyGsmCommon.h"
|
||||
|
||||
#define GSM_NL "\r\n"
|
||||
static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
|
||||
static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
|
||||
|
||||
enum SimStatus {
|
||||
SIM_ERROR = 0,
|
||||
SIM_READY = 1,
|
||||
SIM_LOCKED = 2,
|
||||
};
|
||||
|
||||
enum RegStatus {
|
||||
REG_UNREGISTERED = 0,
|
||||
REG_SEARCHING = 3,
|
||||
REG_DENIED = 2,
|
||||
REG_OK_HOME = 1,
|
||||
REG_OK_ROAMING = 5,
|
||||
REG_UNKNOWN = 4,
|
||||
};
|
||||
|
||||
|
||||
class TinyGsm
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
class GsmClient : public Client
|
||||
{
|
||||
friend class TinyGsm;
|
||||
typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
|
||||
|
||||
public:
|
||||
GsmClient() {}
|
||||
|
||||
GsmClient(TinyGsm& modem, uint8_t mux = 1)
|
||||
{
|
||||
init(&modem, mux);
|
||||
}
|
||||
|
||||
bool init(TinyGsm* modem, uint8_t mux = 1)
|
||||
{
|
||||
this->at = modem;
|
||||
this->mux = mux;
|
||||
sock_connected = false;
|
||||
|
||||
at->sockets[mux] = this;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual int connect(const char *host, uint16_t port)
|
||||
{
|
||||
stop();
|
||||
TINY_GSM_YIELD();
|
||||
rx.clear();
|
||||
sock_connected = at->modemConnect(host, port, mux);
|
||||
return sock_connected;
|
||||
}
|
||||
|
||||
virtual int connect(IPAddress ip, uint16_t port)
|
||||
{
|
||||
String host;
|
||||
host.reserve(16);
|
||||
host += ip[0];
|
||||
host += ".";
|
||||
host += ip[1];
|
||||
host += ".";
|
||||
host += ip[2];
|
||||
host += ".";
|
||||
host += ip[3];
|
||||
return connect(host.c_str(), port);
|
||||
}
|
||||
|
||||
virtual void stop()
|
||||
{
|
||||
TINY_GSM_YIELD();
|
||||
at->sendAT(GF("+TCPCLOSE="), mux);
|
||||
sock_connected = false;
|
||||
at->waitResponse();
|
||||
rx.clear();
|
||||
}
|
||||
|
||||
virtual size_t write(const uint8_t *buf, size_t size)
|
||||
{
|
||||
TINY_GSM_YIELD();
|
||||
//at->maintain();
|
||||
return at->modemSend(buf, size, mux);
|
||||
}
|
||||
|
||||
virtual size_t write(uint8_t c)
|
||||
{
|
||||
return write(&c, 1);
|
||||
}
|
||||
|
||||
virtual int available()
|
||||
{
|
||||
TINY_GSM_YIELD();
|
||||
if (!rx.size() && sock_connected) {
|
||||
at->maintain();
|
||||
}
|
||||
return rx.size();
|
||||
}
|
||||
|
||||
virtual int read(uint8_t *buf, size_t size)
|
||||
{
|
||||
TINY_GSM_YIELD();
|
||||
size_t cnt = 0;
|
||||
while (cnt < size) {
|
||||
size_t chunk = TinyGsmMin(size-cnt, rx.size());
|
||||
if (chunk > 0) {
|
||||
rx.get(buf, chunk);
|
||||
buf += chunk;
|
||||
cnt += chunk;
|
||||
continue;
|
||||
}
|
||||
// TODO: Read directly into user buffer?
|
||||
if (!rx.size() && sock_connected) {
|
||||
at->maintain();
|
||||
//break;
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
virtual int read()
|
||||
{
|
||||
uint8_t c;
|
||||
if (read(&c, 1) == 1) {
|
||||
return c;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
virtual int peek()
|
||||
{
|
||||
return -1; //TODO
|
||||
}
|
||||
virtual void flush()
|
||||
{
|
||||
at->stream.flush();
|
||||
}
|
||||
|
||||
virtual uint8_t connected()
|
||||
{
|
||||
if (available()) {
|
||||
return true;
|
||||
}
|
||||
return sock_connected;
|
||||
}
|
||||
virtual operator bool()
|
||||
{
|
||||
return connected();
|
||||
}
|
||||
|
||||
/*
|
||||
* Extended API
|
||||
*/
|
||||
|
||||
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
|
||||
private:
|
||||
TinyGsm* at;
|
||||
uint8_t mux;
|
||||
bool sock_connected;
|
||||
RxFifo rx;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
explicit TinyGsm(Stream& stream)
|
||||
: stream(stream)
|
||||
{
|
||||
memset(sockets, 0, sizeof(sockets));
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic functions
|
||||
*/
|
||||
bool begin()
|
||||
{
|
||||
return init();
|
||||
}
|
||||
|
||||
bool init()
|
||||
{
|
||||
if (!testAT()) {
|
||||
return false;
|
||||
}
|
||||
sendAT(GF("&FZE0")); // Factory + Reset + Echo Off
|
||||
if (waitResponse() != 1) {
|
||||
return false;
|
||||
}
|
||||
#ifdef TINY_GSM_DEBUG
|
||||
sendAT(GF("+CMEE=2"));
|
||||
waitResponse();
|
||||
#endif
|
||||
|
||||
getSimStatus();
|
||||
return true;
|
||||
}
|
||||
|
||||
void setBaud(unsigned long baud)
|
||||
{
|
||||
sendAT(GF("+IPR="), baud);
|
||||
}
|
||||
|
||||
bool testAT(unsigned long timeout = 10000L)
|
||||
{
|
||||
for (unsigned long start = millis(); millis() - start < timeout; ) {
|
||||
sendAT(GF(""));
|
||||
if (waitResponse(200) == 1) {
|
||||
delay(100);
|
||||
return true;
|
||||
}
|
||||
delay(100);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void maintain()
|
||||
{
|
||||
//while (stream.available()) {
|
||||
waitResponse(10, NULL, NULL);
|
||||
//}
|
||||
}
|
||||
|
||||
bool factoryDefault()
|
||||
{
|
||||
sendAT(GF("&FZE0&W")); // Factory + Reset + Echo Off + Write
|
||||
waitResponse();
|
||||
sendAT(GF("+ICF=3,1")); // 8 data 0 parity 1 stop
|
||||
waitResponse();
|
||||
sendAT(GF("+ENPWRSAVE=0")); // Disable PWR save
|
||||
waitResponse();
|
||||
sendAT(GF("+XISP=0")); // Use internal stack
|
||||
waitResponse();
|
||||
sendAT(GF("&W")); // Write configuration
|
||||
return waitResponse() == 1;
|
||||
}
|
||||
|
||||
String getModemInfo()
|
||||
{
|
||||
sendAT(GF("I"));
|
||||
String res;
|
||||
if (waitResponse(1000L, res) != 1) {
|
||||
return "";
|
||||
}
|
||||
res.replace(GSM_NL "OK" GSM_NL, "");
|
||||
res.replace(GSM_NL, " ");
|
||||
res.trim();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool hasSSL()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Power functions
|
||||
*/
|
||||
|
||||
bool restart()
|
||||
{
|
||||
if (!testAT()) {
|
||||
return false;
|
||||
}
|
||||
sendAT(GF("+CFUN=15"));
|
||||
if (waitResponse(10000L) != 1) {
|
||||
return false;
|
||||
}
|
||||
//MODEM:STARTUP
|
||||
waitResponse(60000L, GF(GSM_NL "+PBREADY" GSM_NL));
|
||||
return init();
|
||||
}
|
||||
|
||||
bool poweroff()
|
||||
{
|
||||
sendAT(GF("+CPWROFF"));
|
||||
return waitResponse(3000L) == 1;
|
||||
}
|
||||
|
||||
bool radioOff() TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
|
||||
bool sleepEnable(bool enable = true)
|
||||
{
|
||||
sendAT(GF("+ENPWRSAVE="), enable);
|
||||
return waitResponse() == 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* SIM card functions
|
||||
*/
|
||||
|
||||
bool simUnlock(const char *pin)
|
||||
{
|
||||
sendAT(GF("+CPIN=\""), pin, GF("\""));
|
||||
return waitResponse() == 1;
|
||||
}
|
||||
|
||||
String getSimCCID()
|
||||
{
|
||||
sendAT(GF("+CCID"));
|
||||
if (waitResponse(GF(GSM_NL "+CCID:")) != 1) {
|
||||
return "";
|
||||
}
|
||||
String res = stream.readStringUntil('\n');
|
||||
waitResponse();
|
||||
res.trim();
|
||||
return res;
|
||||
}
|
||||
|
||||
String getIMEI()
|
||||
{
|
||||
sendAT(GF("+GSN"));
|
||||
if (waitResponse(GF(GSM_NL)) != 1) {
|
||||
return "";
|
||||
}
|
||||
String res = stream.readStringUntil('\n');
|
||||
waitResponse();
|
||||
res.trim();
|
||||
return res;
|
||||
}
|
||||
|
||||
SimStatus getSimStatus(unsigned long timeout = 10000L)
|
||||
{
|
||||
for (unsigned long start = millis(); millis() - start < timeout; ) {
|
||||
sendAT(GF("+CPIN?"));
|
||||
if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
|
||||
delay(1000);
|
||||
continue;
|
||||
}
|
||||
int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"));
|
||||
waitResponse();
|
||||
switch (status) {
|
||||
case 2:
|
||||
case 3:
|
||||
return SIM_LOCKED;
|
||||
case 1:
|
||||
return SIM_READY;
|
||||
default:
|
||||
return SIM_ERROR;
|
||||
}
|
||||
}
|
||||
return SIM_ERROR;
|
||||
}
|
||||
|
||||
RegStatus getRegistrationStatus()
|
||||
{
|
||||
sendAT(GF("+CREG?"));
|
||||
if (waitResponse(GF(GSM_NL "+CREG:")) != 1) {
|
||||
return REG_UNKNOWN;
|
||||
}
|
||||
streamSkipUntil(','); // Skip format (0)
|
||||
int status = stream.readStringUntil('\n').toInt();
|
||||
waitResponse();
|
||||
return (RegStatus)status;
|
||||
}
|
||||
|
||||
String getOperator()
|
||||
{
|
||||
sendAT(GF("+COPS?"));
|
||||
if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
|
||||
return "";
|
||||
}
|
||||
streamSkipUntil('"'); // Skip mode and format
|
||||
String res = stream.readStringUntil('"');
|
||||
waitResponse();
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic network functions
|
||||
*/
|
||||
|
||||
int getSignalQuality()
|
||||
{
|
||||
sendAT(GF("+CSQ"));
|
||||
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
|
||||
return 99;
|
||||
}
|
||||
int res = stream.readStringUntil(',').toInt();
|
||||
waitResponse();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool isNetworkConnected()
|
||||
{
|
||||
RegStatus s = getRegistrationStatus();
|
||||
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
|
||||
}
|
||||
|
||||
bool waitForNetwork(unsigned long timeout = 60000L)
|
||||
{
|
||||
for (unsigned long start = millis(); millis() - start < timeout; ) {
|
||||
if (isNetworkConnected()) {
|
||||
return true;
|
||||
}
|
||||
delay(250);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* GPRS functions
|
||||
*/
|
||||
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL)
|
||||
{
|
||||
gprsDisconnect();
|
||||
|
||||
sendAT(GF("+XISP=0"));
|
||||
waitResponse();
|
||||
|
||||
sendAT(GF("+CGDCONT=1,\"IP\",\""), apn, '"');
|
||||
waitResponse();
|
||||
|
||||
if (!user) {
|
||||
user = "";
|
||||
}
|
||||
if (!pwd) {
|
||||
pwd = "";
|
||||
}
|
||||
sendAT(GF("+XGAUTH=1,1,\""), user, GF("\",\""), pwd, GF("\""));
|
||||
waitResponse();
|
||||
|
||||
sendAT(GF("+XIIC=1"));
|
||||
waitResponse();
|
||||
|
||||
const unsigned long timeout = 60000L;
|
||||
for (unsigned long start = millis(); millis() - start < timeout; ) {
|
||||
if (isGprsConnected()) {
|
||||
//goto set_dns; // TODO
|
||||
return true;
|
||||
}
|
||||
delay(500);
|
||||
}
|
||||
return false;
|
||||
|
||||
set_dns:
|
||||
sendAT(GF("+DNSSERVER=1,8.8.8.8"));
|
||||
waitResponse();
|
||||
|
||||
sendAT(GF("+DNSSERVER=2,8.8.4.4"));
|
||||
waitResponse();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gprsDisconnect()
|
||||
{
|
||||
// TODO: There is no command in AT command set
|
||||
// XIIC=0 does not work
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isGprsConnected()
|
||||
{
|
||||
sendAT(GF("+XIIC?"));
|
||||
if (waitResponse(GF(GSM_NL "+XIIC:")) != 1) {
|
||||
return false;
|
||||
}
|
||||
int res = stream.readStringUntil(',').toInt();
|
||||
waitResponse();
|
||||
return res == 1;
|
||||
}
|
||||
|
||||
String getLocalIP()
|
||||
{
|
||||
sendAT(GF("+XIIC?"));
|
||||
if (waitResponse(GF(GSM_NL "+XIIC:")) != 1) {
|
||||
return "";
|
||||
}
|
||||
stream.readStringUntil(',');
|
||||
String res = stream.readStringUntil('\n');
|
||||
waitResponse();
|
||||
res.trim();
|
||||
return res;
|
||||
}
|
||||
|
||||
IPAddress localIP()
|
||||
{
|
||||
return TinyGsmIpFromString(getLocalIP());
|
||||
}
|
||||
|
||||
/*
|
||||
* Phone Call functions
|
||||
*/
|
||||
|
||||
bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_AVAILABLE;
|
||||
|
||||
bool callAnswer() TINY_GSM_ATTR_NOT_AVAILABLE;
|
||||
|
||||
bool callNumber(const String& number) TINY_GSM_ATTR_NOT_AVAILABLE;
|
||||
|
||||
bool callHangup() TINY_GSM_ATTR_NOT_AVAILABLE;
|
||||
|
||||
/*
|
||||
* Messaging functions
|
||||
*/
|
||||
|
||||
String sendUSSD(const String& code)
|
||||
{
|
||||
sendAT(GF("+CMGF=1"));
|
||||
waitResponse();
|
||||
sendAT(GF("+CSCS=\"HEX\""));
|
||||
waitResponse();
|
||||
sendAT(GF("D"), code);
|
||||
if (waitResponse(10000L, GF(GSM_NL "+CUSD:")) != 1) {
|
||||
return "";
|
||||
}
|
||||
stream.readStringUntil('"');
|
||||
String hex = stream.readStringUntil('"');
|
||||
stream.readStringUntil(',');
|
||||
int dcs = stream.readStringUntil('\n').toInt();
|
||||
|
||||
if (waitResponse() != 1) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (dcs == 15) {
|
||||
return TinyGsmDecodeHex8bit(hex);
|
||||
} else if (dcs == 72) {
|
||||
return TinyGsmDecodeHex16bit(hex);
|
||||
} else {
|
||||
return hex;
|
||||
}
|
||||
}
|
||||
|
||||
bool sendSMS(const String& number, const String& text)
|
||||
{
|
||||
sendAT(GF("+CSCS=\"GSM\""));
|
||||
waitResponse();
|
||||
sendAT(GF("+CMGF=1"));
|
||||
waitResponse();
|
||||
sendAT(GF("+CMGS=\""), number, GF("\""));
|
||||
if (waitResponse(GF(">")) != 1) {
|
||||
return false;
|
||||
}
|
||||
stream.print(text);
|
||||
stream.write((char)0x1A);
|
||||
stream.flush();
|
||||
return waitResponse(60000L) == 1;
|
||||
}
|
||||
|
||||
bool sendSMS_UTF16(const String& number, const void* text, size_t len)
|
||||
TINY_GSM_ATTR_NOT_AVAILABLE;
|
||||
|
||||
/*
|
||||
* Location functions
|
||||
*/
|
||||
|
||||
String getGsmLocation() TINY_GSM_ATTR_NOT_AVAILABLE;
|
||||
|
||||
/*
|
||||
* Battery functions
|
||||
*/
|
||||
|
||||
uint16_t getBattVoltage() TINY_GSM_ATTR_NOT_AVAILABLE;
|
||||
|
||||
int getBattPercent() TINY_GSM_ATTR_NOT_AVAILABLE;
|
||||
|
||||
protected:
|
||||
|
||||
bool modemConnect(const char* host, uint16_t port, uint8_t mux)
|
||||
{
|
||||
for (int i=0; i<3; i++) { // TODO: no need for loop?
|
||||
String ip = dnsIpQuery(host);
|
||||
|
||||
sendAT(GF("+TCPSETUP="), mux, GF(","), ip, GF(","), port);
|
||||
int rsp = waitResponse(75000L,
|
||||
GF(",OK" GSM_NL),
|
||||
GF(",FAIL" GSM_NL),
|
||||
GF("+TCPSETUP:Error" GSM_NL));
|
||||
if (1 == rsp) {
|
||||
return true;
|
||||
} else if (3 == rsp) {
|
||||
sendAT(GF("+TCPCLOSE="), mux);
|
||||
waitResponse();
|
||||
}
|
||||
delay(1000);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int modemSend(const void* buff, size_t len, uint8_t mux)
|
||||
{
|
||||
sendAT(GF("+TCPSEND="), mux, ',', len);
|
||||
if (waitResponse(GF(">")) != 1) {
|
||||
return 0;
|
||||
}
|
||||
stream.write((uint8_t*)buff, len);
|
||||
stream.write((char)0x0D);
|
||||
stream.flush();
|
||||
if (waitResponse(30000L, GF(GSM_NL "+TCPSEND:")) != 1) {
|
||||
return 0;
|
||||
}
|
||||
stream.readStringUntil('\n');
|
||||
return len;
|
||||
}
|
||||
|
||||
bool modemGetConnected(uint8_t mux)
|
||||
{
|
||||
sendAT(GF("+CIPSTATUS="), mux);
|
||||
int res = waitResponse(GF(",\"CONNECTED\""), GF(",\"CLOSED\""), GF(",\"CLOSING\""),
|
||||
GF(",\"INITIAL\""));
|
||||
waitResponse();
|
||||
return 1 == res;
|
||||
}
|
||||
|
||||
String dnsIpQuery(const char* host)
|
||||
{
|
||||
sendAT(GF("+DNS=\""), host, GF("\""));
|
||||
if (waitResponse(10000L, GF(GSM_NL "+DNS:")) != 1) {
|
||||
return "";
|
||||
}
|
||||
String res = stream.readStringUntil('\n');
|
||||
waitResponse(GF("+DNS:OK" GSM_NL));
|
||||
res.trim();
|
||||
return res;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/* Utilities */
|
||||
|
||||
template<typename T>
|
||||
void streamWrite(T last)
|
||||
{
|
||||
stream.print(last);
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
void streamWrite(T head, Args... tail)
|
||||
{
|
||||
stream.print(head);
|
||||
streamWrite(tail...);
|
||||
}
|
||||
|
||||
bool streamSkipUntil(char c) //TODO: timeout
|
||||
{
|
||||
while (true) {
|
||||
while (!stream.available()) {
|
||||
TINY_GSM_YIELD();
|
||||
}
|
||||
if (stream.read() == c) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void sendAT(Args... cmd)
|
||||
{
|
||||
streamWrite("AT", cmd..., GSM_NL);
|
||||
stream.flush();
|
||||
TINY_GSM_YIELD();
|
||||
//DBG("### AT:", cmd...);
|
||||
}
|
||||
|
||||
// TODO: Optimize this!
|
||||
uint8_t waitResponse(uint32_t timeout, String& data,
|
||||
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
|
||||
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
|
||||
{
|
||||
/*String r1s(r1); r1s.trim();
|
||||
String r2s(r2); r2s.trim();
|
||||
String r3s(r3); r3s.trim();
|
||||
String r4s(r4); r4s.trim();
|
||||
String r5s(r5); r5s.trim();
|
||||
DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
|
||||
data.reserve(64);
|
||||
int index = 0;
|
||||
unsigned long startMillis = millis();
|
||||
do {
|
||||
TINY_GSM_YIELD();
|
||||
while (stream.available() > 0) {
|
||||
int a = stream.read();
|
||||
if (a <= 0) {
|
||||
continue; // Skip 0x00 bytes, just in case
|
||||
}
|
||||
data += (char)a;
|
||||
if (r1 && data.endsWith(r1)) {
|
||||
index = 1;
|
||||
goto finish;
|
||||
} else if (r2 && data.endsWith(r2)) {
|
||||
index = 2;
|
||||
goto finish;
|
||||
} else if (r3 && data.endsWith(r3)) {
|
||||
index = 3;
|
||||
goto finish;
|
||||
} else if (r4 && data.endsWith(r4)) {
|
||||
index = 4;
|
||||
goto finish;
|
||||
} else if (r5 && data.endsWith(r5)) {
|
||||
index = 5;
|
||||
goto finish;
|
||||
} else if (data.endsWith(GF("+TCPRECV:"))) {
|
||||
int mux = stream.readStringUntil(',').toInt();
|
||||
int len = stream.readStringUntil(',').toInt();
|
||||
int len_orig = len;
|
||||
if (len > sockets[mux]->rx.free()) {
|
||||
DBG("### Buffer overflow: ", len, "->", sockets[mux]->rx.free());
|
||||
} else {
|
||||
DBG("### Got: ", len, "->", sockets[mux]->rx.free());
|
||||
}
|
||||
while (len--) {
|
||||
while (!stream.available()) {
|
||||
TINY_GSM_YIELD();
|
||||
}
|
||||
sockets[mux]->rx.put(stream.read());
|
||||
}
|
||||
if (len_orig > sockets[mux]->available()) { // TODO
|
||||
DBG("### Fewer characters received than expected: ", sockets[mux]->available(), " vs ", len_orig);
|
||||
}
|
||||
data = "";
|
||||
} else if (data.endsWith(GF("+TCPCLOSE:"))) {
|
||||
int mux = stream.readStringUntil(',').toInt();
|
||||
stream.readStringUntil('\n');
|
||||
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT) {
|
||||
sockets[mux]->sock_connected = false;
|
||||
}
|
||||
data = "";
|
||||
DBG("### Closed: ", mux);
|
||||
}
|
||||
}
|
||||
} while (millis() - startMillis < timeout);
|
||||
finish:
|
||||
if (!index) {
|
||||
data.trim();
|
||||
if (data.length()) {
|
||||
DBG("### Unhandled:", data);
|
||||
}
|
||||
data = "";
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
uint8_t waitResponse(uint32_t timeout,
|
||||
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
|
||||
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
|
||||
{
|
||||
String data;
|
||||
return waitResponse(timeout, data, r1, r2, r3, r4, r5);
|
||||
}
|
||||
|
||||
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
|
||||
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
|
||||
{
|
||||
return waitResponse(1000, r1, r2, r3, r4, r5);
|
||||
}
|
||||
|
||||
public:
|
||||
Stream& stream;
|
||||
|
||||
protected:
|
||||
GsmClient* sockets[TINY_GSM_MUX_COUNT];
|
||||
};
|
||||
|
||||
#endif
|
||||
1057
lib/MySensors/drivers/TinyGSM/TinyGsmClientSIM800.h
Normal file
1057
lib/MySensors/drivers/TinyGSM/TinyGsmClientSIM800.h
Normal file
File diff suppressed because it is too large
Load Diff
163
lib/MySensors/drivers/TinyGSM/TinyGsmClientSIM808.h
Normal file
163
lib/MySensors/drivers/TinyGSM/TinyGsmClientSIM808.h
Normal file
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* file TinyGsmClientSIM808.h
|
||||
* author Volodymyr Shymanskyy
|
||||
* license LGPL-3.0
|
||||
* copyright Copyright (c) 2016 Volodymyr Shymanskyy
|
||||
* date Nov 2016
|
||||
*/
|
||||
|
||||
#ifndef TinyGsmClientSIM808_h
|
||||
#define TinyGsmClientSIM808_h
|
||||
|
||||
#include "TinyGsmClientSIM800.h"
|
||||
|
||||
class TinyGsmSim808: public TinyGsmSim800
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
explicit TinyGsmSim808(Stream& stream)
|
||||
: TinyGsmSim800(stream)
|
||||
{}
|
||||
|
||||
/*
|
||||
* GPS location functions
|
||||
*/
|
||||
|
||||
// enable GPS
|
||||
bool enableGPS()
|
||||
{
|
||||
uint16_t state;
|
||||
|
||||
sendAT(GF("+CGNSPWR=1"));
|
||||
if (waitResponse() != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool disableGPS()
|
||||
{
|
||||
uint16_t state;
|
||||
|
||||
sendAT(GF("+CGNSPWR=0"));
|
||||
if (waitResponse() != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// get the RAW GPS output
|
||||
// works only with ans SIM808 V2
|
||||
String getGPSraw()
|
||||
{
|
||||
sendAT(GF("+CGNSINF"));
|
||||
if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) {
|
||||
return "";
|
||||
}
|
||||
String res = stream.readStringUntil('\n');
|
||||
waitResponse();
|
||||
res.trim();
|
||||
return res;
|
||||
}
|
||||
|
||||
// get GPS informations
|
||||
// works only with ans SIM808 V2
|
||||
bool getGPS(float *lat, float *lon, float *speed=0, int *alt=0, int *vsat=0, int *usat=0)
|
||||
{
|
||||
//String buffer = "";
|
||||
char chr_buffer[12];
|
||||
bool fix = false;
|
||||
|
||||
sendAT(GF("+CGNSINF"));
|
||||
if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
stream.readStringUntil(','); // mode
|
||||
if ( stream.readStringUntil(',').toInt() == 1 ) {
|
||||
fix = true;
|
||||
}
|
||||
stream.readStringUntil(','); //utctime
|
||||
*lat = stream.readStringUntil(',').toFloat(); //lat
|
||||
*lon = stream.readStringUntil(',').toFloat(); //lon
|
||||
if (alt != NULL) {
|
||||
*alt = stream.readStringUntil(',').toFloat(); //lon
|
||||
}
|
||||
if (speed != NULL) {
|
||||
*speed = stream.readStringUntil(',').toFloat(); //speed
|
||||
}
|
||||
stream.readStringUntil(',');
|
||||
stream.readStringUntil(',');
|
||||
stream.readStringUntil(',');
|
||||
stream.readStringUntil(',');
|
||||
stream.readStringUntil(',');
|
||||
stream.readStringUntil(',');
|
||||
stream.readStringUntil(',');
|
||||
if (vsat != NULL) {
|
||||
*vsat = stream.readStringUntil(',').toInt(); //viewed satelites
|
||||
}
|
||||
if (usat != NULL) {
|
||||
*usat = stream.readStringUntil(',').toInt(); //used satelites
|
||||
}
|
||||
stream.readStringUntil('\n');
|
||||
|
||||
waitResponse();
|
||||
|
||||
return fix;
|
||||
}
|
||||
|
||||
// get GPS time
|
||||
// works only with SIM808 V2
|
||||
bool getGPSTime(int *year, int *month, int *day, int *hour, int *minute, int *second)
|
||||
{
|
||||
bool fix = false;
|
||||
char chr_buffer[12];
|
||||
sendAT(GF("+CGNSINF"));
|
||||
if (waitResponse(GF(GSM_NL "+CGNSINF:")) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
String buffer = stream.readStringUntil(',');
|
||||
buffer.toCharArray(chr_buffer, sizeof(chr_buffer));
|
||||
switch (i) {
|
||||
case 0:
|
||||
//mode
|
||||
break;
|
||||
case 1:
|
||||
//fixstatus
|
||||
if ( buffer.toInt() == 1 ) {
|
||||
fix = buffer.toInt();
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
*year = buffer.substring(0,4).toInt();
|
||||
*month = buffer.substring(4,6).toInt();
|
||||
*day = buffer.substring(6,8).toInt();
|
||||
*hour = buffer.substring(8,10).toInt();
|
||||
*minute = buffer.substring(10,12).toInt();
|
||||
*second = buffer.substring(12,14).toInt();
|
||||
break;
|
||||
|
||||
default:
|
||||
// if nothing else matches, do the default
|
||||
// default is optional
|
||||
break;
|
||||
}
|
||||
}
|
||||
String res = stream.readStringUntil('\n');
|
||||
waitResponse();
|
||||
|
||||
if (fix) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
789
lib/MySensors/drivers/TinyGSM/TinyGsmClientU201.h
Normal file
789
lib/MySensors/drivers/TinyGSM/TinyGsmClientU201.h
Normal file
@@ -0,0 +1,789 @@
|
||||
/**
|
||||
* file TinyGsmClientU201.h
|
||||
* author Volodymyr Shymanskyy
|
||||
* license LGPL-3.0
|
||||
* copyright Copyright (c) 2016 Volodymyr Shymanskyy
|
||||
* date Nov 2016
|
||||
*/
|
||||
|
||||
#ifndef TinyGsmClientU201_h
|
||||
#define TinyGsmClientU201_h
|
||||
|
||||
//#define TINY_GSM_DEBUG Serial
|
||||
|
||||
#if !defined(TINY_GSM_RX_BUFFER)
|
||||
#define TINY_GSM_RX_BUFFER 64
|
||||
#endif
|
||||
|
||||
#define TINY_GSM_MUX_COUNT 5
|
||||
|
||||
#include "TinyGsmCommon.h"
|
||||
|
||||
#define GSM_NL "\r\n"
|
||||
static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
|
||||
static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
|
||||
|
||||
enum SimStatus {
|
||||
SIM_ERROR = 0,
|
||||
SIM_READY = 1,
|
||||
SIM_LOCKED = 2,
|
||||
};
|
||||
|
||||
enum RegStatus {
|
||||
REG_UNREGISTERED = 0,
|
||||
REG_SEARCHING = 2,
|
||||
REG_DENIED = 3,
|
||||
REG_OK_HOME = 1,
|
||||
REG_OK_ROAMING = 5,
|
||||
REG_UNKNOWN = 4,
|
||||
};
|
||||
|
||||
|
||||
class TinyGsmU201
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
class GsmClient : public Client
|
||||
{
|
||||
friend class TinyGsmU201;
|
||||
typedef TinyGsmFifo<uint8_t, TINY_GSM_RX_BUFFER> RxFifo;
|
||||
|
||||
public:
|
||||
GsmClient() {}
|
||||
|
||||
GsmClient(TinyGsmU201& modem, uint8_t mux = 1)
|
||||
{
|
||||
init(&modem, mux);
|
||||
}
|
||||
|
||||
bool init(TinyGsmU201* modem, uint8_t mux = 1)
|
||||
{
|
||||
this->at = modem;
|
||||
this->mux = mux;
|
||||
sock_available = 0;
|
||||
sock_connected = false;
|
||||
got_data = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual int connect(const char *host, uint16_t port)
|
||||
{
|
||||
stop();
|
||||
TINY_GSM_YIELD();
|
||||
rx.clear();
|
||||
sock_connected = at->modemConnect(host, port, &mux);
|
||||
at->sockets[mux] = this;
|
||||
return sock_connected;
|
||||
}
|
||||
|
||||
virtual int connect(IPAddress ip, uint16_t port)
|
||||
{
|
||||
String host;
|
||||
host.reserve(16);
|
||||
host += ip[0];
|
||||
host += ".";
|
||||
host += ip[1];
|
||||
host += ".";
|
||||
host += ip[2];
|
||||
host += ".";
|
||||
host += ip[3];
|
||||
return connect(host.c_str(), port);
|
||||
}
|
||||
|
||||
virtual void stop()
|
||||
{
|
||||
TINY_GSM_YIELD();
|
||||
at->sendAT(GF("+USOCL="), mux);
|
||||
sock_connected = false;
|
||||
at->waitResponse();
|
||||
rx.clear();
|
||||
}
|
||||
|
||||
virtual size_t write(const uint8_t *buf, size_t size)
|
||||
{
|
||||
TINY_GSM_YIELD();
|
||||
at->maintain();
|
||||
return at->modemSend(buf, size, mux);
|
||||
}
|
||||
|
||||
virtual size_t write(uint8_t c)
|
||||
{
|
||||
return write(&c, 1);
|
||||
}
|
||||
|
||||
virtual int available()
|
||||
{
|
||||
TINY_GSM_YIELD();
|
||||
if (!rx.size() && sock_connected) {
|
||||
at->maintain();
|
||||
}
|
||||
return rx.size() + sock_available;
|
||||
}
|
||||
|
||||
virtual int read(uint8_t *buf, size_t size)
|
||||
{
|
||||
TINY_GSM_YIELD();
|
||||
at->maintain();
|
||||
size_t cnt = 0;
|
||||
while (cnt < size) {
|
||||
size_t chunk = TinyGsmMin(size-cnt, rx.size());
|
||||
if (chunk > 0) {
|
||||
rx.get(buf, chunk);
|
||||
buf += chunk;
|
||||
cnt += chunk;
|
||||
continue;
|
||||
}
|
||||
// TODO: Read directly into user buffer?
|
||||
at->maintain();
|
||||
if (sock_available > 0) {
|
||||
at->modemRead(rx.free(), mux);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
virtual int read()
|
||||
{
|
||||
uint8_t c;
|
||||
if (read(&c, 1) == 1) {
|
||||
return c;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
virtual int peek()
|
||||
{
|
||||
return -1; //TODO
|
||||
}
|
||||
virtual void flush()
|
||||
{
|
||||
at->stream.flush();
|
||||
}
|
||||
|
||||
virtual uint8_t connected()
|
||||
{
|
||||
if (available()) {
|
||||
return true;
|
||||
}
|
||||
return sock_connected;
|
||||
}
|
||||
virtual operator bool()
|
||||
{
|
||||
return connected();
|
||||
}
|
||||
|
||||
/*
|
||||
* Extended API
|
||||
*/
|
||||
|
||||
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
|
||||
private:
|
||||
TinyGsmU201* at;
|
||||
uint8_t mux;
|
||||
uint16_t sock_available;
|
||||
bool sock_connected;
|
||||
bool got_data;
|
||||
RxFifo rx;
|
||||
};
|
||||
|
||||
class GsmClientSecure : public GsmClient
|
||||
{
|
||||
public:
|
||||
GsmClientSecure() {}
|
||||
|
||||
GsmClientSecure(TinyGsmU201& modem, uint8_t mux = 1)
|
||||
: GsmClient(modem, mux)
|
||||
{}
|
||||
|
||||
public:
|
||||
virtual int connect(const char *host, uint16_t port)
|
||||
{
|
||||
stop();
|
||||
TINY_GSM_YIELD();
|
||||
rx.clear();
|
||||
sock_connected = at->modemConnect(host, port, &mux, true);
|
||||
at->sockets[mux] = this;
|
||||
return sock_connected;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
#ifdef GSM_DEFAULT_STREAM
|
||||
explicit TinyGsmU201(Stream& stream = GSM_DEFAULT_STREAM)
|
||||
#else
|
||||
explicit TinyGsmU201(Stream& stream)
|
||||
#endif
|
||||
: stream(stream)
|
||||
{
|
||||
memset(sockets, 0, sizeof(sockets));
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic functions
|
||||
*/
|
||||
bool begin(const char* pin = NULL)
|
||||
{
|
||||
return init(pin);
|
||||
}
|
||||
|
||||
bool init(const char* pin = NULL)
|
||||
{
|
||||
if (!testAT()) {
|
||||
return false;
|
||||
}
|
||||
sendAT(GF("E0")); // Echo Off
|
||||
if (waitResponse() != 1) {
|
||||
return false;
|
||||
}
|
||||
int ret = getSimStatus();
|
||||
if (ret != SIM_READY && pin != NULL && strlen(pin) > 0) {
|
||||
simUnlock(pin);
|
||||
}
|
||||
return (getSimStatus() == SIM_READY);
|
||||
}
|
||||
|
||||
void setBaud(unsigned long baud)
|
||||
{
|
||||
sendAT(GF("+IPR="), baud);
|
||||
}
|
||||
|
||||
bool testAT(unsigned long timeout = 10000L)
|
||||
{
|
||||
for (unsigned long start = millis(); millis() - start < timeout; ) {
|
||||
sendAT(GF(""));
|
||||
if (waitResponse(200) == 1) {
|
||||
delay(100);
|
||||
return true;
|
||||
}
|
||||
delay(100);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void maintain()
|
||||
{
|
||||
for (int mux = 0; mux < TINY_GSM_MUX_COUNT; mux++) {
|
||||
GsmClient* sock = sockets[mux];
|
||||
if (sock && sock->got_data) {
|
||||
sock->got_data = false;
|
||||
sock->sock_available = modemGetAvailable(mux);
|
||||
}
|
||||
}
|
||||
while (stream.available()) {
|
||||
waitResponse(10, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
bool factoryDefault()
|
||||
{
|
||||
sendAT(GF("+UFACTORY=0,1")); // Factory + Reset + Echo Off
|
||||
waitResponse();
|
||||
sendAT(GF("+CFUN=16")); // Auto-baud
|
||||
return waitResponse() == 1;
|
||||
}
|
||||
|
||||
String getModemInfo() TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
|
||||
bool hasSSL()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Power functions
|
||||
*/
|
||||
|
||||
bool restart()
|
||||
{
|
||||
if (!testAT()) {
|
||||
return false;
|
||||
}
|
||||
sendAT(GF("+CFUN=16"));
|
||||
if (waitResponse(10000L) != 1) {
|
||||
return false;
|
||||
}
|
||||
delay(3000);
|
||||
return init();
|
||||
}
|
||||
|
||||
bool poweroff() TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
|
||||
/*
|
||||
* SIM card functions
|
||||
*/
|
||||
|
||||
bool simUnlock(const char *pin)
|
||||
{
|
||||
sendAT(GF("+CPIN=\""), pin, GF("\""));
|
||||
return waitResponse() == 1;
|
||||
}
|
||||
|
||||
String getSimCCID()
|
||||
{
|
||||
sendAT(GF("+CCID"));
|
||||
if (waitResponse(GF(GSM_NL "+CCID:")) != 1) {
|
||||
return "";
|
||||
}
|
||||
String res = stream.readStringUntil('\n');
|
||||
waitResponse();
|
||||
res.trim();
|
||||
return res;
|
||||
}
|
||||
|
||||
String getIMEI()
|
||||
{
|
||||
sendAT(GF("+CGSN"));
|
||||
if (waitResponse(GF(GSM_NL)) != 1) {
|
||||
return "";
|
||||
}
|
||||
String res = stream.readStringUntil('\n');
|
||||
waitResponse();
|
||||
res.trim();
|
||||
return res;
|
||||
}
|
||||
|
||||
SimStatus getSimStatus(unsigned long timeout = 10000L)
|
||||
{
|
||||
for (unsigned long start = millis(); millis() - start < timeout; ) {
|
||||
sendAT(GF("+CPIN?"));
|
||||
if (waitResponse(GF(GSM_NL "+CPIN:")) != 1) {
|
||||
delay(1000);
|
||||
continue;
|
||||
}
|
||||
int status = waitResponse(GF("READY"), GF("SIM PIN"), GF("SIM PUK"), GF("NOT INSERTED"));
|
||||
waitResponse();
|
||||
switch (status) {
|
||||
case 2:
|
||||
case 3:
|
||||
return SIM_LOCKED;
|
||||
case 1:
|
||||
return SIM_READY;
|
||||
default:
|
||||
return SIM_ERROR;
|
||||
}
|
||||
}
|
||||
return SIM_ERROR;
|
||||
}
|
||||
|
||||
RegStatus getRegistrationStatus()
|
||||
{
|
||||
sendAT(GF("+CGREG?"));
|
||||
if (waitResponse(GF(GSM_NL "+CGREG:")) != 1) {
|
||||
return REG_UNKNOWN;
|
||||
}
|
||||
streamSkipUntil(','); // Skip format (0)
|
||||
int status = stream.readStringUntil('\n').toInt();
|
||||
waitResponse();
|
||||
return (RegStatus)status;
|
||||
}
|
||||
|
||||
String getOperator()
|
||||
{
|
||||
sendAT(GF("+COPS?"));
|
||||
if (waitResponse(GF(GSM_NL "+COPS:")) != 1) {
|
||||
return "";
|
||||
}
|
||||
streamSkipUntil('"'); // Skip mode and format
|
||||
String res = stream.readStringUntil('"');
|
||||
waitResponse();
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic network functions
|
||||
*/
|
||||
|
||||
int getSignalQuality()
|
||||
{
|
||||
sendAT(GF("+CSQ"));
|
||||
if (waitResponse(GF(GSM_NL "+CSQ:")) != 1) {
|
||||
return 99;
|
||||
}
|
||||
int res = stream.readStringUntil(',').toInt();
|
||||
waitResponse();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool isNetworkConnected()
|
||||
{
|
||||
RegStatus s = getRegistrationStatus();
|
||||
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
|
||||
}
|
||||
|
||||
bool waitForNetwork(unsigned long timeout = 60000L)
|
||||
{
|
||||
for (unsigned long start = millis(); millis() - start < timeout; ) {
|
||||
if (isNetworkConnected()) {
|
||||
return true;
|
||||
}
|
||||
delay(250);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* GPRS functions
|
||||
*/
|
||||
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL)
|
||||
{
|
||||
gprsDisconnect();
|
||||
|
||||
sendAT(GF("+CGATT=1"));
|
||||
waitResponse(5000L);
|
||||
|
||||
sendAT(GF("+UPSD=0,1,\""), apn, '"');
|
||||
waitResponse();
|
||||
|
||||
if (user && strlen(user) > 0) {
|
||||
sendAT(GF("+UPSD=0,2,\""), user, '"');
|
||||
waitResponse();
|
||||
}
|
||||
if (pwd && strlen(pwd) > 0) {
|
||||
sendAT(GF("+UPSD=0,3,\""), pwd, '"');
|
||||
waitResponse();
|
||||
}
|
||||
|
||||
sendAT(GF("+UPSD=0,7,\"0.0.0.0\"")); // Dynamic IP
|
||||
waitResponse();
|
||||
|
||||
sendAT(GF("+UPSDA=0,3"));
|
||||
waitResponse(6000L);
|
||||
|
||||
// Open a GPRS context
|
||||
sendAT(GF("+UPSND=0,8"));
|
||||
if (waitResponse(GF(",8,1")) != 1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gprsDisconnect()
|
||||
{
|
||||
sendAT(GF("+UPSDA=0,4"));
|
||||
if (waitResponse(60000L) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sendAT(GF("+CGATT=0"));
|
||||
if (waitResponse(60000L) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isGprsConnected()
|
||||
{
|
||||
sendAT(GF("+CGATT?"));
|
||||
if (waitResponse(GF(GSM_NL "+CGATT:")) != 1) {
|
||||
return false;
|
||||
}
|
||||
int res = stream.readStringUntil('\n').toInt();
|
||||
waitResponse();
|
||||
if (res != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sendAT(GF("+CIFSR"));
|
||||
if (waitResponse() != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String getLocalIP()
|
||||
{
|
||||
sendAT(GF("+CIFSR;E0"));
|
||||
String res;
|
||||
if (waitResponse(10000L, res) != 1) {
|
||||
return "";
|
||||
}
|
||||
res.trim();
|
||||
return res;
|
||||
}
|
||||
|
||||
IPAddress localIP()
|
||||
{
|
||||
return TinyGsmIpFromString(getLocalIP());
|
||||
}
|
||||
|
||||
/*
|
||||
* Phone Call functions
|
||||
*/
|
||||
|
||||
bool setGsmBusy(bool busy = true) TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
|
||||
bool callAnswer() TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
|
||||
bool callNumber(const String& number) TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
|
||||
bool callHangup() TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
|
||||
/*
|
||||
* Messaging functions
|
||||
*/
|
||||
|
||||
String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
|
||||
bool sendSMS(const String& number, const String& text) TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
|
||||
bool sendSMS_UTF16(const String& number, const void* text,
|
||||
size_t len) TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
|
||||
|
||||
/*
|
||||
* Location functions
|
||||
*/
|
||||
|
||||
String getGsmLocation()
|
||||
{
|
||||
sendAT(GF("+ULOC=2,3,0,120,1"));
|
||||
if (waitResponse(GF(GSM_NL "+UULOC:")) != 1) {
|
||||
return "";
|
||||
}
|
||||
String res = stream.readStringUntil('\n');
|
||||
waitResponse();
|
||||
res.trim();
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Battery functions
|
||||
*/
|
||||
// Use: float vBatt = modem.getBattVoltage() / 1000.0;
|
||||
uint16_t getBattVoltage()
|
||||
{
|
||||
sendAT(GF("+CIND"));
|
||||
if (waitResponse(GF(GSM_NL "+CIND:")) != 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t res = stream.readStringUntil(',').toInt();
|
||||
waitResponse();
|
||||
return res;
|
||||
}
|
||||
|
||||
int getBattPercent() TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
|
||||
protected:
|
||||
|
||||
bool modemConnect(const char* host, uint16_t port, uint8_t* mux, bool ssl = false)
|
||||
{
|
||||
sendAT(GF("+USOCR=6"));
|
||||
if (waitResponse(GF(GSM_NL "+USOCR:")) != 1) {
|
||||
return false;
|
||||
}
|
||||
*mux = stream.readStringUntil('\n').toInt();
|
||||
waitResponse();
|
||||
|
||||
if (ssl) {
|
||||
sendAT(GF("+USOSEC="), *mux, ",1");
|
||||
waitResponse();
|
||||
}
|
||||
|
||||
sendAT(GF("+USOCO="), *mux, ",\"", host, "\",", port);
|
||||
int rsp = waitResponse(75000L);
|
||||
return (1 == rsp);
|
||||
}
|
||||
|
||||
int modemSend(const void* buff, size_t len, uint8_t mux)
|
||||
{
|
||||
sendAT(GF("+USOWR="), mux, ',', len);
|
||||
if (waitResponse(GF("@")) != 1) {
|
||||
return -1;
|
||||
}
|
||||
// 50ms delay, see AT manual section 25.10.4
|
||||
delay(50);
|
||||
stream.write((uint8_t*)buff, len);
|
||||
stream.flush();
|
||||
if (waitResponse(GF(GSM_NL "+USOWR:")) != 1) {
|
||||
return -1;
|
||||
}
|
||||
streamSkipUntil(','); // Skip mux
|
||||
return stream.readStringUntil('\n').toInt();
|
||||
}
|
||||
|
||||
size_t modemRead(size_t size, uint8_t mux)
|
||||
{
|
||||
sendAT(GF("+USORD="), mux, ',', size);
|
||||
if (waitResponse(GF(GSM_NL "+USORD:")) != 1) {
|
||||
return 0;
|
||||
}
|
||||
streamSkipUntil(','); // Skip mux
|
||||
size_t len = stream.readStringUntil(',').toInt();
|
||||
streamSkipUntil('\"');
|
||||
|
||||
for (size_t i=0; i<len; i++) {
|
||||
while (!stream.available()) {
|
||||
TINY_GSM_YIELD();
|
||||
}
|
||||
char c = stream.read();
|
||||
sockets[mux]->rx.put(c);
|
||||
}
|
||||
streamSkipUntil('\"');
|
||||
waitResponse();
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t modemGetAvailable(uint8_t mux)
|
||||
{
|
||||
sendAT(GF("+USORD="), mux, ',', 0);
|
||||
size_t result = 0;
|
||||
if (waitResponse(GF(GSM_NL "+USORD:")) == 1) {
|
||||
streamSkipUntil(','); // Skip mux
|
||||
result = stream.readStringUntil('\n').toInt();
|
||||
waitResponse();
|
||||
}
|
||||
if (!result) {
|
||||
sockets[mux]->sock_connected = modemGetConnected(mux);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool modemGetConnected(uint8_t mux)
|
||||
{
|
||||
sendAT(GF("+USOCTL="), mux, ",10");
|
||||
if (waitResponse(GF(GSM_NL "+USOCTL:")) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
streamSkipUntil(','); // Skip mux
|
||||
streamSkipUntil(','); // Skip type
|
||||
int result = stream.readStringUntil('\n').toInt();
|
||||
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/* Utilities */
|
||||
|
||||
template<typename T>
|
||||
void streamWrite(T last)
|
||||
{
|
||||
stream.print(last);
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
void streamWrite(T head, Args... tail)
|
||||
{
|
||||
stream.print(head);
|
||||
streamWrite(tail...);
|
||||
}
|
||||
|
||||
bool streamSkipUntil(char c) //TODO: timeout
|
||||
{
|
||||
while (true) {
|
||||
while (!stream.available()) {
|
||||
TINY_GSM_YIELD();
|
||||
}
|
||||
if (stream.read() == c) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void sendAT(Args... cmd)
|
||||
{
|
||||
streamWrite("AT", cmd..., GSM_NL);
|
||||
stream.flush();
|
||||
TINY_GSM_YIELD();
|
||||
//DBG("### AT:", cmd...);
|
||||
}
|
||||
|
||||
// TODO: Optimize this!
|
||||
uint8_t waitResponse(uint32_t timeout, String& data,
|
||||
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
|
||||
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
|
||||
{
|
||||
/*String r1s(r1); r1s.trim();
|
||||
String r2s(r2); r2s.trim();
|
||||
String r3s(r3); r3s.trim();
|
||||
String r4s(r4); r4s.trim();
|
||||
String r5s(r5); r5s.trim();
|
||||
DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
|
||||
data.reserve(64);
|
||||
int index = 0;
|
||||
unsigned long startMillis = millis();
|
||||
do {
|
||||
TINY_GSM_YIELD();
|
||||
while (stream.available() > 0) {
|
||||
int a = stream.read();
|
||||
if (a < 0) {
|
||||
continue;
|
||||
}
|
||||
data += (char)a;
|
||||
if (r1 && data.endsWith(r1)) {
|
||||
index = 1;
|
||||
goto finish;
|
||||
} else if (r2 && data.endsWith(r2)) {
|
||||
index = 2;
|
||||
goto finish;
|
||||
} else if (r3 && data.endsWith(r3)) {
|
||||
index = 3;
|
||||
goto finish;
|
||||
} else if (r4 && data.endsWith(r4)) {
|
||||
index = 4;
|
||||
goto finish;
|
||||
} else if (r5 && data.endsWith(r5)) {
|
||||
index = 5;
|
||||
goto finish;
|
||||
} else if (data.endsWith(GF(GSM_NL "+UUSORD:"))) {
|
||||
int mux = stream.readStringUntil(',').toInt();
|
||||
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
|
||||
sockets[mux]->got_data = true;
|
||||
}
|
||||
data = "";
|
||||
} else if (data.endsWith(GF(GSM_NL "+UUSOCL:"))) {
|
||||
int mux = stream.readStringUntil('\n').toInt();
|
||||
if (mux >= 0 && mux < TINY_GSM_MUX_COUNT && sockets[mux]) {
|
||||
sockets[mux]->sock_connected = false;
|
||||
}
|
||||
data = "";
|
||||
DBG("### Closed: ", mux);
|
||||
}
|
||||
}
|
||||
} while (millis() - startMillis < timeout);
|
||||
finish:
|
||||
if (!index) {
|
||||
data.trim();
|
||||
if (data.length()) {
|
||||
DBG("### Unhandled:", data);
|
||||
}
|
||||
data = "";
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
uint8_t waitResponse(uint32_t timeout,
|
||||
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
|
||||
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
|
||||
{
|
||||
String data;
|
||||
return waitResponse(timeout, data, r1, r2, r3, r4, r5);
|
||||
}
|
||||
|
||||
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
|
||||
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
|
||||
{
|
||||
return waitResponse(1000, r1, r2, r3, r4, r5);
|
||||
}
|
||||
|
||||
public:
|
||||
Stream& stream;
|
||||
|
||||
protected:
|
||||
GsmClient* sockets[TINY_GSM_MUX_COUNT];
|
||||
};
|
||||
|
||||
#endif
|
||||
752
lib/MySensors/drivers/TinyGSM/TinyGsmClientXBee.h
Normal file
752
lib/MySensors/drivers/TinyGSM/TinyGsmClientXBee.h
Normal file
@@ -0,0 +1,752 @@
|
||||
/**
|
||||
* file TinyGsmClientXBee.h
|
||||
* author Volodymyr Shymanskyy
|
||||
* license LGPL-3.0
|
||||
* copyright Copyright (c) 2016 Volodymyr Shymanskyy
|
||||
* date Nov 2016
|
||||
*/
|
||||
|
||||
#ifndef TinyGsmClientXBee_h
|
||||
#define TinyGsmClientXBee_h
|
||||
|
||||
//#define TINY_GSM_DEBUG Serial
|
||||
|
||||
#if !defined(TINY_GSM_RX_BUFFER)
|
||||
#define TINY_GSM_RX_BUFFER 256
|
||||
#endif
|
||||
|
||||
#define TINY_GSM_MUX_COUNT 1 // Multi-plexing isn't supported using command mode
|
||||
|
||||
#include "TinyGsmCommon.h"
|
||||
|
||||
#define GSM_NL "\r"
|
||||
static const char GSM_OK[] TINY_GSM_PROGMEM = "OK" GSM_NL;
|
||||
static const char GSM_ERROR[] TINY_GSM_PROGMEM = "ERROR" GSM_NL;
|
||||
|
||||
enum SimStatus {
|
||||
SIM_ERROR = 0,
|
||||
SIM_READY = 1,
|
||||
SIM_LOCKED = 2,
|
||||
};
|
||||
|
||||
enum XBeeType {
|
||||
S6B = 0,
|
||||
LTEC1 = 1,
|
||||
};
|
||||
|
||||
enum RegStatus {
|
||||
REG_UNREGISTERED = 0,
|
||||
REG_SEARCHING = 2,
|
||||
REG_DENIED = 3,
|
||||
REG_OK_HOME = 1,
|
||||
REG_OK_ROAMING = 5,
|
||||
REG_UNKNOWN = 4,
|
||||
};
|
||||
|
||||
|
||||
class TinyGsm
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
class GsmClient : public Client
|
||||
{
|
||||
friend class TinyGsm;
|
||||
|
||||
public:
|
||||
GsmClient() {}
|
||||
|
||||
GsmClient(TinyGsm& modem, uint8_t mux = 0)
|
||||
{
|
||||
init(&modem, mux);
|
||||
}
|
||||
|
||||
bool init(TinyGsm* modem, uint8_t mux = 0)
|
||||
{
|
||||
this->at = modem;
|
||||
this->mux = mux;
|
||||
sock_connected = false;
|
||||
|
||||
at->sockets[mux] = this;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual int connect(const char *host, uint16_t port)
|
||||
{
|
||||
at->streamClear(); // Empty anything remaining in the buffer;
|
||||
at->commandMode();
|
||||
sock_connected = at->modemConnect(host, port, mux, false);
|
||||
at->writeChanges();
|
||||
at->exitCommand();
|
||||
return sock_connected;
|
||||
}
|
||||
|
||||
virtual int connect(IPAddress ip, uint16_t port)
|
||||
{
|
||||
at->streamClear(); // Empty anything remaining in the buffer;
|
||||
at->commandMode();
|
||||
sock_connected = at->modemConnect(ip, port, mux, false);
|
||||
at->writeChanges();
|
||||
at->exitCommand();
|
||||
return sock_connected;
|
||||
}
|
||||
|
||||
// This is a hack to shut the socket by setting the timeout to zero and
|
||||
// then sending an empty line to the server.
|
||||
virtual void stop()
|
||||
{
|
||||
at->streamClear(); // Empty anything remaining in the buffer;
|
||||
at->commandMode();
|
||||
at->sendAT(GF("TM0")); // Set socket timeout to 0;
|
||||
at->waitResponse();
|
||||
at->writeChanges();
|
||||
at->exitCommand();
|
||||
at->modemSend("", 1, mux);
|
||||
at->commandMode();
|
||||
at->sendAT(GF("TM64")); // Set socket timeout back to 10seconds;
|
||||
at->waitResponse();
|
||||
at->writeChanges();
|
||||
at->exitCommand();
|
||||
at->streamClear(); // Empty anything remaining in the buffer;
|
||||
sock_connected = false;
|
||||
}
|
||||
|
||||
virtual size_t write(const uint8_t *buf, size_t size)
|
||||
{
|
||||
TINY_GSM_YIELD();
|
||||
//at->maintain();
|
||||
return at->modemSend(buf, size, mux);
|
||||
}
|
||||
|
||||
virtual size_t write(uint8_t c)
|
||||
{
|
||||
return write(&c, 1);
|
||||
}
|
||||
|
||||
virtual int available()
|
||||
{
|
||||
TINY_GSM_YIELD();
|
||||
return at->stream.available();
|
||||
}
|
||||
|
||||
virtual int read(uint8_t *buf, size_t size)
|
||||
{
|
||||
TINY_GSM_YIELD();
|
||||
return at->stream.readBytes(buf, size);
|
||||
}
|
||||
|
||||
virtual int read()
|
||||
{
|
||||
TINY_GSM_YIELD();
|
||||
return at->stream.read();
|
||||
}
|
||||
|
||||
virtual int peek()
|
||||
{
|
||||
return at->stream.peek();
|
||||
}
|
||||
virtual void flush()
|
||||
{
|
||||
at->stream.flush();
|
||||
}
|
||||
|
||||
virtual uint8_t connected()
|
||||
{
|
||||
if (available()) {
|
||||
return true;
|
||||
}
|
||||
return sock_connected;
|
||||
}
|
||||
virtual operator bool()
|
||||
{
|
||||
return connected();
|
||||
}
|
||||
|
||||
/*
|
||||
* Extended API
|
||||
*/
|
||||
|
||||
String remoteIP() TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
|
||||
private:
|
||||
TinyGsm* at;
|
||||
uint8_t mux;
|
||||
bool sock_connected;
|
||||
};
|
||||
|
||||
class GsmClientSecure : public GsmClient
|
||||
{
|
||||
public:
|
||||
GsmClientSecure() {}
|
||||
|
||||
GsmClientSecure(TinyGsm& modem, uint8_t mux = 1)
|
||||
: GsmClient(modem, mux)
|
||||
{}
|
||||
|
||||
public:
|
||||
virtual int connect(const char *host, uint16_t port)
|
||||
{
|
||||
at->streamClear(); // Empty anything remaining in the buffer;
|
||||
at->commandMode();
|
||||
sock_connected = at->modemConnect(host, port, mux, true);
|
||||
at->writeChanges();
|
||||
at->exitCommand();
|
||||
return sock_connected;
|
||||
}
|
||||
|
||||
virtual int connect(IPAddress ip, uint16_t port)
|
||||
{
|
||||
at->streamClear(); // Empty anything remaining in the buffer;
|
||||
at->commandMode();
|
||||
sock_connected = at->modemConnect(ip, port, mux, true);
|
||||
at->writeChanges();
|
||||
at->exitCommand();
|
||||
return sock_connected;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
explicit TinyGsm(Stream& stream)
|
||||
: stream(stream)
|
||||
{
|
||||
memset(sockets, 0, sizeof(sockets));
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic functions
|
||||
*/
|
||||
bool begin()
|
||||
{
|
||||
return init();
|
||||
}
|
||||
|
||||
bool init()
|
||||
{
|
||||
guardTime = 1100;
|
||||
commandMode();
|
||||
sendAT(GF("AP0")); // Put in transparent mode
|
||||
waitResponse();
|
||||
sendAT(GF("GT64")); // shorten the guard time to 100ms
|
||||
waitResponse();
|
||||
writeChanges();
|
||||
sendAT(GF("HS")); // Get the "Hardware Series"; 0x601 for S6B (Wifi)
|
||||
// wait for the response
|
||||
unsigned long startMillis = millis();
|
||||
while (!stream.available() && millis() - startMillis < 1000) {};
|
||||
String res = streamReadUntil('\r'); // Does not send an OK, just the result
|
||||
exitCommand();
|
||||
if (res == "601") {
|
||||
beeType = S6B;
|
||||
} else {
|
||||
beeType = LTEC1;
|
||||
}
|
||||
guardTime = 125;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool testAT(unsigned long timeout = 10000L)
|
||||
{
|
||||
for (unsigned long start = millis(); millis() - start < timeout; ) {
|
||||
if (commandMode()) {
|
||||
sendAT();
|
||||
if (waitResponse(200) == 1) {
|
||||
return true;
|
||||
}
|
||||
exitCommand();
|
||||
}
|
||||
delay(100);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void maintain() {}
|
||||
|
||||
bool factoryDefault()
|
||||
{
|
||||
commandMode();
|
||||
sendAT(GF("RE"));
|
||||
bool ret_val = waitResponse() == 1;
|
||||
writeChanges();
|
||||
exitCommand();
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
bool hasSSL()
|
||||
{
|
||||
if (beeType == S6B) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Power functions
|
||||
*/
|
||||
|
||||
bool restart()
|
||||
{
|
||||
commandMode();
|
||||
sendAT(GF("FR"));
|
||||
if (waitResponse() != 1) {
|
||||
return false;
|
||||
}
|
||||
delay (2000); // Actually resets about 2 seconds later
|
||||
for (unsigned long start = millis(); millis() - start < 60000L; ) {
|
||||
if (commandMode()) {
|
||||
exitCommand();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
exitCommand();
|
||||
return false;;
|
||||
}
|
||||
|
||||
void setupPinSleep()
|
||||
{
|
||||
commandMode();
|
||||
sendAT(GF("SM"),1);
|
||||
waitResponse();
|
||||
if (beeType == S6B) {
|
||||
sendAT(GF("SO"),200);
|
||||
waitResponse();
|
||||
}
|
||||
writeChanges();
|
||||
exitCommand();
|
||||
}
|
||||
|
||||
/*
|
||||
* SIM card functions
|
||||
*/
|
||||
|
||||
bool simUnlock(const char *pin) // Not supported
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
String getSimCCID()
|
||||
{
|
||||
commandMode();
|
||||
sendAT(GF("S#"));
|
||||
// wait for the response
|
||||
unsigned long startMillis = millis();
|
||||
while (!stream.available() && millis() - startMillis < 1000) {};
|
||||
String res = streamReadUntil('\r'); // Does not send an OK, just the result
|
||||
exitCommand();
|
||||
return res;
|
||||
}
|
||||
|
||||
String getIMEI()
|
||||
{
|
||||
commandMode();
|
||||
sendAT(GF("IM"));
|
||||
// wait for the response
|
||||
unsigned long startMillis = millis();
|
||||
while (!stream.available() && millis() - startMillis < 1000) {};
|
||||
String res = streamReadUntil('\r'); // Does not send an OK, just the result
|
||||
exitCommand();
|
||||
return res;
|
||||
}
|
||||
|
||||
SimStatus getSimStatus(unsigned long timeout = 10000L)
|
||||
{
|
||||
return SIM_READY; // unsupported
|
||||
}
|
||||
|
||||
RegStatus getRegistrationStatus()
|
||||
{
|
||||
commandMode();
|
||||
sendAT(GF("AI"));
|
||||
// wait for the response
|
||||
unsigned long startMillis = millis();
|
||||
while (!stream.available() && millis() - startMillis < 1000) {};
|
||||
String res = streamReadUntil('\r'); // Does not send an OK, just the result
|
||||
exitCommand();
|
||||
|
||||
if(res == GF("0")) {
|
||||
return REG_OK_HOME;
|
||||
}
|
||||
|
||||
else if(res == GF("13") || res == GF("2A")) {
|
||||
return REG_UNREGISTERED;
|
||||
}
|
||||
|
||||
else if(res == GF("FF") || res == GF("22") || res == GF("23") ||
|
||||
res == GF("40") || res == GF("41") || res == GF("42")) {
|
||||
return REG_SEARCHING;
|
||||
}
|
||||
|
||||
else if(res == GF("24") || res == GF("25") || res == GF("27")) {
|
||||
return REG_DENIED;
|
||||
}
|
||||
|
||||
else {
|
||||
return REG_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
String getOperator()
|
||||
{
|
||||
commandMode();
|
||||
sendAT(GF("MN"));
|
||||
// wait for the response
|
||||
unsigned long startMillis = millis();
|
||||
while (!stream.available() && millis() - startMillis < 1000) {};
|
||||
String res = streamReadUntil('\r'); // Does not send an OK, just the result
|
||||
exitCommand();
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic network functions
|
||||
*/
|
||||
|
||||
int getSignalQuality()
|
||||
{
|
||||
commandMode();
|
||||
if (beeType == S6B) {
|
||||
sendAT(GF("LM")); // ask for the "link margin" - the dB above sensitivity
|
||||
} else {
|
||||
sendAT(GF("DB")); // ask for the cell strength in dBm
|
||||
}
|
||||
// wait for the response
|
||||
unsigned long startMillis = millis();
|
||||
while (!stream.available() && millis() - startMillis < 1000) {};
|
||||
char buf[2] = {0}; // Set up buffer for response
|
||||
buf[0] = streamRead();
|
||||
buf[1] = streamRead();
|
||||
// DBG(buf[0], buf[1], "\n");
|
||||
exitCommand();
|
||||
int intr = strtol(buf, 0, 16);
|
||||
if (beeType == S6B) {
|
||||
return -93 + intr; // the maximum sensitivity is -93dBm
|
||||
} else {
|
||||
return -1*intr; // need to convert to negative number
|
||||
}
|
||||
}
|
||||
|
||||
bool isNetworkConnected()
|
||||
{
|
||||
RegStatus s = getRegistrationStatus();
|
||||
return (s == REG_OK_HOME || s == REG_OK_ROAMING);
|
||||
}
|
||||
|
||||
bool waitForNetwork(unsigned long timeout = 60000L)
|
||||
{
|
||||
for (unsigned long start = millis(); millis() - start < timeout; ) {
|
||||
if (isNetworkConnected()) {
|
||||
return true;
|
||||
}
|
||||
delay(250);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* WiFi functions
|
||||
*/
|
||||
bool networkConnect(const char* ssid, const char* pwd)
|
||||
{
|
||||
|
||||
commandMode();
|
||||
|
||||
sendAT(GF("EE"), 2); // Set security to WPA2
|
||||
waitResponse();
|
||||
|
||||
sendAT(GF("ID"), ssid);
|
||||
if (waitResponse() != 1) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
sendAT(GF("PK"), pwd);
|
||||
if (waitResponse() != 1) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
writeChanges();
|
||||
exitCommand();
|
||||
|
||||
return true;
|
||||
|
||||
fail:
|
||||
exitCommand();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool networkDisconnect()
|
||||
{
|
||||
return false; // Doesn't support disconnecting
|
||||
}
|
||||
|
||||
String getLocalIP()
|
||||
{
|
||||
commandMode();
|
||||
sendAT(GF("MY"));
|
||||
String IPaddr;
|
||||
IPaddr.reserve(16);
|
||||
// wait for the response
|
||||
unsigned long startMillis = millis();
|
||||
while (stream.available() < 8 && millis() - startMillis < 30000) {};
|
||||
IPaddr = streamReadUntil('\r'); // read result
|
||||
return IPaddr;
|
||||
}
|
||||
|
||||
IPAddress localIP()
|
||||
{
|
||||
return TinyGsmIpFromString(getLocalIP());
|
||||
}
|
||||
|
||||
/*
|
||||
* GPRS functions
|
||||
*/
|
||||
bool gprsConnect(const char* apn, const char* user = NULL, const char* pwd = NULL)
|
||||
{
|
||||
commandMode();
|
||||
sendAT(GF("AN"), apn); // Set the APN
|
||||
waitResponse();
|
||||
writeChanges();
|
||||
exitCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gprsDisconnect() // TODO
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Messaging functions
|
||||
*/
|
||||
|
||||
String sendUSSD(const String& code) TINY_GSM_ATTR_NOT_IMPLEMENTED;
|
||||
|
||||
bool sendSMS(const String& number, const String& text)
|
||||
{
|
||||
commandMode();
|
||||
sendAT(GF("IP"), 2); // Put in text messaging mode
|
||||
waitResponse();
|
||||
sendAT(GF("PH"), number); // Set the phone number
|
||||
waitResponse();
|
||||
sendAT(GF("TDD")); // Set the text delimiter to the standard 0x0D (carriabe return)
|
||||
waitResponse();
|
||||
writeChanges();
|
||||
exitCommand();
|
||||
stream.print(text);
|
||||
stream.write((char)0x0D); // close off with the carriage return
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
int modemConnect(const char* host, uint16_t port, uint8_t mux = 0, bool ssl = false)
|
||||
{
|
||||
sendAT(GF("LA"), host);
|
||||
String strIP;
|
||||
strIP.reserve(16);
|
||||
// wait for the response
|
||||
unsigned long startMillis = millis();
|
||||
while (stream.available() < 8 && millis() - startMillis < 30000) {};
|
||||
strIP = streamReadUntil('\r'); // read result
|
||||
IPAddress ip = TinyGsmIpFromString(strIP);
|
||||
return modemConnect(ip, port, mux, ssl);
|
||||
}
|
||||
|
||||
int modemConnect(IPAddress ip, uint16_t port, uint8_t mux = 0, bool ssl = false)
|
||||
{
|
||||
String host;
|
||||
host.reserve(16);
|
||||
host += ip[0];
|
||||
host += ".";
|
||||
host += ip[1];
|
||||
host += ".";
|
||||
host += ip[2];
|
||||
host += ".";
|
||||
host += ip[3];
|
||||
if (ssl) {
|
||||
sendAT(GF("IP"), 4); // Put in TCP mode
|
||||
waitResponse();
|
||||
} else {
|
||||
sendAT(GF("IP"), 1); // Put in TCP mode
|
||||
waitResponse();
|
||||
}
|
||||
sendAT(GF("DL"), host); // Set the "Destination Address Low"
|
||||
waitResponse();
|
||||
sendAT(GF("DE"), String(port, HEX)); // Set the destination port
|
||||
int rsp = waitResponse();
|
||||
return rsp;
|
||||
}
|
||||
|
||||
int modemSend(const void* buff, size_t len, uint8_t mux = 0)
|
||||
{
|
||||
stream.write((uint8_t*)buff, len);
|
||||
stream.flush();
|
||||
return len;
|
||||
}
|
||||
|
||||
bool modemGetConnected(uint8_t mux = 0)
|
||||
{
|
||||
commandMode();
|
||||
sendAT(GF("AI"));
|
||||
int res = waitResponse(GF("0"));
|
||||
exitCommand();
|
||||
return 1 == res;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/* Utilities */
|
||||
|
||||
template<typename T>
|
||||
void streamWrite(T last)
|
||||
{
|
||||
stream.print(last);
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
void streamWrite(T head, Args... tail)
|
||||
{
|
||||
stream.print(head);
|
||||
streamWrite(tail...);
|
||||
}
|
||||
|
||||
int streamRead()
|
||||
{
|
||||
return stream.read();
|
||||
}
|
||||
|
||||
String streamReadUntil(char c)
|
||||
{
|
||||
TINY_GSM_YIELD();
|
||||
String return_string = stream.readStringUntil(c);
|
||||
return_string.trim();
|
||||
// DBG(return_string, c);
|
||||
return return_string;
|
||||
}
|
||||
|
||||
void streamClear(void)
|
||||
{
|
||||
while (stream.available()) {
|
||||
streamRead();
|
||||
}
|
||||
}
|
||||
|
||||
bool commandMode(void)
|
||||
{
|
||||
delay(guardTime); // cannot send anything for 1 second before entering command mode
|
||||
streamWrite(GF("+++")); // enter command mode
|
||||
// DBG("\r\n+++\r\n");
|
||||
return 1 == waitResponse(guardTime*2);
|
||||
}
|
||||
|
||||
void writeChanges(void)
|
||||
{
|
||||
sendAT(GF("WR")); // Write changes to flash
|
||||
waitResponse();
|
||||
sendAT(GF("AC")); // Apply changes
|
||||
waitResponse();
|
||||
}
|
||||
|
||||
void exitCommand(void)
|
||||
{
|
||||
sendAT(GF("CN")); // Exit command mode
|
||||
waitResponse();
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void sendAT(Args... cmd)
|
||||
{
|
||||
streamWrite("AT", cmd..., GSM_NL);
|
||||
stream.flush();
|
||||
TINY_GSM_YIELD();
|
||||
//DBG("### AT:", cmd...);
|
||||
}
|
||||
|
||||
// TODO: Optimize this!
|
||||
uint8_t waitResponse(uint32_t timeout, String& data,
|
||||
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
|
||||
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
|
||||
{
|
||||
/*String r1s(r1); r1s.trim();
|
||||
String r2s(r2); r2s.trim();
|
||||
String r3s(r3); r3s.trim();
|
||||
String r4s(r4); r4s.trim();
|
||||
String r5s(r5); r5s.trim();
|
||||
DBG("### ..:", r1s, ",", r2s, ",", r3s, ",", r4s, ",", r5s);*/
|
||||
data.reserve(64);
|
||||
int index = 0;
|
||||
unsigned long startMillis = millis();
|
||||
do {
|
||||
TINY_GSM_YIELD();
|
||||
while (stream.available() > 0) {
|
||||
int a = streamRead();
|
||||
if (a <= 0) {
|
||||
continue; // Skip 0x00 bytes, just in case
|
||||
}
|
||||
data += (char)a;
|
||||
if (r1 && data.endsWith(r1)) {
|
||||
index = 1;
|
||||
goto finish;
|
||||
} else if (r2 && data.endsWith(r2)) {
|
||||
index = 2;
|
||||
goto finish;
|
||||
} else if (r3 && data.endsWith(r3)) {
|
||||
index = 3;
|
||||
goto finish;
|
||||
} else if (r4 && data.endsWith(r4)) {
|
||||
index = 4;
|
||||
goto finish;
|
||||
} else if (r5 && data.endsWith(r5)) {
|
||||
index = 5;
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
} while (millis() - startMillis < timeout);
|
||||
finish:
|
||||
if (!index) {
|
||||
data.trim();
|
||||
data.replace(GSM_NL GSM_NL, GSM_NL);
|
||||
data.replace(GSM_NL, "\r\n" " ");
|
||||
if (data.length()) {
|
||||
DBG("### Unhandled:", data, "\r\n");
|
||||
} else {
|
||||
DBG("### NO RESPONSE!\r\n");
|
||||
}
|
||||
} else {
|
||||
data.trim();
|
||||
data.replace(GSM_NL GSM_NL, GSM_NL);
|
||||
data.replace(GSM_NL, "\r\n ");
|
||||
if (data.length()) {
|
||||
// DBG("<<< ", data);
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
uint8_t waitResponse(uint32_t timeout,
|
||||
GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
|
||||
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
|
||||
{
|
||||
String data;
|
||||
return waitResponse(timeout, data, r1, r2, r3, r4, r5);
|
||||
}
|
||||
|
||||
uint8_t waitResponse(GsmConstStr r1=GFP(GSM_OK), GsmConstStr r2=GFP(GSM_ERROR),
|
||||
GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL)
|
||||
{
|
||||
return waitResponse(1000, r1, r2, r3, r4, r5);
|
||||
}
|
||||
|
||||
public:
|
||||
Stream& stream;
|
||||
|
||||
protected:
|
||||
int guardTime;
|
||||
XBeeType beeType;
|
||||
GsmClient* sockets[TINY_GSM_MUX_COUNT];
|
||||
};
|
||||
|
||||
#endif
|
||||
195
lib/MySensors/drivers/TinyGSM/TinyGsmCommon.h
Normal file
195
lib/MySensors/drivers/TinyGSM/TinyGsmCommon.h
Normal file
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* file TinyGsmCommon.h
|
||||
* author Volodymyr Shymanskyy
|
||||
* license LGPL-3.0
|
||||
* copyright Copyright (c) 2016 Volodymyr Shymanskyy
|
||||
* date Nov 2016
|
||||
*/
|
||||
|
||||
#ifndef TinyGsmCommon_h
|
||||
#define TinyGsmCommon_h
|
||||
|
||||
#if defined(SPARK) || defined(PARTICLE)
|
||||
#include "Particle.h"
|
||||
#elif defined(ARDUINO)
|
||||
#if ARDUINO >= 100
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <Client.h>
|
||||
#include "TinyGsmFifo.h"
|
||||
|
||||
#ifndef TINY_GSM_YIELD
|
||||
#define TINY_GSM_YIELD() { delay(0); }
|
||||
#endif
|
||||
|
||||
#define TINY_GSM_ATTR_NOT_AVAILABLE __attribute__((error("Not available on this modem type")))
|
||||
#define TINY_GSM_ATTR_NOT_IMPLEMENTED __attribute__((error("Not implemented")))
|
||||
|
||||
#if defined(__AVR__)
|
||||
#define TINY_GSM_PROGMEM PROGMEM
|
||||
typedef const __FlashStringHelper* GsmConstStr;
|
||||
#define GFP(x) (reinterpret_cast<GsmConstStr>(x))
|
||||
#define GF(x) F(x)
|
||||
#else
|
||||
#define TINY_GSM_PROGMEM
|
||||
typedef const char* GsmConstStr;
|
||||
#define GFP(x) x
|
||||
#define GF(x) x
|
||||
#endif
|
||||
|
||||
#ifdef TINY_GSM_DEBUG
|
||||
namespace
|
||||
{
|
||||
template<typename T>
|
||||
static void DBG(T last)
|
||||
{
|
||||
TINY_GSM_DEBUG.println(last);
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
static void DBG(T head, Args... tail)
|
||||
{
|
||||
TINY_GSM_DEBUG.print(head);
|
||||
TINY_GSM_DEBUG.print(' ');
|
||||
DBG(tail...);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define DBG(...)
|
||||
#endif
|
||||
|
||||
template<class T>
|
||||
const T& TinyGsmMin(const T& a, const T& b)
|
||||
{
|
||||
return (b < a) ? b : a;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
const T& TinyGsmMax(const T& a, const T& b)
|
||||
{
|
||||
return (b < a) ? a : b;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
uint32_t TinyGsmAutoBaud(T& SerialAT, uint32_t minimum = 9600, uint32_t maximum = 115200)
|
||||
{
|
||||
static uint32_t rates[] = { 115200, 57600, 38400, 19200, 9600, 74400, 74880, 230400, 460800, 2400, 4800, 14400, 28800 };
|
||||
|
||||
for (unsigned i = 0; i < sizeof(rates)/sizeof(rates[0]); i++) {
|
||||
uint32_t rate = rates[i];
|
||||
if (rate < minimum || rate > maximum) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DBG("Trying baud rate", rate, "...");
|
||||
SerialAT.begin(rate);
|
||||
delay(10);
|
||||
for (int i=0; i<3; i++) {
|
||||
SerialAT.print("AT\r\n");
|
||||
String input = SerialAT.readString();
|
||||
if (input.indexOf("OK") >= 0) {
|
||||
DBG("Modem responded at rate", rate);
|
||||
return rate;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline
|
||||
IPAddress TinyGsmIpFromString(const String& strIP)
|
||||
{
|
||||
int Parts[4] = {0, };
|
||||
int Part = 0;
|
||||
for (uint8_t i=0; i<strIP.length(); i++) {
|
||||
char c = strIP[i];
|
||||
if (c == '.') {
|
||||
Part++;
|
||||
if (Part > 3) {
|
||||
return IPAddress(0,0,0,0);
|
||||
}
|
||||
continue;
|
||||
} else if (c >= '0' && c <= '9') {
|
||||
Parts[Part] *= 10;
|
||||
Parts[Part] += c - '0';
|
||||
} else {
|
||||
if (Part == 3) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return IPAddress(Parts[0], Parts[1], Parts[2], Parts[3]);
|
||||
}
|
||||
|
||||
static inline
|
||||
String TinyGsmDecodeHex7bit(String &instr)
|
||||
{
|
||||
String result;
|
||||
byte reminder = 0;
|
||||
int bitstate = 7;
|
||||
for (unsigned i=0; i<instr.length(); i+=2) {
|
||||
char buf[4] = { 0, };
|
||||
buf[0] = instr[i];
|
||||
buf[1] = instr[i+1];
|
||||
byte b = strtol(buf, NULL, 16);
|
||||
|
||||
byte bb = b << (7 - bitstate);
|
||||
char c = (bb + reminder) & 0x7F;
|
||||
result += c;
|
||||
reminder = b >> bitstate;
|
||||
bitstate--;
|
||||
if (bitstate == 0) {
|
||||
char c = reminder;
|
||||
result += c;
|
||||
reminder = 0;
|
||||
bitstate = 7;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline
|
||||
String TinyGsmDecodeHex8bit(String &instr)
|
||||
{
|
||||
String result;
|
||||
for (unsigned i=0; i<instr.length(); i+=2) {
|
||||
char buf[4] = { 0, };
|
||||
buf[0] = instr[i];
|
||||
buf[1] = instr[i+1];
|
||||
char b = strtol(buf, NULL, 16);
|
||||
result += b;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline
|
||||
String TinyGsmDecodeHex16bit(String &instr)
|
||||
{
|
||||
String result;
|
||||
for (unsigned i=0; i<instr.length(); i+=4) {
|
||||
char buf[4] = { 0, };
|
||||
buf[0] = instr[i];
|
||||
buf[1] = instr[i+1];
|
||||
char b = strtol(buf, NULL, 16);
|
||||
if (b) { // If high byte is non-zero, we can't handle it ;(
|
||||
#if defined(TINY_GSM_UNICODE_TO_HEX)
|
||||
result += "\\x";
|
||||
result += instr.substring(i, i+4);
|
||||
#else
|
||||
result += "?";
|
||||
#endif
|
||||
} else {
|
||||
buf[0] = instr[i+2];
|
||||
buf[1] = instr[i+3];
|
||||
b = strtol(buf, NULL, 16);
|
||||
result += b;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
152
lib/MySensors/drivers/TinyGSM/TinyGsmFifo.h
Normal file
152
lib/MySensors/drivers/TinyGSM/TinyGsmFifo.h
Normal file
@@ -0,0 +1,152 @@
|
||||
#ifndef TinyGsmFifo_h
|
||||
#define TinyGsmFifo_h
|
||||
|
||||
template <class T, unsigned N>
|
||||
|
||||
class TinyGsmFifo
|
||||
{
|
||||
public:
|
||||
|
||||
TinyGsmFifo()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
_r = 0;
|
||||
_w = 0;
|
||||
}
|
||||
|
||||
// writing thread/context API
|
||||
//-------------------------------------------------------------
|
||||
|
||||
bool writeable(void)
|
||||
{
|
||||
return free() > 0;
|
||||
}
|
||||
|
||||
int free(void)
|
||||
{
|
||||
int s = _r - _w;
|
||||
if (s <= 0) {
|
||||
s += N;
|
||||
}
|
||||
return s - 1;
|
||||
}
|
||||
|
||||
bool put(const T& c)
|
||||
{
|
||||
int i = _w;
|
||||
int j = i;
|
||||
i = _inc(i);
|
||||
if (i == _r) { // !writeable()
|
||||
return false;
|
||||
}
|
||||
_b[j] = c;
|
||||
_w = i;
|
||||
return true;
|
||||
}
|
||||
|
||||
int put(const T* p, int n, bool t = false)
|
||||
{
|
||||
int c = n;
|
||||
while (c) {
|
||||
int f;
|
||||
while ((f = free()) == 0) { // wait for space
|
||||
if (!t) {
|
||||
return n - c; // no more space and not blocking
|
||||
}
|
||||
/* nothing / just wait */;
|
||||
}
|
||||
// check free space
|
||||
if (c < f) {
|
||||
f = c;
|
||||
}
|
||||
int w = _w;
|
||||
int m = N - w;
|
||||
// check wrap
|
||||
if (f > m) {
|
||||
f = m;
|
||||
}
|
||||
memcpy(&_b[w], p, f);
|
||||
_w = _inc(w, f);
|
||||
c -= f;
|
||||
p += f;
|
||||
}
|
||||
return n - c;
|
||||
}
|
||||
|
||||
// reading thread/context API
|
||||
// --------------------------------------------------------
|
||||
|
||||
bool readable(void)
|
||||
{
|
||||
return (_r != _w);
|
||||
}
|
||||
|
||||
size_t size(void)
|
||||
{
|
||||
int s = _w - _r;
|
||||
if (s < 0) {
|
||||
s += N;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
bool get(T* p)
|
||||
{
|
||||
int r = _r;
|
||||
if (r == _w) { // !readable()
|
||||
return false;
|
||||
}
|
||||
*p = _b[r];
|
||||
_r = _inc(r);
|
||||
return true;
|
||||
}
|
||||
|
||||
int get(T* p, int n, bool t = false)
|
||||
{
|
||||
int c = n;
|
||||
while (c) {
|
||||
int f;
|
||||
for (;;) { // wait for data
|
||||
f = size();
|
||||
if (f) {
|
||||
break; // free space
|
||||
}
|
||||
if (!t) {
|
||||
return n - c; // no space and not blocking
|
||||
}
|
||||
/* nothing / just wait */;
|
||||
}
|
||||
// check available data
|
||||
if (c < f) {
|
||||
f = c;
|
||||
}
|
||||
int r = _r;
|
||||
int m = N - r;
|
||||
// check wrap
|
||||
if (f > m) {
|
||||
f = m;
|
||||
}
|
||||
memcpy(p, &_b[r], f);
|
||||
_r = _inc(r, f);
|
||||
c -= f;
|
||||
p += f;
|
||||
}
|
||||
return n - c;
|
||||
}
|
||||
|
||||
private:
|
||||
int _inc(int i, int n = 1)
|
||||
{
|
||||
return (i + n) % N;
|
||||
}
|
||||
|
||||
T _b[N];
|
||||
int _w;
|
||||
int _r;
|
||||
};
|
||||
|
||||
#endif
|
||||
26
lib/MySensors/drivers/TinyGSM/keywords.txt
Normal file
26
lib/MySensors/drivers/TinyGSM/keywords.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
#######################################
|
||||
# Data types (KEYWORD1)
|
||||
#######################################
|
||||
TinyGsm KEYWORD1
|
||||
TinyGsmClient KEYWORD1
|
||||
TinyGsmClientSecure KEYWORD1
|
||||
|
||||
SerialAT KEYWORD1
|
||||
SerialMon KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
begin KEYWORD2
|
||||
restart KEYWORD2
|
||||
waitForNetwork KEYWORD2
|
||||
getSimStatus KEYWORD2
|
||||
gprsConnect KEYWORD2
|
||||
gprsDisconnect KEYWORD2
|
||||
isGprsConnected KEYWORD2
|
||||
isNetworkConnected KEYWORD2
|
||||
factoryReset KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Literals (LITERAL1)
|
||||
#######################################
|
||||
8
lib/MySensors/drivers/extEEPROM/LICENSE.md
Normal file
8
lib/MySensors/drivers/extEEPROM/LICENSE.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Arduino External EEPROM Library v3 #
|
||||
http://github.com/JChristensen/extEEPROM
|
||||
LICENSE file
|
||||
Jack Christensen Jul 2014
|
||||
|
||||

|
||||
## CC BY-SA ##
|
||||
"Arduino External EEPROM Library" by Jack Christensen is licensed under [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/).
|
||||
226
lib/MySensors/drivers/extEEPROM/ReadMe.md
Normal file
226
lib/MySensors/drivers/extEEPROM/ReadMe.md
Normal file
@@ -0,0 +1,226 @@
|
||||
# Arduino External EEPROM Library v3.4 #
|
||||
|
||||
Original library by http://github.com/JChristensen/extEEPROM
|
||||
|
||||
## Introduction ##
|
||||
**Arduino External EEPROM Library**
|
||||
|
||||
This library will work with most I2C serial EEPROM chips between 2k bits and 2048k bits (2M bits) in size. Multiple EEPROMs on the bus are supported as a single address space. I/O across block, page and device boundaries is supported. Certain assumptions are made regarding the EEPROM device addressing. These assumptions should be true for most EEPROMs but there are exceptions, so read the datasheet and know your hardware.
|
||||
|
||||
The library should also work for EEPROMs smaller than 2k bits, assuming that there is only one EEPROM on the bus and also that the user is careful to not exceed the maximum address for the EEPROM.
|
||||
|
||||
The **extEEPROM Library** has been tested with:
|
||||
- Microchip 24AA02E48 (2k bit)
|
||||
- 24xx32 (32k bit, thanks to Richard M)
|
||||
- Microchip 24LC256 (256k bit)
|
||||
- Microchip 24FC1026 (1M bit, thanks to Gabriele B on the Arduino forum)
|
||||
- ST Micro M24M02 (2M bit)
|
||||
- Atmel AT24C256C (32k x 8, thanks to Searobin)
|
||||
|
||||
The **extEEPROM Library** will **NOT** work with Microchip 24xx1025 as its control byte does not conform to the following assumptions.
|
||||
|
||||
**Device addressing assumptions:**
|
||||
- The I2C address sequence consists of a control byte followed by one address byte (for EEPROMs <= 16k bits) or two address bytes (for EEPROMs > 16k bits).
|
||||
- The three least-significant bits in the control byte (excluding the R/W bit) comprise the three most-significant bits for the entire address space, i.e. all chips on the bus. As such, these may be chip-select bits or block-select bits (for individual chips that have an internal block organization), or a combination of both (in which case the block-select bits must be of lesser significance than the chip-select bits).
|
||||
- Regardless of the number of bits needed to address the entire address space, the three most-significant bits always go in the control byte. Depending on EEPROM device size, this may result in one or more of the most significant bits in the I2C address bytes being unused (or "don't care" bits).
|
||||
- An EEPROM contains an integral number of pages.
|
||||
|
||||
Note that the Arduino Wire library has a buffer size of 32 bytes. This limits the size of physical I/Os that can be done to EEPROM. For writes, one or two bytes are used for the address, so writing is therefore limited to 31 or 30 bytes. Because the **extEEPROM Library** will handle I/O across block, page and device boundaries, the only consequence this has for the user is one of efficiency; arbitrarily large blocks of data can be written and read; however, carefully chosen block sizes may reduce the number of physical I/Os needed.
|
||||
|
||||
## Installation ##
|
||||
|
||||
### Install with the Library Manager
|
||||
- For Arduino IDE versions 1.6.2 and up, add library by selecting "Manage Libraries..." from the "Include Library" submenu within the Sketch menu.
|
||||
- The library manager will open and you will find a list of libraries that are already installed or ready for installation. Scroll the list to find **extEEPROM** library and click on it.
|
||||
- Select the version of the library you want to install.
|
||||
- Click on install and wait for the IDE to install the new library. Downloading may take time depending on your connection speed.
|
||||
- Once it has finished, an Installed tag should appear, You can close the library manager.
|
||||
|
||||
### Manual Install
|
||||
- Go to http://github.com/PaoloP74/extEEPROM, click the **Download ZIP** button and save the ZIP file to a convenient location on your PC.
|
||||
- Uncompress the downloaded file. This will result in a folder containing all the files for the library, that has a name that includes the branch name, usually **extEEPROM-master**.
|
||||
- Rename the folder to just **extEEPROM**.
|
||||
- Copy the renamed folder to the Arduino sketchbook\libraries folder.
|
||||
|
||||
## Examples ##
|
||||
The following example sketch is included with the **extEEPROM Library**:
|
||||
- **eepromReadWrite**
|
||||
- **eepromTest:** Writes 32-bit integers to the entire EEPROM address space, starting at address 0 and continuing to the topmost address. These are then read back in and verified; any discrepancies are reported to the serial monitor.
|
||||
- **eepromTest_Wire1**
|
||||
## Usage notes ##
|
||||
The **extEEPROM Library** is designed for use with Arduino version 1.0 or later.
|
||||
|
||||
To use the **extEEPROM Library**, the standard [Arduino Wire library](http://arduino.cc/en/Reference/Wire) must also be included. For brevity, this include is not repeated in the examples below:
|
||||
```c++
|
||||
#include <Wire.h> //http://arduino.cc/en/Reference/Wire (included with Arduino IDE)
|
||||
```
|
||||
## Enumerations ##
|
||||
### eeprom_size_t
|
||||
##### Description
|
||||
EEPROM device size in k-bits. Many manufacturers' EEPROM part numbers are designated in k-bits.
|
||||
##### Values
|
||||
- kbits_2
|
||||
- kbits_4
|
||||
- kbits_8
|
||||
- kbits_16
|
||||
- kbits_32
|
||||
- kbits_64
|
||||
- kbits_128
|
||||
- kbits_256
|
||||
- kbits_512
|
||||
- kbits_1024
|
||||
- kbits_2048
|
||||
### twiClockFreq_t
|
||||
##### Description
|
||||
I2C bus speed.
|
||||
##### Values
|
||||
- extEEPROM::twiClock100kHz
|
||||
- extEEPROM::twiClock400kHz
|
||||
|
||||
## Constructor ##
|
||||
### extEEPROM(eeprom_size_t devCap, byte nDev, unsigned int pgSize, byte busAddr)
|
||||
##### Description
|
||||
Instantiates an external EEPROM object.
|
||||
##### Syntax
|
||||
`extEEPROM myEEPROM(eeprom_size_t devCap, byte nDev, unsigned int pgSize, byte busAddr);`
|
||||
##### Parameters
|
||||
**devCap** *(eeprom_size_t)*: The size of one EEPROM device in k-bits. Choose a value from the eeprom_size_t enumeration above.
|
||||
**nDev** *(byte)*: The number of EEPROM devices on the bus. Note that if there are multiple EEPROM devices on the bus, they must be identical and each must have its address pins strapped properly.
|
||||
**pgSize** *(unsigned int)*: The EEPROM page size in bytes. Consult the datasheet if you are unsure of the page size.
|
||||
**busAddr** *(byte)*: The base I2C bus address for the EEPROM(s). 0x50 is a common value and this parameter can be omitted, in which case 0x50 will be used as the default.
|
||||
##### Example
|
||||
```c++
|
||||
extEEPROM myEEPROM(kbits_256, 2, 64); //two 24LC256 EEPROMS on the bus
|
||||
extEEPROM oddEEPROM(kbits_8, 1, 16, 0x42); //an EEPROM with a non-standard I2C address
|
||||
```
|
||||
|
||||
## Methods ##
|
||||
### begin(twiClockFreq_t freq, TwoWire *_comm)
|
||||
##### Description
|
||||
Initializes the library. Call this method once in the setup code. begin() does a dummy I/O so that the user may interrogate the return status to ensure the EEPROM is operational.
|
||||
##### Syntax
|
||||
`myEEPROM.begin(twiClockFreq_t freq);`
|
||||
or
|
||||
`myEEPROM.begin(twiClockFreq_t freq, TwoWire *_comm);`
|
||||
##### Parameters
|
||||
**freq** *(twiClockFreq_t)*: The desired I2C bus speed, `extEEPROM::twiClock100kHz` or `extEEPROM::twiClock400kHz`. Can be omitted in which case it will default to `twiClock100kHz`.
|
||||
**NOTE:** When using 400kHz, if there are other devices on the bus they must all support a 400kHz bus speed. **Secondly**, the other devices should be initialized first, as other libraries may not support adjusting the bus speed. To ensure the desired speed is set, call the extEEPROM.begin() function *after* initializing all other I2C devices.
|
||||
|
||||
**_comm** *(TwoWire * )*: The Used I2C TwoWire channel . Can be omitted in which case it will default to the first Arduino I2C channel `Wire`. If another of the possible I2C channel is used its pointer shall be passed as parameter.
|
||||
**NOTE:** If another I2C channel is unse, and not the default one, the first parameters **freq** MUST be defined.
|
||||
##### Returns
|
||||
I2C I/O status, zero if successful *(byte)*. See the [Arduino Wire.endTransmission() function](http://arduino.cc/en/Reference/WireEndTransmission) for a description of other return codes.
|
||||
|
||||
##### Examples
|
||||
```c++
|
||||
extEEPROM myEEPROM(kbits_256, 2, 64);
|
||||
byte i2cStat = myEEPROM.begin(extEEPROM::twiClock400kHz);
|
||||
if ( i2cStat != 0 ) {
|
||||
//there was a problem
|
||||
}
|
||||
```
|
||||
##### Use of other I2C channel
|
||||
```c++
|
||||
extEEPROM myEEPROM(kbits_256, 2, 64);
|
||||
byte i2cStat = myEEPROM.begin(extEEPROM::twiClock400kHz, &Wire1);
|
||||
if ( i2cStat != 0 ) {
|
||||
//there was a problem
|
||||
}
|
||||
```
|
||||
### write(unsigned long addr, byte *values, unsigned int nBytes)
|
||||
##### Description
|
||||
Write one or more bytes to external EEPROM.
|
||||
##### Syntax
|
||||
`myEEPROM.write(unsigned long addr, byte* values, byte nBytes);`
|
||||
##### Parameters
|
||||
**addr** *(unsigned long)*: The beginning EEPROM location to write.
|
||||
**values** _(byte*)_: Pointer to an array containing the data to write.
|
||||
**nBytes** *(unsigned int)*: The number of bytes to write.
|
||||
##### Returns
|
||||
I2C I/O status, zero if successful *(byte)*. See the [Arduino Wire.endTransmission() function](http://arduino.cc/en/Reference/WireEndTransmission) for a description of other return codes. Returns a status of EEPROM_ADDR_ERR if the I/O would extend past the top of the EEPROM address space.
|
||||
##### Example
|
||||
```c++
|
||||
byte myData[10];
|
||||
//write 10 bytes starting at location 42
|
||||
byte i2cStat = myEEPROM.write(42, &data, 10);
|
||||
if ( i2cStat != 0 ) {
|
||||
//there was a problem
|
||||
if ( i2cStat == EEPROM_ADDR_ERR) {
|
||||
//bad address
|
||||
}
|
||||
else {
|
||||
//some other I2C error
|
||||
}
|
||||
}
|
||||
```
|
||||
### write(unsigned long addr, byte value)
|
||||
##### Description
|
||||
Writes a single byte to external EEPROM.
|
||||
##### Syntax
|
||||
`myEEPROM.write(unsigned long addr, byte value);`
|
||||
##### Parameters
|
||||
**addr** *(unsigned long)*: The EEPROM location to write.
|
||||
**values** _(byte)_: The value to write.
|
||||
##### Returns
|
||||
Same as multiple-byte write() above.
|
||||
##### Example
|
||||
```c++
|
||||
//write the value 16 to EEPROM location 314.
|
||||
byte i2cStat = myEEPROM.write(314, 16);
|
||||
```
|
||||
### read(unsigned long addr, byte *values, unsigned int nBytes)
|
||||
##### Description
|
||||
Reads one or more bytes from external EEPROM into an array supplied by the caller.
|
||||
##### Syntax
|
||||
`myEEPROM.read(unsigned long addr, byte *values, byte nBytes);`
|
||||
##### Parameters
|
||||
**addr** *(unsigned long)*: The beginning EEPROM location to read from.
|
||||
**values** _(byte*)_: Pointer to an array to receive the data.
|
||||
**nBytes** *(unsigned int)*: The number of bytes to read.
|
||||
##### Returns
|
||||
I2C I/O status, zero if successful *(byte)*. See the [Arduino Wire.endTransmission() function](http://arduino.cc/en/Reference/WireEndTransmission) for a description of other return codes. Returns a status of EEPROM_ADDR_ERR if the I/O would extend past the top of the EEPROM address space.
|
||||
##### Example
|
||||
```c++
|
||||
byte myData[10];
|
||||
//read 10 bytes starting at location 42
|
||||
byte i2cStat = myEEPROM.read(42, &data, 10);
|
||||
if ( i2cStat != 0 ) {
|
||||
//there was a problem
|
||||
if ( i2cStat == EEPROM_ADDR_ERR) {
|
||||
//bad address
|
||||
}
|
||||
else {
|
||||
//some other I2C error
|
||||
}
|
||||
}
|
||||
```
|
||||
### read(unsigned long addr)
|
||||
##### Description
|
||||
Reads a single byte from external EEPROM.
|
||||
##### Syntax
|
||||
`myEEPROM.read(unsigned long addr);`
|
||||
##### Parameters
|
||||
**addr** *(unsigned long)*: The EEPROM location to read from.
|
||||
##### Returns
|
||||
The data read from EEPROM or an error code *(int)*. To distinguish error values from valid data, error values are returned as negative numbers. See the [Arduino Wire.endTransmission() function](http://arduino.cc/en/Reference/WireEndTransmission) for a description of return codes. Returns a status of EEPROM_ADDR_ERR if the I/O would extend past the top of the EEPROM address space.
|
||||
##### Example
|
||||
```c++
|
||||
int myData;
|
||||
//read a byte from location 42
|
||||
int readValue = myEEPROM.read(42);
|
||||
if ( readValue < 0 ) {
|
||||
//there was a problem
|
||||
if ( -readValue == EEPROM_ADDR_ERR) {
|
||||
//bad address
|
||||
}
|
||||
else {
|
||||
//some other I2C error
|
||||
}
|
||||
}
|
||||
else {
|
||||
//data read ok
|
||||
}
|
||||
```
|
||||
|
||||
"Arduino External EEPROM Library" by Jack Christensen is licensed under [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/).
|
||||

|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
Copyright (c) 2014 Arduino. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and / or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110 - 1301 USA
|
||||
*/
|
||||
|
||||
#include "extEEPROM.h"
|
||||
|
||||
extEEPROM myEEPROM(kbits_256, 1, 64, 0x57);
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
SerialUSB.begin(115200);
|
||||
while (!SerialUSB) {
|
||||
;
|
||||
}
|
||||
|
||||
byte i2cStat = myEEPROM.begin(myEEPROM.twiClock100kHz);
|
||||
if ( i2cStat != 0 ) {
|
||||
SerialUSB.println(F("I2C Problem"));
|
||||
}
|
||||
|
||||
SerialUSB.println(
|
||||
F("EEPROM Memory commands: read:(a)(l)(r) , write:(a)(d)(w), next read data (n)"));
|
||||
SerialUSB.println(F("- Commands TO PRESS:"));
|
||||
SerialUSB.println(F("\t a : memory address to read / write"));
|
||||
SerialUSB.println(F("\t d : data to write"));
|
||||
SerialUSB.println(F("\t l : data to write"));
|
||||
SerialUSB.println(F("\t r : read command"));
|
||||
SerialUSB.println(F("\t w : write command"));
|
||||
}
|
||||
|
||||
unsigned long address = 0;
|
||||
const unsigned int maxDataSize = 1024; //0x8000; // 32 k bytes (32768 = 0x8000) = 256 kbits
|
||||
|
||||
byte data[maxDataSize] = {'p', 'i', 'p', 'p', 'o'};
|
||||
unsigned int dataSize = 5;
|
||||
|
||||
void eprom_read_write(bool write)
|
||||
{
|
||||
byte i2cStat = 0;
|
||||
if (write) {
|
||||
i2cStat = myEEPROM.write(address, data, dataSize);
|
||||
} else {
|
||||
memset(data, 0, maxDataSize);
|
||||
i2cStat = myEEPROM.read(address, data, dataSize);
|
||||
}
|
||||
if ( i2cStat != 0 ) {
|
||||
//there was a problem
|
||||
SerialUSB.print(F("I2C Problem: "));
|
||||
if ( i2cStat == EEPROM_ADDR_ERR) {
|
||||
SerialUSB.println(F("Wrong address"));
|
||||
} else {
|
||||
SerialUSB.print(F("I2C error: "));
|
||||
SerialUSB.print(i2cStat);
|
||||
SerialUSB.println(F(""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void parse(char inChar)
|
||||
{
|
||||
const char addr_len = 5;
|
||||
char addr_char[addr_len] = "";
|
||||
const char data_len = 3;
|
||||
char data_char[data_len] = "";
|
||||
char size_char[data_len] = "";
|
||||
char inc = 0, i = 0, j = 0;
|
||||
|
||||
switch (inChar) {
|
||||
case 'a':
|
||||
SerialUSB.print(F("Insert Address as 4 Hex chars (without '0x'): "));
|
||||
|
||||
while (i < 4) {
|
||||
while (SerialUSB.available() <= 0)
|
||||
;
|
||||
inc = SerialUSB.read();
|
||||
|
||||
if (inc == 'q') {
|
||||
return;
|
||||
}
|
||||
|
||||
addr_char[i] = inc;
|
||||
++i;
|
||||
}
|
||||
address = (unsigned long)strtol(addr_char, NULL, 16);
|
||||
SerialUSB.println(address);
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
SerialUSB.print(F("Insert Hex data sequence (without '0x'), return to enter: "));
|
||||
memset(data, 0, maxDataSize);
|
||||
while (true) {
|
||||
while (SerialUSB.available() <= 0)
|
||||
;
|
||||
inc = SerialUSB.read();
|
||||
if (inc == 'q') {
|
||||
return;
|
||||
}
|
||||
if (inc == '\r' || inc == '\n') {
|
||||
break;
|
||||
}
|
||||
|
||||
if (inc >= 'a' && inc <= 'f') {
|
||||
data[j] += inc - 'a' + 10;
|
||||
} else if (inc >= 'A' && inc <= 'F') {
|
||||
data[j] += inc - 'A' + 10;
|
||||
} else if (inc >= '0' && inc <= '9') {
|
||||
data[j] += inc - '0';
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (i % 2) {
|
||||
j++;
|
||||
} else {
|
||||
data[j] = data[j] << 4;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
dataSize = j;
|
||||
SerialUSB.println(dataSize);
|
||||
SerialUSB.println(F(""));
|
||||
break;
|
||||
case 'l':
|
||||
SerialUSB.print(F("Insert data len as 2 Hex chars (without '0x'): "));
|
||||
while (i < 2) {
|
||||
while (SerialUSB.available() <= 0)
|
||||
;
|
||||
inc = SerialUSB.read();
|
||||
if (inc == 'q') {
|
||||
return;
|
||||
}
|
||||
|
||||
size_char[i] = inc;
|
||||
++i;
|
||||
if (inc == '\n') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dataSize = (unsigned int)strtol(size_char, NULL, 16);
|
||||
SerialUSB.println(dataSize);
|
||||
break;
|
||||
|
||||
|
||||
case 'n':
|
||||
address += dataSize;
|
||||
/* FALLTHROUGH */
|
||||
case 'r':
|
||||
SerialUSB.print(F("reading address: "));
|
||||
SerialUSB.println(address, HEX);
|
||||
|
||||
eprom_read_write(false);
|
||||
for (i = 0; i < dataSize ; ++i) {
|
||||
SerialUSB.print(data[i], HEX);
|
||||
SerialUSB.print(F(" "));
|
||||
}
|
||||
SerialUSB.println();
|
||||
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
SerialUSB.print(F("writing at address: "));
|
||||
SerialUSB.print(address, HEX);
|
||||
SerialUSB.print(F(", len: "));
|
||||
SerialUSB.println(address, dataSize);
|
||||
for (i = 0; i < dataSize ; ++i) {
|
||||
SerialUSB.print(data[i], HEX);
|
||||
SerialUSB.print(F(" "));
|
||||
}
|
||||
eprom_read_write(true);
|
||||
SerialUSB.println();
|
||||
|
||||
break;
|
||||
case 'T':
|
||||
SerialUSB.println(F("Memory test: writing and verifying the whole memory"));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
if (SerialUSB.available() > 0) {
|
||||
char inChar = SerialUSB.read();
|
||||
SerialUSB.print(inChar);
|
||||
parse(inChar);
|
||||
}
|
||||
|
||||
delay(10);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
//Test extEEPROM library.
|
||||
//Writes the EEPROM full of 32-bit integers and reads them back to verify.
|
||||
//Wire a button from digital pin 6 to ground, this is used as a start button
|
||||
//so the sketch doesn't do unnecessary EEPROM writes every time it's reset.
|
||||
//Jack Christensen 09Jul2014
|
||||
//Paolo Paolucci 17Mar2016 (fix 28Jun2017)
|
||||
|
||||
#include <extEEPROM.h> //https://github.com/PaoloP74/extEEPROM
|
||||
|
||||
//One 24LC256 EEPROMs on the bus
|
||||
const uint32_t totalKBytes = 32; //for read and write test functions
|
||||
extEEPROM eep(kbits_256, 1, 64); //device size, number of devices, page size
|
||||
|
||||
const uint8_t btnStart = 6; //start button
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
pinMode(btnStart, INPUT_PULLUP);
|
||||
Serial.begin(115200);
|
||||
uint8_t eepStatus = eep.begin(eep.twiClock400kHz); //go fast!
|
||||
if (eepStatus) {
|
||||
Serial.print(F("extEEPROM.begin() failed, status = "));
|
||||
Serial.println(eepStatus);
|
||||
while (1);
|
||||
}
|
||||
|
||||
Serial.println(F("Press button to start..."));
|
||||
while (digitalRead(btnStart) == HIGH) {
|
||||
delay(10); //wait for button push
|
||||
}
|
||||
|
||||
uint8_t chunkSize =
|
||||
64; //this can be changed, but must be a multiple of 4 since we're writing 32-bit integers
|
||||
// eeErase(chunkSize, 0, totalKBytes * 1024 - 1);
|
||||
eeWrite(chunkSize);
|
||||
eeRead(chunkSize);
|
||||
|
||||
dump(0, 32); //the first 32 bytes
|
||||
dump(32256, 64); //the last 64 bytes
|
||||
//dump(32512, 64); //across the device boundary
|
||||
//dump(65520, 16); //the last 16 bytes
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
}
|
||||
|
||||
//write test data (32-bit integers) to eeprom, "chunk" bytes at a time
|
||||
void eeWrite(uint8_t chunk)
|
||||
{
|
||||
chunk &= 0xFC; //force chunk to be a multiple of 4
|
||||
uint8_t data[chunk];
|
||||
uint32_t val = 0;
|
||||
Serial.println(F("Writing..."));
|
||||
uint32_t msStart = millis();
|
||||
|
||||
for (uint32_t addr = 0; addr < totalKBytes * 1024; addr += chunk) {
|
||||
if ( (addr & 0xFFF) == 0 ) {
|
||||
Serial.println(addr);
|
||||
}
|
||||
for (uint8_t c = 0; c < chunk; c += 4) {
|
||||
data[c + 0] = val >> 24;
|
||||
data[c + 1] = val >> 16;
|
||||
data[c + 2] = val >> 8;
|
||||
data[c + 3] = val;
|
||||
++val;
|
||||
}
|
||||
eep.write(addr, data, chunk);
|
||||
}
|
||||
uint32_t msLapse = millis() - msStart;
|
||||
Serial.print(F("Write lapse: "));
|
||||
Serial.print(msLapse);
|
||||
Serial.println(F(" ms"));
|
||||
}
|
||||
|
||||
//read test data (32-bit integers) from eeprom, "chunk" bytes at a time
|
||||
void eeRead(uint8_t chunk)
|
||||
{
|
||||
chunk &= 0xFC; //force chunk to be a multiple of 4
|
||||
uint8_t data[chunk];
|
||||
uint32_t val = 0, testVal;
|
||||
Serial.println(F("Reading..."));
|
||||
uint32_t msStart = millis();
|
||||
|
||||
for (uint32_t addr = 0; addr < totalKBytes * 1024; addr += chunk) {
|
||||
if ( (addr & 0xFFF) == 0 ) {
|
||||
Serial.println(addr);
|
||||
}
|
||||
eep.read(addr, data, chunk);
|
||||
for (uint8_t c = 0; c < chunk; c += 4) {
|
||||
testVal = ((uint32_t)data[c + 0] << 24) + ((uint32_t)data[c + 1] << 16) + ((
|
||||
uint32_t)data[c + 2] << 8) + (uint32_t)data[c + 3];
|
||||
if (testVal != val) {
|
||||
Serial.print(F("Error @ addr "));
|
||||
Serial.print(addr + c);
|
||||
Serial.print(F(" Expected "));
|
||||
Serial.print(val);
|
||||
Serial.print(F(" Read "));
|
||||
Serial.print(testVal);
|
||||
Serial.print(F(" 0x"));
|
||||
Serial.println(testVal, HEX);
|
||||
}
|
||||
++val;
|
||||
}
|
||||
}
|
||||
uint32_t msLapse = millis() - msStart;
|
||||
Serial.print(F("Last value: "));
|
||||
Serial.print(val);
|
||||
Serial.print(F(" Read lapse: "));
|
||||
Serial.print(msLapse);
|
||||
Serial.println(F(" ms"));
|
||||
}
|
||||
|
||||
//write 0xFF to eeprom, "chunk" bytes at a time
|
||||
void eeErase(uint8_t chunk, uint32_t startAddr, uint32_t endAddr)
|
||||
{
|
||||
chunk &= 0xFC; //force chunk to be a multiple of 4
|
||||
uint8_t data[chunk];
|
||||
Serial.println(F("Erasing..."));
|
||||
for (int i = 0; i < chunk; i++) {
|
||||
data[i] = 0xFF;
|
||||
}
|
||||
uint32_t msStart = millis();
|
||||
|
||||
for (uint32_t a = startAddr; a <= endAddr; a += chunk) {
|
||||
if ( (a & 0xFFF) == 0 ) {
|
||||
Serial.println(a);
|
||||
}
|
||||
eep.write(a, data, chunk);
|
||||
}
|
||||
uint32_t msLapse = millis() - msStart;
|
||||
Serial.print(F("Erase lapse: "));
|
||||
Serial.print(msLapse);
|
||||
Serial.print(F(" ms"));
|
||||
}
|
||||
|
||||
//dump eeprom contents, 16 bytes at a time.
|
||||
//always dumps a multiple of 16 bytes.
|
||||
void dump(uint32_t startAddr, uint32_t nBytes)
|
||||
{
|
||||
Serial.print(F("EEPROM DUMP 0x"));
|
||||
Serial.print(startAddr, HEX);
|
||||
Serial.print(F(" 0x"));
|
||||
Serial.print(nBytes, HEX);
|
||||
Serial.print(F(" "));
|
||||
Serial.print(startAddr);
|
||||
Serial.print(F(" "));
|
||||
Serial.println(nBytes);
|
||||
uint32_t nRows = (nBytes + 15) >> 4;
|
||||
|
||||
uint8_t d[16];
|
||||
for (uint32_t r = 0; r < nRows; r++) {
|
||||
uint32_t a = startAddr + 16 * r;
|
||||
eep.read(a, d, 16);
|
||||
Serial.print(F("0x"));
|
||||
if ( a < 16 * 16 * 16 ) {
|
||||
Serial.print(F("0"));
|
||||
}
|
||||
if ( a < 16 * 16 ) {
|
||||
Serial.print(F("0"));
|
||||
}
|
||||
if ( a < 16 ) {
|
||||
Serial.print(F("0"));
|
||||
}
|
||||
Serial.print(a, HEX);
|
||||
Serial.print(F(" "));
|
||||
for ( int c = 0; c < 16; c++ ) {
|
||||
if ( d[c] < 16 ) {
|
||||
Serial.print(F("0"));
|
||||
Serial.print(d[c], HEX);
|
||||
Serial.print( c == 7 ? " " : " ");
|
||||
}
|
||||
}
|
||||
Serial.println(F(""));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
//Test extEEPROM library.
|
||||
//Writes the EEPROM full of 32-bit integers and reads them back to verify.
|
||||
//Wire a button from digital pin 6 to ground, this is used as a start button
|
||||
//so the sketch doesn't do unnecessary EEPROM writes every time it's reset.
|
||||
//Jack Christensen 09Jul2014
|
||||
//Paolo Paolucci 17Mar2016 (fix 28Jun2017)
|
||||
//Mik 03Jan2017 (configured Library to use the Wire1 as I2C channel)
|
||||
|
||||
#include <extEEPROM.h> //https://github.com/PaoloP74/extEEPROM
|
||||
|
||||
//One 24LC256 EEPROMs on the bus
|
||||
const uint32_t totalKBytes = 32; //for read and write test functions
|
||||
extEEPROM eep(kbits_256, 1, 64, 0x57); //device size, number of devices, page size
|
||||
|
||||
const uint8_t btnStart = 6; //start button
|
||||
|
||||
void setup(void)
|
||||
{
|
||||
uint8_t eepStatus;
|
||||
pinMode(btnStart, INPUT_PULLUP);
|
||||
Serial.begin(115200);
|
||||
while (!SerialUSB) {}
|
||||
|
||||
bool channelInsert = false;
|
||||
Serial.println(F("Select the number of Wire channel use the eeprom"));
|
||||
Serial.println(F("0 = Wire"));
|
||||
Serial.println(F("1 = Wire1"));
|
||||
Serial.println(F("...."));
|
||||
Serial.println(F("x = WIRE_INTERFACES_COUNT"));
|
||||
|
||||
do {
|
||||
if (Serial.available()) {
|
||||
char I2Cchannel = Serial.read();
|
||||
|
||||
// only number that are less than WIRE_INTERFACES_COUNT are allowed
|
||||
if ((I2Cchannel > '0') && (I2Cchannel < ('0' + WIRE_INTERFACES_COUNT))) {
|
||||
channelInsert = true;
|
||||
}
|
||||
|
||||
switch ((I2Cchannel - '0')) {
|
||||
|
||||
case 0:
|
||||
Serial.println(F("Using the default Wire interface"));
|
||||
eepStatus = eep.begin(eep.twiClock400kHz); //go fast!
|
||||
break;
|
||||
|
||||
case 1:
|
||||
Serial.println(F("Using the Wire1 interface"));
|
||||
eepStatus = eep.begin(eep.twiClock400kHz, &Wire1); //go fast!
|
||||
break;
|
||||
|
||||
/*
|
||||
Uncomment till the number of WIRE_INTERFACES_COUNT of your Arduino board
|
||||
case 2:
|
||||
Serial.println(F("Using the Wire2 interface"));
|
||||
eepStatus = eep.begin(eep.twiClock400kHz, &Wire2); //go fast!
|
||||
break;
|
||||
|
||||
case 3:
|
||||
Serial.println(F("Using the Wire3 interface"));
|
||||
eepStatus = eep.begin(eep.twiClock400kHz, &Wire3); //go fast!
|
||||
break;
|
||||
|
||||
case 4:
|
||||
Serial.println(F("Using the Wire4 interface"));
|
||||
eepStatus = eep.begin(eep.twiClock400kHz, &Wire4); //go fast!
|
||||
break;
|
||||
|
||||
case 5:
|
||||
Serial.println(F("Using the Wire5 interface"));
|
||||
eepStatus = eep.begin(eep.twiClock400kHz, &Wire5); //go fast!
|
||||
break;*/
|
||||
|
||||
default:
|
||||
Serial.println(F("A wrong channel has been inserted (Arduino manage max 5)"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (!channelInsert);
|
||||
|
||||
if (eepStatus) {
|
||||
Serial.print(F("extEEPROM.begin() failed, status = "));
|
||||
Serial.println(eepStatus);
|
||||
while (1);
|
||||
}
|
||||
|
||||
Serial.println(F("Started !!"));
|
||||
|
||||
uint8_t chunkSize =
|
||||
64; //this can be changed, but must be a multiple of 4 since we're writing 32-bit integers
|
||||
// eeErase(chunkSize, 0, totalKBytes * 1024 - 1);
|
||||
eeWrite(chunkSize);
|
||||
eeRead(chunkSize);
|
||||
|
||||
dump(0, 32); //the first 32 bytes
|
||||
dump(32256, 64); //the last 64 bytes
|
||||
//dump(32512, 64); //across the device boundary
|
||||
//dump(65520, 16); //the last 16 bytes
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
}
|
||||
|
||||
//write test data (32-bit integers) to eeprom, "chunk" bytes at a time
|
||||
void eeWrite(uint8_t chunk)
|
||||
{
|
||||
chunk &= 0xFC; //force chunk to be a multiple of 4
|
||||
uint8_t data[chunk];
|
||||
uint32_t val = 0;
|
||||
Serial.println(F("Writing..."));
|
||||
uint32_t msStart = millis();
|
||||
|
||||
for (uint32_t addr = 0; addr < totalKBytes * 1024; addr += chunk) {
|
||||
if ( (addr & 0xFFF) == 0 ) {
|
||||
Serial.println(addr);
|
||||
}
|
||||
for (uint8_t c = 0; c < chunk; c += 4) {
|
||||
data[c + 0] = val >> 24;
|
||||
data[c + 1] = val >> 16;
|
||||
data[c + 2] = val >> 8;
|
||||
data[c + 3] = val;
|
||||
++val;
|
||||
}
|
||||
eep.write(addr, data, chunk);
|
||||
}
|
||||
uint32_t msLapse = millis() - msStart;
|
||||
Serial.print(F("Write lapse: "));
|
||||
Serial.print(msLapse);
|
||||
Serial.println(F(" ms"));
|
||||
}
|
||||
|
||||
//read test data (32-bit integers) from eeprom, "chunk" bytes at a time
|
||||
void eeRead(uint8_t chunk)
|
||||
{
|
||||
chunk &= 0xFC; //force chunk to be a multiple of 4
|
||||
uint8_t data[chunk];
|
||||
uint32_t val = 0, testVal;
|
||||
Serial.println(F("Reading..."));
|
||||
uint32_t msStart = millis();
|
||||
|
||||
for (uint32_t addr = 0; addr < totalKBytes * 1024; addr += chunk) {
|
||||
if ( (addr & 0xFFF) == 0 ) {
|
||||
Serial.println(addr);
|
||||
}
|
||||
eep.read(addr, data, chunk);
|
||||
for (uint8_t c = 0; c < chunk; c += 4) {
|
||||
testVal = ((uint32_t)data[c + 0] << 24) + ((uint32_t)data[c + 1] << 16) + ((
|
||||
uint32_t)data[c + 2] << 8) + (uint32_t)data[c + 3];
|
||||
if (testVal != val) {
|
||||
Serial.print(F("Error @ addr "));
|
||||
Serial.print(addr + c);
|
||||
Serial.print(F(" Expected "));
|
||||
Serial.print(val);
|
||||
Serial.print(F(" Read "));
|
||||
Serial.print(testVal);
|
||||
Serial.print(F(" 0x"));
|
||||
Serial.println(testVal, HEX);
|
||||
}
|
||||
++val;
|
||||
}
|
||||
}
|
||||
uint32_t msLapse = millis() - msStart;
|
||||
Serial.print(F("Last value: "));
|
||||
Serial.print(val);
|
||||
Serial.print(F(" Read lapse: "));
|
||||
Serial.print(msLapse);
|
||||
Serial.println(F(" ms"));
|
||||
}
|
||||
|
||||
//write 0xFF to eeprom, "chunk" bytes at a time
|
||||
void eeErase(uint8_t chunk, uint32_t startAddr, uint32_t endAddr)
|
||||
{
|
||||
chunk &= 0xFC; //force chunk to be a multiple of 4
|
||||
uint8_t data[chunk];
|
||||
Serial.println(F("Erasing..."));
|
||||
for (int i = 0; i < chunk; i++) {
|
||||
data[i] = 0xFF;
|
||||
}
|
||||
uint32_t msStart = millis();
|
||||
|
||||
for (uint32_t a = startAddr; a <= endAddr; a += chunk) {
|
||||
if ( (a & 0xFFF) == 0 ) {
|
||||
Serial.println(a);
|
||||
}
|
||||
eep.write(a, data, chunk);
|
||||
}
|
||||
uint32_t msLapse = millis() - msStart;
|
||||
Serial.print(F("Erase lapse: "));
|
||||
Serial.print(msLapse);
|
||||
Serial.print(F(" ms"));
|
||||
}
|
||||
|
||||
//dump eeprom contents, 16 bytes at a time.
|
||||
//always dumps a multiple of 16 bytes.
|
||||
void dump(uint32_t startAddr, uint32_t nBytes)
|
||||
{
|
||||
Serial.print(F("EEPROM DUMP 0x"));
|
||||
Serial.print(startAddr, HEX);
|
||||
Serial.print(F(" 0x"));
|
||||
Serial.print(nBytes, HEX);
|
||||
Serial.print(F(" "));
|
||||
Serial.print(startAddr);
|
||||
Serial.print(F(" "));
|
||||
Serial.println(nBytes);
|
||||
uint32_t nRows = (nBytes + 15) >> 4;
|
||||
|
||||
uint8_t d[16];
|
||||
for (uint32_t r = 0; r < nRows; r++) {
|
||||
uint32_t a = startAddr + 16 * r;
|
||||
eep.read(a, d, 16);
|
||||
Serial.print(F("0x"));
|
||||
if ( a < 16 * 16 * 16 ) {
|
||||
Serial.print(F("0"));
|
||||
}
|
||||
if ( a < 16 * 16 ) {
|
||||
Serial.print(F("0"));
|
||||
}
|
||||
if ( a < 16 ) {
|
||||
Serial.print(F("0"));
|
||||
}
|
||||
Serial.print(a, HEX);
|
||||
Serial.print(F(" "));
|
||||
for ( int c = 0; c < 16; c++ ) {
|
||||
if ( d[c] < 16 ) {
|
||||
Serial.print(F("0"));
|
||||
Serial.print(d[c], HEX);
|
||||
Serial.print( c == 7 ? " " : " ");
|
||||
}
|
||||
}
|
||||
Serial.println(F(""));
|
||||
}
|
||||
}
|
||||
257
lib/MySensors/drivers/extEEPROM/extEEPROM.cpp
Normal file
257
lib/MySensors/drivers/extEEPROM/extEEPROM.cpp
Normal file
@@ -0,0 +1,257 @@
|
||||
/*-----------------------------------------------------------------------------*
|
||||
* extEEPROM.cpp - Arduino library to support external I2C EEPROMs. *
|
||||
* *
|
||||
* This library will work with most I2C serial EEPROM chips between 2k bits *
|
||||
* and 2048k bits (2M bits) in size. Multiple EEPROMs on the bus are supported *
|
||||
* as a single address space. I/O across block, page and device boundaries *
|
||||
* is supported. Certain assumptions are made regarding the EEPROM *
|
||||
* device addressing. These assumptions should be true for most EEPROMs *
|
||||
* but there are exceptions, so read the datasheet and know your hardware. *
|
||||
* *
|
||||
* The library should also work for EEPROMs smaller than 2k bits, assuming *
|
||||
* that there is only one EEPROM on the bus and also that the user is careful *
|
||||
* to not exceed the maximum address for the EEPROM. *
|
||||
* *
|
||||
* Library tested with: *
|
||||
* Microchip 24AA02E48 (2k bit) *
|
||||
* 24xx32 (32k bit, thanks to Richard M) *
|
||||
* Microchip 24LC256 (256k bit) *
|
||||
* Microchip 24FC1026 (1M bit, thanks to Gabriele B on the Arduino forum) *
|
||||
* ST Micro M24M02 (2M bit) *
|
||||
* *
|
||||
* Library will NOT work with Microchip 24xx1025 as its control byte does not *
|
||||
* conform to the following assumptions. *
|
||||
* *
|
||||
* Device addressing assumptions: *
|
||||
* 1. The I2C address sequence consists of a control byte followed by one *
|
||||
* address byte (for EEPROMs <= 16k bits) or two address bytes (for *
|
||||
* EEPROMs > 16k bits). *
|
||||
* 2. The three least-significant bits in the control byte (excluding the R/W *
|
||||
* bit) comprise the three most-significant bits for the entire address *
|
||||
* space, i.e. all chips on the bus. As such, these may be chip-select *
|
||||
* bits or block-select bits (for individual chips that have an internal *
|
||||
* block organization), or a combination of both (in which case the *
|
||||
* block-select bits must be of lesser significance than the chip-select *
|
||||
* bits). *
|
||||
* 3. Regardless of the number of bits needed to address the entire address *
|
||||
* space, the three most-significant bits always go in the control byte. *
|
||||
* Depending on EEPROM device size, this may result in one or more of the *
|
||||
* most significant bits in the I2C address bytes being unused (or "don't *
|
||||
* care"). *
|
||||
* 4. An EEPROM contains an integral number of pages. *
|
||||
* *
|
||||
* To use the extEEPROM library, the Arduino Wire library must also *
|
||||
* be included. *
|
||||
* *
|
||||
* Jack Christensen 23Mar2013 v1 *
|
||||
* 29Mar2013 v2 - Updated to span page boundaries (and therefore also *
|
||||
* device boundaries, assuming an integral number of pages per device) *
|
||||
* 08Jul2014 v3 - Generalized for 2kb - 2Mb EEPROMs. *
|
||||
* *
|
||||
* Paolo Paolucci 22-10-2015 v3.1 *
|
||||
* 09-01-2016 v3.2 Add update function. *
|
||||
* *
|
||||
* External EEPROM Library by Jack Christensen is licensed under CC BY-SA 4.0, *
|
||||
* http://creativecommons.org/licenses/by-sa/4.0/ *
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
#include "extEEPROM.h"
|
||||
|
||||
// workaround, BUFFER_LENGTH is not defined in Wire.h for SAMD controllers
|
||||
#ifndef BUFFER_LENGTH
|
||||
#define BUFFER_LENGTH 32
|
||||
#endif
|
||||
|
||||
// Constructor.
|
||||
// - deviceCapacity is the capacity of a single EEPROM device in
|
||||
// kilobits (kb) and should be one of the values defined in the
|
||||
// eeprom_size_t enumeration in the extEEPROM.h file. (Most
|
||||
// EEPROM manufacturers use kbits in their part numbers.)
|
||||
// - nDevice is the number of EEPROM devices on the I2C bus (all must
|
||||
// be identical).
|
||||
// - pageSize is the EEPROM's page size in bytes.
|
||||
// - eepromAddr is the EEPROM's I2C address and defaults to 0x50 which is common.
|
||||
extEEPROM::extEEPROM(eeprom_size_t deviceCapacity, byte nDevice, unsigned int pageSize,
|
||||
uint8_t eepromAddr)
|
||||
{
|
||||
communication = NULL;
|
||||
_dvcCapacity = deviceCapacity;
|
||||
_nDevice = nDevice;
|
||||
_pageSize = pageSize;
|
||||
_eepromAddr = eepromAddr;
|
||||
_totalCapacity = _nDevice * _dvcCapacity * 1024UL / 8;
|
||||
_nAddrBytes = deviceCapacity > kbits_16 ? 2 :
|
||||
1; //two address bytes needed for eeproms > 16kbits
|
||||
|
||||
//determine the bitshift needed to isolate the chip select bits from the address to put into the control byte
|
||||
uint16_t kb = _dvcCapacity;
|
||||
if ( kb <= kbits_16 ) {
|
||||
_csShift = 8;
|
||||
} else if ( kb >= kbits_512 ) {
|
||||
_csShift = 16;
|
||||
} else {
|
||||
kb >>= 6;
|
||||
_csShift = 12;
|
||||
while ( kb >= 1 ) {
|
||||
++_csShift;
|
||||
kb >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//initialize the I2C bus and do a dummy write (no data sent)
|
||||
//to the device so that the caller can determine whether it is responding.
|
||||
//when using a 400kHz bus speed and there are multiple I2C devices on the
|
||||
//bus (other than EEPROM), call extEEPROM::begin() after any initialization
|
||||
//calls for the other devices to ensure the intended I2C clock speed is set.
|
||||
byte extEEPROM::begin(twiClockFreq_t twiFreq, TwoWire *_comm)
|
||||
{
|
||||
communication = _comm;
|
||||
communication->begin();
|
||||
communication->setClock(twiFreq);
|
||||
communication->beginTransmission(_eepromAddr);
|
||||
if (_nAddrBytes == 2) {
|
||||
communication->write((byte)0); //high addr byte
|
||||
}
|
||||
communication->write((byte)0); //low addr byte
|
||||
return communication->endTransmission();
|
||||
}
|
||||
|
||||
//Write bytes to external EEPROM.
|
||||
//If the I/O would extend past the top of the EEPROM address space,
|
||||
//a status of EEPROM_ADDR_ERR is returned. For I2C errors, the status
|
||||
//from the Arduino Wire library is passed back through to the caller.
|
||||
byte extEEPROM::write(unsigned long addr, byte *values, unsigned int nBytes)
|
||||
{
|
||||
uint8_t txStatus = 0; //transmit status
|
||||
|
||||
if (addr + nBytes > _totalCapacity) { //will this write go past the top of the EEPROM?
|
||||
return EEPROM_ADDR_ERR; //yes, tell the caller
|
||||
}
|
||||
|
||||
while (nBytes > 0) {
|
||||
const uint16_t nPage = _pageSize - ( addr & (_pageSize - 1) );
|
||||
//find min(nBytes, nPage, BUFFER_LENGTH) -- BUFFER_LENGTH is defined in the Wire library.
|
||||
uint16_t nWrite = nBytes < nPage ? nBytes : nPage;
|
||||
nWrite = BUFFER_LENGTH - _nAddrBytes < nWrite ? BUFFER_LENGTH - _nAddrBytes : nWrite;
|
||||
const uint8_t ctrlByte = _eepromAddr | (byte) (addr >> _csShift);
|
||||
communication->beginTransmission(ctrlByte);
|
||||
if (_nAddrBytes == 2) {
|
||||
communication->write( (byte) (addr >> 8) ); //high addr byte
|
||||
}
|
||||
communication->write( (byte) addr ); //low addr byte
|
||||
communication->write(values, nWrite);
|
||||
txStatus = communication->endTransmission();
|
||||
if (txStatus != 0) {
|
||||
return txStatus;
|
||||
}
|
||||
|
||||
//wait up to 50ms for the write to complete
|
||||
for (uint8_t i=100; i; --i) {
|
||||
delayMicroseconds(500); //no point in waiting too fast
|
||||
communication->beginTransmission(ctrlByte);
|
||||
if (_nAddrBytes == 2) {
|
||||
communication->write((byte)0); //high addr byte
|
||||
}
|
||||
communication->write((byte)0); //low addr byte
|
||||
txStatus = communication->endTransmission();
|
||||
if (txStatus == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (txStatus != 0) {
|
||||
return txStatus;
|
||||
}
|
||||
|
||||
addr += nWrite; //increment the EEPROM address
|
||||
values += nWrite; //increment the input data pointer
|
||||
nBytes -= nWrite; //decrement the number of bytes left to write
|
||||
}
|
||||
return txStatus;
|
||||
}
|
||||
|
||||
//Read bytes from external EEPROM.
|
||||
//If the I/O would extend past the top of the EEPROM address space,
|
||||
//a status of EEPROM_ADDR_ERR is returned. For I2C errors, the status
|
||||
//from the Arduino Wire library is passed back through to the caller.
|
||||
byte extEEPROM::read(unsigned long addr, byte *values, unsigned int nBytes)
|
||||
{
|
||||
if (addr + nBytes > _totalCapacity) { //will this read take us past the top of the EEPROM?
|
||||
return EEPROM_ADDR_ERR; //yes, tell the caller
|
||||
}
|
||||
|
||||
while (nBytes > 0) {
|
||||
const uint16_t nPage = _pageSize - ( addr & (_pageSize - 1) );
|
||||
uint16_t nRead = nBytes < nPage ? nBytes : nPage;
|
||||
nRead = BUFFER_LENGTH < nRead ? BUFFER_LENGTH : nRead;
|
||||
byte ctrlByte = _eepromAddr | (byte) (addr >> _csShift);
|
||||
communication->beginTransmission(ctrlByte);
|
||||
if (_nAddrBytes == 2) {
|
||||
communication->write( (byte) (addr >> 8) ); //high addr byte
|
||||
}
|
||||
communication->write( (byte) addr ); //low addr byte
|
||||
const byte rxStatus = communication->endTransmission();
|
||||
if (rxStatus != 0) {
|
||||
return rxStatus; //read error
|
||||
}
|
||||
|
||||
communication->requestFrom(ctrlByte, nRead);
|
||||
for (byte i=0; i<nRead; i++) {
|
||||
values[i] = communication->read();
|
||||
}
|
||||
|
||||
addr += nRead; //increment the EEPROM address
|
||||
values += nRead; //increment the input data pointer
|
||||
nBytes -= nRead; //decrement the number of bytes left to write
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Write a single byte to external EEPROM.
|
||||
//If the I/O would extend past the top of the EEPROM address space,
|
||||
//a status of EEPROM_ADDR_ERR is returned. For I2C errors, the status
|
||||
//from the Arduino Wire library is passed back through to the caller.
|
||||
byte extEEPROM::write(unsigned long addr, byte value)
|
||||
{
|
||||
return write(addr, &value, 1);
|
||||
}
|
||||
|
||||
//Read a single byte from external EEPROM.
|
||||
//If the I/O would extend past the top of the EEPROM address space,
|
||||
//a status of EEPROM_ADDR_ERR is returned. For I2C errors, the status
|
||||
//from the Arduino Wire library is passed back through to the caller.
|
||||
//To distinguish error values from valid data, error values are returned as negative numbers.
|
||||
int extEEPROM::read(unsigned long addr)
|
||||
{
|
||||
uint8_t data;
|
||||
int ret;
|
||||
|
||||
ret = read(addr, &data, 1);
|
||||
return ret == 0 ? data : -ret;
|
||||
}
|
||||
|
||||
//Update bytes to external EEPROM.
|
||||
//For I2C errors, the status from the Arduino Wire library is passed back through to the caller.
|
||||
byte extEEPROM::update(unsigned long addr, byte *values, unsigned int nBytes)
|
||||
{
|
||||
for (unsigned int i = 0; i < nBytes; i++) {
|
||||
const uint8_t newValue = values[i];
|
||||
if (newValue != read(addr + i)) {
|
||||
write(addr + i, newValue);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//Update a single byte to external EEPROM.
|
||||
//For I2C errors, the status from the Arduino Wire library is passed back through to the caller.
|
||||
byte extEEPROM::update(unsigned long addr, byte value)
|
||||
{
|
||||
return (value != read(addr) ? write(addr, &value, 1) : 0);
|
||||
}
|
||||
|
||||
//For I2C errors, the status from the Arduino Wire library is passed back through to the caller.
|
||||
unsigned long extEEPROM::length( void )
|
||||
{
|
||||
return _totalCapacity * 8;
|
||||
}
|
||||
131
lib/MySensors/drivers/extEEPROM/extEEPROM.h
Normal file
131
lib/MySensors/drivers/extEEPROM/extEEPROM.h
Normal file
@@ -0,0 +1,131 @@
|
||||
/*-----------------------------------------------------------------------------*
|
||||
* extEEPROM.h - Arduino library to support external I2C EEPROMs. *
|
||||
* *
|
||||
* This library will work with most I2C serial EEPROM chips between 2k bits *
|
||||
* and 2048k bits (2M bits) in size. Multiple EEPROMs on the bus are supported *
|
||||
* as a single address space. I/O across block, page and device boundaries *
|
||||
* is supported. Certain assumptions are made regarding the EEPROM *
|
||||
* device addressing. These assumptions should be true for most EEPROMs *
|
||||
* but there are exceptions, so read the datasheet and know your hardware. *
|
||||
* *
|
||||
* The library should also work for EEPROMs smaller than 2k bits, assuming *
|
||||
* that there is only one EEPROM on the bus and also that the user is careful *
|
||||
* to not exceed the maximum address for the EEPROM. *
|
||||
* *
|
||||
* Library tested with: *
|
||||
* Microchip 24AA02E48 (2k bit) *
|
||||
* 24xx32 (32k bit, thanks to Richard M) *
|
||||
* Microchip 24LC256 (256k bit) *
|
||||
* Microchip 24FC1026 (1M bit, thanks to Gabriele B on the Arduino forum) *
|
||||
* ST Micro M24M02 (2M bit) *
|
||||
* *
|
||||
* Library will NOT work with Microchip 24xx1025 as its control byte does not *
|
||||
* conform to the following assumptions. *
|
||||
* *
|
||||
* Device addressing assumptions: *
|
||||
* 1. The I2C address sequence consists of a control byte followed by one *
|
||||
* address byte (for EEPROMs <= 16k bits) or two address bytes (for *
|
||||
* EEPROMs > 16k bits). *
|
||||
* 2. The three least-significant bits in the control byte (excluding the R/W *
|
||||
* bit) comprise the three most-significant bits for the entire address *
|
||||
* space, i.e. all chips on the bus. As such, these may be chip-select *
|
||||
* bits or block-select bits (for individual chips that have an internal *
|
||||
* block organization), or a combination of both (in which case the *
|
||||
* block-select bits must be of lesser significance than the chip-select *
|
||||
* bits). *
|
||||
* 3. Regardless of the number of bits needed to address the entire address *
|
||||
* space, the three most-significant bits always go in the control byte. *
|
||||
* Depending on EEPROM device size, this may result in one or more of the *
|
||||
* most significant bits in the I2C address bytes being unused (or "don't *
|
||||
* care"). *
|
||||
* 4. An EEPROM contains an integral number of pages. *
|
||||
* *
|
||||
* To use the extEEPROM library, the Arduino Wire library must also *
|
||||
* be included. *
|
||||
* *
|
||||
* Jack Christensen 23Mar2013 v1 *
|
||||
* 29Mar2013 v2 - Updated to span page boundaries (and therefore also *
|
||||
* device boundaries, assuming an integral number of pages per device) *
|
||||
* 08Jul2014 v3 - Generalized for 2kb - 2Mb EEPROMs. *
|
||||
* *
|
||||
* Paolo Paolucci 22-10-2015 v3.2 *
|
||||
* 09-01-2016 v3.2 Add update function. *
|
||||
* *
|
||||
* External EEPROM Library by Jack Christensen is licensed under CC BY-SA 4.0, *
|
||||
* http://creativecommons.org/licenses/by-sa/4.0/ *
|
||||
*-----------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* tekka 2018:
|
||||
* Re-implementing extEEPROM::update(unsigned long addr, byte value);
|
||||
*/
|
||||
#ifndef extEEPROM_h
|
||||
#define extEEPROM_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
|
||||
//EEPROM size in kilobits. EEPROM part numbers are usually designated in k-bits.
|
||||
enum eeprom_size_t {
|
||||
kbits_2 = 2,
|
||||
kbits_4 = 4,
|
||||
kbits_8 = 8,
|
||||
kbits_16 = 16,
|
||||
kbits_32 = 32,
|
||||
kbits_64 = 64,
|
||||
kbits_128 = 128,
|
||||
kbits_256 = 256,
|
||||
kbits_512 = 512,
|
||||
kbits_1024 = 1024,
|
||||
kbits_2048 = 2048
|
||||
};
|
||||
|
||||
//EEPROM addressing error, returned by write() or read() if upper address bound is exceeded
|
||||
const uint8_t EEPROM_ADDR_ERR = 9;
|
||||
|
||||
/** extEEPROM class */
|
||||
class extEEPROM
|
||||
{
|
||||
private:
|
||||
// the private attribute used to comunicate with the correct I2C SERCOM
|
||||
TwoWire *communication;
|
||||
|
||||
public:
|
||||
/**
|
||||
* I2C clock frequencies
|
||||
*/
|
||||
enum twiClockFreq_t {
|
||||
twiClock100kHz = 100000, //!< twiClock100kHz
|
||||
twiClock400kHz = 400000 //!< twiClock400kHz
|
||||
};
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param deviceCapacity
|
||||
* @param nDevice
|
||||
* @param pageSize
|
||||
* @param eepromAddr
|
||||
*/
|
||||
extEEPROM(eeprom_size_t deviceCapacity, byte nDevice, unsigned int pageSize,
|
||||
byte eepromAddr = 0x50);
|
||||
|
||||
// It is ready for every I2C Sercom, by default use the main Wire
|
||||
byte begin(twiClockFreq_t twiFreq = twiClock100kHz, TwoWire *_comm=&Wire); //!< begin()
|
||||
byte write(unsigned long addr, byte *values, unsigned int nBytes); //!< write()
|
||||
byte write(unsigned long addr, byte value); //!< write()
|
||||
byte read(unsigned long addr, byte *values, unsigned int nBytes); //!< read()
|
||||
int read(unsigned long addr); //!< read()
|
||||
byte update(unsigned long addr, byte *values, unsigned int nBytes); //!< update()
|
||||
byte update(unsigned long addr, byte value); //!< update()
|
||||
unsigned long length(); //!< length()
|
||||
|
||||
private:
|
||||
uint8_t _eepromAddr; //eeprom i2c address
|
||||
uint16_t _dvcCapacity; //capacity of one EEPROM device, in kbits
|
||||
uint8_t _nDevice; //number of devices on the bus
|
||||
uint16_t _pageSize; //page size in bytes
|
||||
uint8_t _csShift; //number of bits to shift address for chip select bits in control byte
|
||||
uint16_t _nAddrBytes; //number of address bytes (1 or 2)
|
||||
unsigned long _totalCapacity; //capacity of all EEPROM devices on the bus, in bytes
|
||||
};
|
||||
|
||||
#endif
|
||||
6
lib/MySensors/drivers/extEEPROM/keywords.txt
Normal file
6
lib/MySensors/drivers/extEEPROM/keywords.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
extEEPROM KEYWORD1
|
||||
begin KEYWORD2
|
||||
write KEYWORD2
|
||||
read KEYWORD2
|
||||
update KEYWORD2
|
||||
length KEYWORD2
|
||||
Reference in New Issue
Block a user