Compare commits
1 Commits
2152b8f727
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 089be9564b |
+13
-2
@@ -22,9 +22,20 @@ int8_t adc_Initialize(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void adc_Disable(void) { ADCSRA &= ~(1 << ADEN); }
|
// Power (PRR clock gate) and ADEN are managed together, so the ADC draws
|
||||||
|
// nothing between readings. Enable rewrites the full config because register
|
||||||
|
// access is unreliable while the clock is gated.
|
||||||
|
void adc_Disable(void)
|
||||||
|
{
|
||||||
|
ADCSRA &= ~(1 << ADEN);
|
||||||
|
power_adc_disable();
|
||||||
|
}
|
||||||
|
|
||||||
void adc_Enable(void) { ADCSRA |= (1 << ADEN); }
|
void adc_Enable(void)
|
||||||
|
{
|
||||||
|
power_adc_enable();
|
||||||
|
ADCSRA = (1 << ADEN) | ADC_PRESCALER_64;
|
||||||
|
}
|
||||||
|
|
||||||
void adc_StartConversion(uint8_t channel)
|
void adc_StartConversion(uint8_t channel)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#define ADC_H
|
#define ADC_H
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
#include <avr/io.h>
|
#include <avr/io.h>
|
||||||
|
#include <avr/power.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <util/delay.h>
|
#include <util/delay.h>
|
||||||
|
|||||||
+5
-3
@@ -28,10 +28,12 @@ extern "C" {
|
|||||||
#define DO_UART true
|
#define DO_UART true
|
||||||
#define ITERATING false
|
#define ITERATING false
|
||||||
|
|
||||||
// Serial log line, compiled out entirely when DO_UART is off. The caller's
|
// Serial log line, compiled out entirely when DO_UART is off. Takes a string
|
||||||
// file must include uart.h (directly or via another driver header).
|
// LITERAL only: PSTR keeps the text in flash instead of copying it into RAM at
|
||||||
|
// boot. The caller's file must include uart.h (directly or via another driver
|
||||||
|
// header). Use uart_sendString() for runtime strings.
|
||||||
#if DO_UART
|
#if DO_UART
|
||||||
#define LOG(msg) uart_sendString(msg)
|
#define LOG(msg) uart_sendString_P(PSTR(msg))
|
||||||
#else
|
#else
|
||||||
#define LOG(msg) ((void)0)
|
#define LOG(msg) ((void)0)
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -56,9 +56,11 @@ void wdt_isr_enable(void)
|
|||||||
MCUSR &= ~(1 << WDRF);
|
MCUSR &= ~(1 << WDRF);
|
||||||
WDTCSR = (1 << WDCE) | (1 << WDE);
|
WDTCSR = (1 << WDCE) | (1 << WDE);
|
||||||
|
|
||||||
// WDP[3:0] = 0b011 -> 0.125 s. Interrupt mode only (WDE clear), so an
|
// WDP[3:0] = 0b010 -> 64 ms debounce, capping the count at ~15 rev/s;
|
||||||
// expiry wakes us to clear the debounce instead of resetting the part.
|
// a reed contact settles in a few ms, so this is generous. Interrupt mode
|
||||||
WDTCSR = (1 << WDIE) | (1 << WDP1) | (1 << WDP0);
|
// only (WDE clear): an expiry wakes us to clear the debounce instead of
|
||||||
|
// resetting the part.
|
||||||
|
WDTCSR = (1 << WDIE) | (1 << WDP1);
|
||||||
|
|
||||||
SREG = sreg; // Restore, never blanket-sei(): these run inside an ISR
|
SREG = sreg; // Restore, never blanket-sei(): these run inside an ISR
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-2
@@ -144,8 +144,12 @@ void write_struct_to_last_page(tx_rx_data_struct tx_data_in)
|
|||||||
{
|
{
|
||||||
uint8_t page_val = get_last_page();
|
uint8_t page_val = get_last_page();
|
||||||
uint8_t next_page_val;
|
uint8_t next_page_val;
|
||||||
if (page_val == 255) {
|
if (page_val >= EEPROM_MAX_PAGE) {
|
||||||
next_page_val = 1;
|
// Spool full (~2.6 days of failed sends). Overwrite the newest spooled
|
||||||
|
// packet: one packet is lost either way, and this keeps the depth
|
||||||
|
// truthful -- the old wrap to page 1 stranded 254 packets the counter
|
||||||
|
// no longer admitted to.
|
||||||
|
next_page_val = EEPROM_MAX_PAGE;
|
||||||
} else {
|
} else {
|
||||||
next_page_val = page_val + 1;
|
next_page_val = page_val + 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,9 @@
|
|||||||
#define EEPROM_RDLS 0b10000011 // 0x83
|
#define EEPROM_RDLS 0b10000011 // 0x83
|
||||||
#define EEPROM_LID 0b10000010 // 0x82
|
#define EEPROM_LID 0b10000010 // 0x82
|
||||||
#define PAGE_SIZE 64
|
#define PAGE_SIZE 64
|
||||||
|
// M95128 is 16 KB = 256 x 64-byte pages; page 0 holds the spool depth, so
|
||||||
|
// pages 1..255 hold packets.
|
||||||
|
#define EEPROM_MAX_PAGE 255
|
||||||
#define EEPROM_STATUS_WIP 0x01
|
#define EEPROM_STATUS_WIP 0x01
|
||||||
|
|
||||||
// A page write takes ~5 ms; past this the device is not responding.
|
// A page write takes ~5 ms; past this the device is not responding.
|
||||||
|
|||||||
+72
-21
@@ -52,6 +52,17 @@
|
|||||||
// Erased EEPROM reads back as 0xFF; anything else is a real spool depth.
|
// Erased EEPROM reads back as 0xFF; anything else is a real spool depth.
|
||||||
#define EEPROM_LAST_PAGE_UNINIT 0xFF
|
#define EEPROM_LAST_PAGE_UNINIT 0xFF
|
||||||
|
|
||||||
|
// The NFC identity is cached; re-read the tag every 4th send (~1 h), so a
|
||||||
|
// renamed nugget still takes effect without a reset.
|
||||||
|
#define TAG_REREAD_SEND_CYCLES 4
|
||||||
|
|
||||||
|
// Re-request the time daily even when the RTC is running, to bound its drift.
|
||||||
|
#define TIME_RESYNC_SEND_CYCLES 96 // 96 x 15 min = 24 h
|
||||||
|
|
||||||
|
// How many spooled packets one successful cycle may retry, so a huge backlog
|
||||||
|
// cannot keep the node awake for minutes.
|
||||||
|
#define SPOOL_DRAIN_MAX 10
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// State shared with the interrupt handlers
|
// State shared with the interrupt handlers
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -63,6 +74,8 @@ static volatile uint16_t wheel_counts[WHEEL_COUNT_SLOTS]; // Revolutions per min
|
|||||||
|
|
||||||
// Whether we ever got a valid timestamp from the base station
|
// Whether we ever got a valid timestamp from the base station
|
||||||
static RTC_RFM69_STATUS time_sync_status;
|
static RTC_RFM69_STATUS time_sync_status;
|
||||||
|
static uint8_t sends_since_tag_read = 0;
|
||||||
|
static uint8_t sends_since_time_sync = 0;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Interrupt handlers
|
// Interrupt handlers
|
||||||
@@ -155,16 +168,33 @@ static void wake_peripheral_rails(void)
|
|||||||
// Measurement helpers
|
// Measurement helpers
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
// Battery level via the internal 1.1 V bandgap: the first conversions after
|
// Battery voltage in millivolts, measured by reading the 1.1 V internal
|
||||||
// enabling the ADC read low, so take three and keep the last.
|
// bandgap against the AVcc (battery) reference: Vcc = 1100 mV * 1023 / raw.
|
||||||
static uint16_t read_battery_level(void)
|
// The first conversions after enabling the ADC read low, so take three and
|
||||||
|
// keep the last. Returns 0 when the ADC fails, which the base station can
|
||||||
|
// recognise as "no reading".
|
||||||
|
static uint16_t read_battery_millivolts(void)
|
||||||
{
|
{
|
||||||
adc_Enable();
|
adc_Enable();
|
||||||
adc_GetConversion(ADC_CHANNEL_BANDGAP);
|
adc_GetConversion(ADC_CHANNEL_BANDGAP);
|
||||||
adc_GetConversion(ADC_CHANNEL_BANDGAP);
|
adc_GetConversion(ADC_CHANNEL_BANDGAP);
|
||||||
uint16_t level = adc_GetConversion(ADC_CHANNEL_BANDGAP);
|
uint16_t raw = adc_GetConversion(ADC_CHANNEL_BANDGAP);
|
||||||
adc_Disable();
|
adc_Disable();
|
||||||
return level;
|
|
||||||
|
if (raw == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (uint16_t)((1100UL * 1023UL) / raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spread nodes out: a name-hash-derived delay (0-236 ms) before transmitting
|
||||||
|
// keeps two nodes that woke on the same RTC second from colliding on every
|
||||||
|
// single cycle.
|
||||||
|
static void tx_backoff_delay(void)
|
||||||
|
{
|
||||||
|
for (uint8_t i = 0; i < IDENTIFIER.hashed; i++) {
|
||||||
|
_delay_ms(4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Atomically hand out the collected counts and start the next collection
|
// Atomically hand out the collected counts and start the next collection
|
||||||
@@ -186,19 +216,30 @@ static void take_counts_snapshot(uint16_t snapshot[WHEEL_COUNT_SLOTS])
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
// Build and send the periodic counts packet. Unacknowledged packets go to the
|
// Build and send the periodic counts packet. Unacknowledged packets go to the
|
||||||
// EEPROM spool; each acknowledged one buys a retry of one spooled packet.
|
// EEPROM spool; each acknowledged send buys retries of spooled packets.
|
||||||
static void send_wheel_counts_report(void)
|
static void send_wheel_counts_report(void)
|
||||||
{
|
{
|
||||||
rfm69_init();
|
rfm69_init();
|
||||||
rfid_set_low_power_down(false);
|
|
||||||
_delay_ms(1);
|
|
||||||
|
|
||||||
// Re-read the tag each period so a renamed nugget takes effect without a
|
// Reading the tag costs an I2C transaction and a tag power-up, so use the
|
||||||
// reset.
|
// cached identity and only re-read about once an hour -- or immediately, if
|
||||||
|
// the last read failed to parse.
|
||||||
|
sends_since_tag_read++;
|
||||||
|
if ((sends_since_tag_read >= TAG_REREAD_SEND_CYCLES) || (NDEF_MSG.success != 0)) {
|
||||||
IDENTIFIER = get_nugget_data();
|
IDENTIFIER = get_nugget_data();
|
||||||
|
sends_since_tag_read = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (time_sync_status == RTC_RFM69_SET_TIME_FAILED) {
|
// Sync time when we never got it, and re-sync daily to bound RTC drift.
|
||||||
|
if (sends_since_time_sync < 255) {
|
||||||
|
sends_since_time_sync++;
|
||||||
|
}
|
||||||
|
if ((time_sync_status == RTC_RFM69_SET_TIME_FAILED)
|
||||||
|
|| (sends_since_time_sync >= TIME_RESYNC_SEND_CYCLES)) {
|
||||||
time_sync_status = set_time_from_rfm69(IDENTIFIER);
|
time_sync_status = set_time_from_rfm69(IDENTIFIER);
|
||||||
|
if (time_sync_status == RTC_RFM69_SET_TIME_SUCCESS) {
|
||||||
|
sends_since_time_sync = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t counts_snapshot[WHEEL_COUNT_SLOTS];
|
uint16_t counts_snapshot[WHEEL_COUNT_SLOTS];
|
||||||
@@ -206,12 +247,13 @@ static void send_wheel_counts_report(void)
|
|||||||
|
|
||||||
reset_txrx_struct(&TX_DATA);
|
reset_txrx_struct(&TX_DATA);
|
||||||
TX_DATA = generate_wheel_counts_message(
|
TX_DATA = generate_wheel_counts_message(
|
||||||
IDENTIFIER, rtc_read_time(), read_battery_level(), counts_snapshot);
|
IDENTIFIER, rtc_read_time(), read_battery_millivolts(), counts_snapshot);
|
||||||
LOG("TX DATA Sent\n");
|
LOG("TX DATA Sent\n");
|
||||||
#if DO_UART
|
#if DO_UART
|
||||||
uart_print_tx_rx_data(TX_DATA);
|
uart_print_tx_rx_data(TX_DATA);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
tx_backoff_delay();
|
||||||
DATA_SEND_STATUS result = send_message(TX_DATA);
|
DATA_SEND_STATUS result = send_message(TX_DATA);
|
||||||
if (result == DATA_NOT_SENT) {
|
if (result == DATA_NOT_SENT) {
|
||||||
LOG(" TX DATA not sent, writing to SPI\n");
|
LOG(" TX DATA not sent, writing to SPI\n");
|
||||||
@@ -219,14 +261,16 @@ static void send_wheel_counts_report(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The base station is listening -- use the chance to drain one packet from
|
// The base station is listening -- drain the spool while sends keep
|
||||||
// the spool.
|
// succeeding, capped at SPOOL_DRAIN_MAX per cycle. At one per cycle a long
|
||||||
if (get_last_page() > 0) {
|
// outage took days to catch up.
|
||||||
|
for (uint8_t drained = 0; (drained < SPOOL_DRAIN_MAX) && (get_last_page() > 0); drained++) {
|
||||||
reset_txrx_struct(&TX_DATA);
|
reset_txrx_struct(&TX_DATA);
|
||||||
TX_DATA = read_struct_last_page();
|
TX_DATA = read_struct_last_page();
|
||||||
TX_DATA.flags = MSG_RESENT_COUNTS;
|
TX_DATA.flags = MSG_RESENT_COUNTS;
|
||||||
_delay_ms(250);
|
_delay_ms(250);
|
||||||
|
|
||||||
|
tx_backoff_delay();
|
||||||
result = send_message(TX_DATA);
|
result = send_message(TX_DATA);
|
||||||
LOG("TX DATA From SPI Memory\n");
|
LOG("TX DATA From SPI Memory\n");
|
||||||
#if DO_UART
|
#if DO_UART
|
||||||
@@ -235,11 +279,11 @@ static void send_wheel_counts_report(void)
|
|||||||
|
|
||||||
// Only drop the spooled page once it is actually acknowledged;
|
// Only drop the spooled page once it is actually acknowledged;
|
||||||
// deleting on failure would lose the data.
|
// deleting on failure would lose the data.
|
||||||
if (result == DATA_SEND_SUCCESS) {
|
if (result != DATA_SEND_SUCCESS) {
|
||||||
delete_last_page();
|
|
||||||
} else {
|
|
||||||
LOG(" SPI not sent\n");
|
LOG(" SPI not sent\n");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
delete_last_page();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,6 +335,10 @@ static void init_all_hardware(void)
|
|||||||
init_spi();
|
init_spi();
|
||||||
adc_Initialize();
|
adc_Initialize();
|
||||||
|
|
||||||
|
// Gate the clocks of everything unused; adc_Enable() lifts the ADC's gate
|
||||||
|
// for the duration of each battery reading.
|
||||||
|
shutdown_all_peripherals();
|
||||||
|
|
||||||
set_up_reed_interrupt();
|
set_up_reed_interrupt();
|
||||||
set_up_minute_interrupt();
|
set_up_minute_interrupt();
|
||||||
LOG("Set up AVR interrupts\n");
|
LOG("Set up AVR interrupts\n");
|
||||||
@@ -348,10 +396,13 @@ int main(void)
|
|||||||
// Ask the base station for the current time and load it into the RTC
|
// Ask the base station for the current time and load it into the RTC
|
||||||
time_sync_status = set_time_from_rfm69(IDENTIFIER);
|
time_sync_status = set_time_from_rfm69(IDENTIFIER);
|
||||||
blink_time_sync_result(time_sync_status == RTC_RFM69_SET_TIME_SUCCESS);
|
blink_time_sync_result(time_sync_status == RTC_RFM69_SET_TIME_SUCCESS);
|
||||||
LOG(time_sync_status == RTC_RFM69_SET_TIME_SUCCESS ? "Success in get time \n"
|
if (time_sync_status == RTC_RFM69_SET_TIME_SUCCESS) {
|
||||||
: "Failed to get time \n");
|
LOG("Success in get time \n");
|
||||||
|
} else {
|
||||||
|
LOG("Failed to get time \n");
|
||||||
|
}
|
||||||
|
|
||||||
read_battery_level(); // Throwaway read to settle the ADC path
|
read_battery_millivolts(); // Throwaway read to settle the ADC path
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
sleep_until_interrupt();
|
sleep_until_interrupt();
|
||||||
|
|||||||
+14
-2
@@ -31,9 +31,13 @@ RTC_RFM69_STATUS set_time_from_rfm69(identifier_results id_data)
|
|||||||
TIME.Year = RX_DATA.msg[5];
|
TIME.Year = RX_DATA.msg[5];
|
||||||
TIME.Wday = RX_DATA.msg[6];
|
TIME.Wday = RX_DATA.msg[6];
|
||||||
|
|
||||||
rtc_write_time(TIME);
|
// Only claim success once the time actually landed in the RTC,
|
||||||
|
// so a failed write is retried next cycle.
|
||||||
|
if (rtc_write_time(TIME) == 0) {
|
||||||
return RTC_RFM69_SET_TIME_SUCCESS;
|
return RTC_RFM69_SET_TIME_SUCCESS;
|
||||||
}
|
}
|
||||||
|
LOG("RTC write failed\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return RTC_RFM69_SET_TIME_FAILED;
|
return RTC_RFM69_SET_TIME_FAILED;
|
||||||
}
|
}
|
||||||
@@ -78,8 +82,16 @@ uint8_t rtc_read_time_array(uint8_t* data)
|
|||||||
|
|
||||||
time_struct rtc_read_time(void)
|
time_struct rtc_read_time(void)
|
||||||
{
|
{
|
||||||
|
if (rtc_read_time_array(DATA_BUFFER_7)) {
|
||||||
|
// RTC unreachable: mark every field with an unmistakably invalid value
|
||||||
|
// rather than transmitting whatever was read last. The base station
|
||||||
|
// sees month 0xFF and knows the timestamp is unusable.
|
||||||
|
LOG("RTC read failed\n");
|
||||||
|
TIME.Second = TIME.Minute = TIME.Hour = 0xFF;
|
||||||
|
TIME.Wday = TIME.Day = TIME.Month = TIME.Year = 0xFF;
|
||||||
|
return TIME;
|
||||||
|
}
|
||||||
|
|
||||||
rtc_read_time_array(DATA_BUFFER_7);
|
|
||||||
TIME.Second = BCD2DEC(DATA_BUFFER_7[0]);
|
TIME.Second = BCD2DEC(DATA_BUFFER_7[0]);
|
||||||
TIME.Minute = BCD2DEC(DATA_BUFFER_7[1]);
|
TIME.Minute = BCD2DEC(DATA_BUFFER_7[1]);
|
||||||
TIME.Hour = BCD2DEC((DATA_BUFFER_7[2] & ~(1 << 6)));
|
TIME.Hour = BCD2DEC((DATA_BUFFER_7[2] & ~(1 << 6)));
|
||||||
|
|||||||
+3
-1
@@ -102,7 +102,9 @@ ndef_message readNDEFText(unsigned char* buf, uint8_t buf_len)
|
|||||||
NDEF_MSG.payload[payload_length] = '\0';
|
NDEF_MSG.payload[payload_length] = '\0';
|
||||||
NDEF_MSG.payload_len = payload_length;
|
NDEF_MSG.payload_len = payload_length;
|
||||||
|
|
||||||
LOG(NDEF_MSG.payload);
|
#if DO_UART
|
||||||
|
uart_sendString(NDEF_MSG.payload); // Runtime string, so not LOG()
|
||||||
|
#endif
|
||||||
LOG("\n");
|
LOG("\n");
|
||||||
|
|
||||||
return NDEF_MSG;
|
return NDEF_MSG;
|
||||||
|
|||||||
+12
-2
@@ -1,15 +1,25 @@
|
|||||||
|
// One-shot clock gating for every peripheral this firmware never uses (and
|
||||||
|
// the ADC, which adc_Enable()/adc_Disable() power up only around a reading).
|
||||||
|
// In use and left alone: SPI1 (radio + EEPROM), TWI0 (RTC + NFC tag), and
|
||||||
|
// USART0 when serial logging is compiled in.
|
||||||
|
|
||||||
#include "power_mgmt.h"
|
#include "power_mgmt.h"
|
||||||
|
|
||||||
void shutdown_all_peripherals(void)
|
void shutdown_all_peripherals(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
power_adc_disable();
|
power_adc_disable();
|
||||||
power_timer0_disable();
|
power_timer0_disable();
|
||||||
power_timer1_disable();
|
power_timer1_disable();
|
||||||
power_timer2_disable();
|
power_timer2_disable();
|
||||||
power_timer3_disable();
|
power_timer3_disable();
|
||||||
|
power_usart1_disable();
|
||||||
|
#ifdef power_spi0_disable
|
||||||
|
power_spi0_disable();
|
||||||
|
#endif
|
||||||
|
#ifdef power_twi1_disable
|
||||||
|
power_twi1_disable();
|
||||||
|
#endif
|
||||||
#if !DO_UART
|
#if !DO_UART
|
||||||
power_usart0_disable();
|
power_usart0_disable();
|
||||||
power_usart1_disable();
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -284,7 +284,7 @@ DATA_SEND_STATUS send_message(tx_rx_data_struct tx_data)
|
|||||||
// 60-byte msg layout:
|
// 60-byte msg layout:
|
||||||
// [0..9] name (padded)
|
// [0..9] name (padded)
|
||||||
// [10..19] wheel diameter (padded)
|
// [10..19] wheel diameter (padded)
|
||||||
// [20..21] battery reading, little endian
|
// [20..21] battery voltage in millivolts, little endian (0 = read failed)
|
||||||
// [22..26] timestamp: minute, hour, day, month, year
|
// [22..26] timestamp: minute, hour, day, month, year
|
||||||
// [27..56] 15 x uint16 per-minute counts, little endian
|
// [27..56] 15 x uint16 per-minute counts, little endian
|
||||||
// [57..59] 24-bit hash of bytes 0..56
|
// [57..59] 24-bit hash of bytes 0..56
|
||||||
|
|||||||
@@ -57,6 +57,14 @@ void uart_sendString(const char* str)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void uart_sendString_P(const char* progmem_str)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
while ((c = pgm_read_byte(progmem_str++)) != '\0') {
|
||||||
|
uart_sendChar(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void uart_print_uint16(uint16_t meas, const char* buf)
|
void uart_print_uint16(uint16_t meas, const char* buf)
|
||||||
{
|
{
|
||||||
snprintf(array_internal, sizeof(array_internal), "%u", meas);
|
snprintf(array_internal, sizeof(array_internal), "%u", meas);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
|
|
||||||
#include <avr/io.h>
|
#include <avr/io.h>
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -26,6 +27,7 @@ extern "C" {
|
|||||||
void uart_init(void);
|
void uart_init(void);
|
||||||
void uart_sendChar(char c);
|
void uart_sendChar(char c);
|
||||||
void uart_sendString(const char* str);
|
void uart_sendString(const char* str);
|
||||||
|
void uart_sendString_P(const char* progmem_str); // For strings kept in flash (PSTR)
|
||||||
void uart_sendStringArray(unsigned char str[], uint8_t len);
|
void uart_sendStringArray(unsigned char str[], uint8_t len);
|
||||||
void uart_print_uint16(uint16_t meas, const char* buf);
|
void uart_print_uint16(uint16_t meas, const char* buf);
|
||||||
void uart_print_hex(unsigned char vin, const char* buf);
|
void uart_print_hex(unsigned char vin, const char* buf);
|
||||||
|
|||||||
Reference in New Issue
Block a user