ASR Microelectronics ASR6501 & ASR6502
- Heltec CubeCell Dev-Board (V1 & V2)
- Heltec CubeCell Dev-Board Plus
Pin Definitions
CubeCell Dev-Board
The CubeCell Dev-Board pin functions are presented in the following pinout diagrams.
CubeCell Dev-Board (HTCC-AB01) [ASR6501]
The CubeCell hardware definition provided within the Arduino IDE includes a set of pin identifiers that can be used to address the various pins on the Dev-Board. The following table lists these, together with the relevant elements of a set of 'universal' pin identifiers used for common functions within my application sketches.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
// Predefined pin names
/* ADC GPIO0 GPIO1 PWM1// GPIO2 GPIO2 PWM2// GPIO3 GPIO3 RGB// GPIO4 GPIO4 GPIO5 Vext// GPIO6 GPIO6 VBAT_ADC_CTL// GPIO7 USER_KEY// GPIO7 GPIO7 UART_TX UART_RX SCL SDA MISO MOSI SCK SS */ // I2C // Nothing required here, SDA & SCL already defined // ALF4all #define A4a_A0ADC #define A4a_A1GPIO5 #define A4a_A2GPIO1 #define A4a_A3GPIO0 // Wind/Rain #define rainCollectorPinGPIO5 #define windDirectionPinADC #define windSpeedPinGPIO0 // Sensor Interface #define sensorWakeGPIO2 #define sensorInterruptGPIO3 #define sphygOUTGPIO2 #define sphygSCKGPIO3 |
While a new pinout diagram was issued with the release of the V2 board, the two boards are pin compatible.
CubeCell Dev-Board V2 (HTCC-AB01 V2) [ASR6502]
While the V2 board is pin compatible with the original, its ADC configuration is slightly different. Since the V2 board is based on the ASR6502 processor, which has three ADCs (the ASR6501 only has one), the pin labelled ADC, and identified as such (cf. CubeCell Dev-Board Plus below), is now permanently available for external applications, even though the pinout diagram provided by Heltec (above) might suggest otherwise. The battery reading function now has a dedicated ADC, which is not available for any other use. There does not appear to be any connection, internally or externally, to the third ADC.
There does not appear to have been any update to documentation with the release of the V2.2 board, nor am I aware that there was a board formally identified as V2.1. I ordered two V2 boards when they were first released and they appear as illustrated here, with the same blue-green label as the V1 board and no explicit V2 markings. The board layout is slightly different with the most obvious distinguishing feature being the use of a USB-C connector rather than the micro-USB connector used on the V1 board. The last two boards that I purchased [in 2023] were delivered as V2.2 boards. These boards are visually distinctly different, with a laser-etched label and explicit V2.2 markings.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
// Predefined pin names
/* ADC ADC_VBAT GPIO0 GPIO1 PWM1// GPIO2 GPIO2 PWM2// GPIO3 GPIO3 RGB// GPIO4 GPIO4 GPIO5 Vext// GPIO6 GPIO6 VBAT_ADC_CTL// GPIO7 USER_KEY// GPIO7 GPIO7 UART_TX UART_RX SCL SDA MISO MOSI SCK SS */ // I2C // Nothing required here, SDA & SCL already defined // ALF4all #define A4a_A0ADC #define A4a_A1GPIO5 #define A4a_A2GPIO1 #define A4a_A3GPIO0 // Wind/Rain #define rainCollectorPinGPIO5 #define windDirectionPinADC #define windSpeedPinGPIO0 // Sensor Interface #define sensorWakeGPIO2 #define sensorInterruptGPIO3 #define sphygOUTGPIO2 #define sphygSCKGPIO3 |
CubeCell Dev-Board Plus (HTCC-AB02) [ASR6502]
The CubeCell Dev-Board Plus pin functions are presented in the following pinout diagram.
Like the CubeCell, the CubeCell Plus hardware definition provided within the Arduino IDE also includes a set of pin identifiers that can be used to address the various pins on the Dev-Board. The following table lists these, together with the relevant elements of a set of 'universal' pin identifiers used for common functions within my application sketches. Note that, unlike the CubeCell Dev-Board, the CubeCell Dev-Board Plus breaks out all three ADCs that are available of the ASR6502 processor, although the first (identified as ADC or ADC1) is still generally assigned to the task of monitoring the battery voltage.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
// Predefined pin names
/* ADC// ADC1 ADC1// ~1 ADC2// ~2 ADC3// ~3 MOSI1// GPIO1 GPIO1// 1 MISO1// GPIO2 GPIO2// 2 PWM1// GPIO3 SCK1// GPIO3 GPIO3// 3 PWM2// GPIO4 GPIO4// 4 GPIO5// 5 GPIO6// 6 GPIO7// 7 SCL1// GPIO8 GPIO8// 8 SDA1// GPIO9 GPIO9// 9 GPIO10// 10 [OLED Reset] GPIO11// 11 GPIO12// 12 RGB// GPIO13 GPIO13// 13 GPIO14// 14 GPIO15// Vext Control GPIO16// Vbat_ADC Control UART_TX// UART_TX1 UART_RX// UART_RX1 UART_TX1// Serial Chip UART_RX1// Serial Chip UART_TX2 UART_RX2 Vext// GPIO15 VBAT_ADC_CTL// GPIO16 USER_KEY SCL// SCL [OLED SCL] SDA// SDA [OLED SDA] MISO0// MI [LoRa MISO0] MOSI0// MO [LoRa MOSI0] CLK0// CLK [LoRa CLK] NSS0// NSS [LoRa NSS] */ // I2C // Nothing required here, SDA & SCL already defined // ALF4all #define A4a_A0ADC3 #define A4a_A1GPIO7 #define A4a_A2GPIO9 #define A4a_A3GPIO8 // Wind/Rain #define rainCollectorPinGPIO7 #define windDirectionPinADC3 #define windSpeedPinGPIO8 // GPS #define gpsTxUART_RX2 #define gpsRxUART_TX2 // AJ-SR04M Ultrasonic Distance Sensor #define ultraTrigUART_TX2 #define ultraEchoUART_RX2 // Sensor Interface #define sensorWakeGPIO5 #define sensorInterruptGPIO6 #define sphygOUTGPIO5 #define sphygSCKGPIO6 // Watchdog Timer #define wdtDoneGPIO12 // Application Interrupt #define interruptButtonGPIO11 |
LoRa MAC Address
To derive a four byte MAC address for the ASR605x processors that are used in my network, I have used my local prefix of 0xDC followed by the last three bytes of the processor ChipID that can be retrieved using the example sketch Examples for CubeCell... > Basics > ChipID provided under the Arduino IDE.
It is acknowledged that this will not result in a globally unique MAC Address, but it has, to date, served its purpose in the present environment.
EEPROM Usage
14 Mar 23 Since writing this section, I have developed the EepromHandler library, which includes all of the functions required to access an I2C EEPROM in the present environment. In due course, I will move the following discussion to the section that describes the functions of the EepromHandler.
Up to eight members of the AT24Cxx family of EEPROMs may share a single I2C bus. Each device on the bus must have its hardware address inputs (A0, A1, A2) hard-wired to a unique address. In the present case, there is only one EEPROM and it is configured as the first device, with address zero (A0..A2 tied to GND).
All of my Nodes currently use a Sequence Number to identify the sequence of transmitted packets. This Sequence Number is stored in EEPROM so that it persists through power cycling of a Node. Initially, I was a little concerned that an EEPROM would have a limited life in such an application, given some claims that an EEPROM was only good for something like 100,000 cycles. As it is, the AT24Cxx datasheets indicate that the lifetime is at least 10× this and I do indeed now have Nodes that have been writing the same EERPOM for over 1,000,000 cycles (~2 years @ 1 min intervals) without any problems.
The Weather Station application also uses the EEPROM to store the incremental count recorded by the rain collector.
The process for reading from and writing to an external EEPROM using the CubeCell platform is illustrated in the code segment below. The parameters that need to be passed to the read/write functions are mostly self-explanatory.
The fourth parameter in the RetrieveBytes
function, the boolean, indicates whether or not the character string being retrieved is zero-terminated. If set to true, the read process will stop when a zero byte is encountered. Since we are effectively reading and writing integers, not characters, this parameter should always be set to false.
Note also that the library used in this case should be the version specifically provided for the CubeCell platform (Arduino/hardware/CubeCell/CubeCell/libraries/EEPROM/AT24C32N).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
#include <Wire.h>
#include <AT24C32N.h> EEPROM_AT24C32NbhcpEEPROM; #define I2C_EEPROM_Address0x50 #define sequenceLocation0 #define rainLocation512 const uint16_t sequenceBytes = 2; const uint16_t rainBytes = 2; union EEPROMPayload { char payloadByte[2]; uint16_t counter; }; EEPROMPayload EEPROMWriteData, EEPROMReadData, rainCounter; void setup () { pinMode(Vext,OUTPUT); digitalWrite(Vext,LOW);// Turn on Vext (assuming EEPROM powered through Vext) Serial.begin(115200); delay(1000); Serial.println("[setup] I2C EEPROM Test"); } void loop () { // Read the Sequence Number from EEPROM Wire.begin(); bhcpEEPROM.RetrieveBytes(EEPROMReadData.payloadByte, sequenceBytes, sequenceLocation, false); Wire.end(); Serial.print("[loop] Read Sequence Number "); Serial.println(EEPROMReadData.counter); messageCounter = EEPROMReadData.counter; // Increment the Sequence Number messageCounter++; // Write the Sequence Number to EEPROM EEPROMWriteData.counter = messageCounter; Serial.print("[loop] Write Sequence Number "); Serial.println(EEPROMReadData.counter); Wire.begin(); bhcpEEPROM.WriteBytes(sequenceLocation, sequenceBytes, EEPROMWriteData.payloadByte); Wire.end(); delay(5000); } |
I have only ever used 32K EEPROMs with my CubeCell applications and, as such, I have not encountered any of the problems that might arise using the current library, the name of which—AT24C32N—implies that it is designed for use with 32K, and presumably 64K, EEPROMs.
The reader is referred to the discussion on EEPROM addressing under the description of the EepromHandler library for more information on this subject.
Low Power Mode
Low Power, Wake Up by Timer
I use a modified version of the procedure provided by Heltec in their LowPower_WakeUpByTimer
example sketch. I do not use their timer to trigger the lowPowerHandler()
, rather I just use the setting of the LOWPOWER
state when activities are complete. The only thing to be a little careful of here is to ensure that any asynchronous activity is actually complete when the code loops into the LOWPOWER state.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
#define sleepTime 60000
static TimerEvent_t wakeUp; typedef enum { LOWPOWER, ACTIVE } ProcessorStates; ProcessorStates state; void onWake() { Serial.println(); Serial.printf("[onWake] Wake up..."); state = ACTIVE; } void setup() { Serial.begin( 115200 ); pinMode( Vext, OUTPUT ); digitalWrite( Vext, LOW )// Turn on the power supply to external devices TimerInit( &wakeUp, onWakeUp );// Initialise the wake timer onWake();// Start off awake } void loop() { switch( state ) { case ACTIVE: { // Do whatever you want done when the processor is active here state = LOWPOWER; break; } case LOWPOWER: { digitalWrite( Vext, HIGH );// Turn the external power off before sleeping TimerSetValue( &wakeUp, sleepTime ); TimerStart( &wakeUp ); lowPowerHandler(); break; } default: break; } } |
Further details pending
Low Power, Wake Up by GPIO
I have not used this method to date and so have nothing to add to the example sketch provided by Heltec.
Sensor Power Management
If sensors or other devices have been configured to take power from the Vext pin on the CubeCell [Plus] module, sensor/device power-down can be managed automatically through the onSleep()
method that is called when the processor enters sleep mode.
1 2 3 4 5 6 7 8 |
void onSleep()
{ Serial.printf("[onSleep] Go into LowPower mode for %d ms\r\n",sleepTime); state = LOWPOWER; digitalWrite( Vext, HIGH );// Turn the external power off before sleeping TimerSetValue( &wakeUp, sleepTime ); TimerStart( &wakeUp ); } |
Note that Vext must then be explicitly turned on, and relevant components initialised, before attempting to read any sensor so powered.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
digitalWrite( Vext, LOW );// Make sure the external power supply is on
delay(10);// Give everything a moment to settle down Wire.begin();// On with the show... if (bme280.init()) { Serial.println("[readBMESensor] BME sensor initialisation complete"); } else { Serial.println("[readBMESensor] BME sensor initialisation error"); } delay(100);// The BME280 needs a moment to get itself together... (50ms is too little time) temperature = (int) (bme280.getTemperature()); pressure = (int) (bme280.getPressure() / 91.79F); humidity = (int) (bme280.getHumidity()); Wire.end();// All done |
DS18B20 Digital Temperature Sensor
When using the CubeCell platform, it is important to ensure that the relevant software libraries actually support the underlying architecture. Incompatibilities can manifest themselves in a variety of ways, from a simple failure to compile, which is generally fine in the present context because the incompatibility is immediately obvious, to unpredictable behaviour during operation, which can be much more difficult to troubleshoot.
If the Arduino IDE has been set up to just handle the CubeCell platform, the potential for surprises should be minimal— only CubeCell compatible libraries should have been loaded. More care needs to be taken if libraries have been loaded to support other platforms.
In the case of the OneWire
library, required to support the DallasTemperature
library and the DS18B20 digital temperature sensor, care must be taken to ensure that the Heltec version, located [on my macOS machine] at
Arduino\hardware\CubeCell\CubeCell\OneWire
is used, rather than the version that is usually located under
Arduino\libraries
Alternatively, the library can be modified as described elsewhere on this site.
Sketches will compile and run with the standard library, but will constantly report:
Temperature for the device 1 (index 0) is: -127.00
See here for a more detailed discussion of how this problem manifests itself.
Processor Interrupts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
#include <Wire.h>// I2C bus
#include <AT24C32N.h>// EEPROM management // Define the interrupt pin(s), just one for the purpose of this example #define rainCollectorPinGPIO7 // and the EEPROM parameters (these are not required for interrupt, they are just part of my application) EEPROM_AT24C32NbhcpEEPROM; #define I2C_EEPROM_Address0x50 #define rainLocation4 #define rainBytes2 union EEPROMPayload { char payloadByte[2]; uint16_t counter; }; EEPROMPayload rainCounter; void setup () { // Set up the interrupt PINMODE_INPUT_PULLUP(rainCollectorPin); attachInterrupt(rainCollectorPin,incrementRainCount,FALLING); } void loop () { // Do stuff here while you're waiting for an interrupt // Most of the time, we'll just go to sleep } void incrementRainCount() { // Do here whatever you want to do when an interrupt occurs // In the present case, we just increment the rain collector counter, which is stored in EEPROM digitalWrite( Vext, LOW );// Make sure the external power supply is on delay(10);// Give everything a moment to settle down Wire.begin();// On with the show... bhcpEEPROM.RetrieveBytes(rainCounter.payloadByte, rainBytes, rainLocation, false); rainCounter.counter++;// This is the essence of what we're doing in processing this interrupt bhcpEEPROM.WriteBytes(rainLocation, rainBytes, rainCounter.payloadByte); Wire.end();// All done } |
Note that it is generally considered good practice to spend as little time as possible in an Interrupt Service Routine (ISR). Ideally, that would just be something like incrementing a counter or setting a boolean flag.
Because our nodes will generally be operating in some low power mode, and the counter often needs to survive power cycles, there is sometimes a little more work to do than might be considered ideal—we will often need to retrieve the counter from EEPROM, and store the updated value, as illustrated in the above example. The CubeCell platform seems to be able to handle this without any trouble, even though this really is much more than should be done in an ISR. Other processors, like the ESP32, don't always seem to be as flexible in this regard. Nonetheless, I will come back to this and remove everything other than the counter increment in due course, I just have to properly think through what's happening with the LowPowerHandler and whether or not this will have any impact on EEPROM activity.
Note the comments below in relation to the use of a watchdog timer. The fact that the current Weather Station implementation uses an ISR to both retrieve data from and store data in an external EEPROM, while not recommended from the point of view of an ISR, may be the reason why the occasional hardware/software fault does not appear to impact the ongoing collection of rainfall data. It may be that these faults do not impact the ongoing operation of the ISR.
Further details pending
LoRa Configuration
I have very little to add here, other than to note the paramater values that are compatible with my present environment. The following code segments are essentially just lifted from the CubeCell example sketches provided in the Arduino IDE.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#define Frequency917E6// Hz
#define SignalBandwidth125E3// Hz #define SignalBandwidthIndex0// 0: 125 kHz, // 1: 250 kHz, // 2: 500 kHz, // 3: Reserved #define SpreadingFactor7// SF7..SF12 #define CodingRate1// 1: 4/5, // 2: 4/6, // 3: 4/7, // 4: 4/8 #define CodingRateDenominator5 #define PreambleLength8// Same for Tx and Rx #define SyncWord0x12 #define OutputPower14// dBm #define SymbolTimeout0// Symbols #define FixedLengthPayloadfalse #define IQInversionfalse |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#include <LoRa_APP.h>
static RadioEvents_t RadioEvents; void onTxDone(void) { Serial.println("[onTxDone] TX done!"); // Add whatever code needs to be executed when transmission is complete } void onTxTimeout(void) { Serial.println("[onTxTimeout] TX Timeout..."); // Add whatever code needs to be executed on transmission timeout } void setup() { // Initialise the radio RadioEvents.TxDone = onTxDone; RadioEvents.TxTimeout = onTxTimeout; Radio.Init( &RadioEvents ); Radio.SetChannel( Frequency ); Radio.SetTxConfig( MODEM_LORA, OutputPower, 0, SignalBandwidthIndex, SpreadingFactor, CodingRate, PreambleLength, FixedLengthPayload, true, 0, 0, IQInversion, 3000 ); Serial.println("[setup] LoRa initialisation complete"); } |
Analog-to-Digital Converter (ADC) Usage
The the CubeCell Dev-Boards include a 12-bit ADC [0..4095], but it is limited to an input voltage of 2.4V. If higher voltages need to be measured, an appropriate voltage divider is required on the input.
Further details pending
Asynchronous Timers
Timers are supported on the ASR650x hardware through native Real Time Clock (RTC) functions. The essential details of these functions are outlined below.
static TimerEvent_t timerName;// Define the timer
TimerInit( &timerName, timerFunction );// Initialise the timer void timerFunction() { // Function executed when timer expires } int timerInterval; TimerSetValue( &timerName, timerInterval );// Set the timer duration TimerStart( &timerName );// Start the timer TimerReset( &timerName );// Restart the timer TimerStop( &timerName );// Stop the timer |
Further details pending
Watchdog Timer
The Problem
Twice now, my Weather Station CubeCell Plus board has effectively hung, failing to send out updates and, while this was over a period of twelve months, any failure is undesirable, if not unacceptable. At this point, I've no real idea where exactly the fault occurs, whether it was the MCU itself, or maybe just the SX1262 IC, but the latter might be more likely since some functions, such as the rain counter interrupt and EEPROM data storage, did appear to continue to operate.
In the event, the system was returned to normal operation by simply pressing the onboard reset button.
The Solution
This was a pretty obvious application for a watchdog timer (WDT). A quick search suggested that, while ASR605x processors do include an internal watchdog timer, an independent, external WDT would probably more effectively serve our range of applications and this then led to a search for an appropriate hardware configuration.
The Options
Plenty of options pop up in response to any search for a WDT, from the humble NE555 to dedicated ICs like the MAX6369, TPL5010, TPS3431 or TPS3813. Two issues, however, quickly became apparent. The first was that many of these ICs, even the NE555, require an operating voltage of 5V or more, which was OK for regular Arduino or Raspberry Pi processors, but not so for the 3.3V MCUs that we are generally using. The second was that dedicated ICs like the MAX6369, TPS3431 or TPS3813 draw a current of around 8µA, which is significantly more than our processor sleep current of around 2µA. There are CMOS versions of the N555, the TS555 or TLC555, that can run at 3.3V, but they draw currents in excess of 100µA.
Ultimately, I chose to go with the TPL5010. It was not the most economical option—depending on the source, it costs ~A$1.40 while the NE555 is just ~A$0.06—but it can operate within the range of our Li-Ion battery supply voltage and its current draw is a mere ~35nA. The circuit I have used is taken pretty much straight from the TPL5010 datasheet, although I have not included a manual reset button, as there is already one on the Dev-Boards we are using, and I have included an optional trimpot to provide an adjustable time period, should that prove useful at any point.
The WDT requires at least one dedicated MCU pin for control, so there are constraints when using it with the CubeCell Dev-Board. There are, however, currently no such constraints with the CubeCell Dev-Board Plus—configuration details provided on the 10068-BHCP board assembly page.